Permettre upload par CSV #36

Merged
vlbeaudoin merged 1 commit from feature/csv-upload into main 2023-09-05 18:05:27 -04:00
5 changed files with 52 additions and 15 deletions

View file

@ -87,7 +87,7 @@ func (d *DataClient) InsertMembres(membres []models.Membre) (int64, error) {
tx.Rollback() tx.Rollback()
return 0, errors.New("Cannot insert membre with no membre_id") return 0, errors.New("Cannot insert membre with no membre_id")
} }
result, err := tx.NamedExec("INSERT INTO membres (id, last_name, first_name, prefered_name, programme_id) VALUES (:id, :last_name, :first_name, :prefered_name, :programme_id);", &membre) result, err := tx.NamedExec("INSERT INTO membres (id, last_name, first_name, prefered_name, programme_id) VALUES (:id, :last_name, :first_name, :prefered_name, :programme_id) ON CONFLICT (id) DO NOTHING;", &membre)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return 0, err return 0, err
@ -124,7 +124,7 @@ func (d *DataClient) InsertProgrammes(programmes []models.Programme) (int64, err
return 0, errors.New("Cannot insert programme with no programme_id") return 0, errors.New("Cannot insert programme with no programme_id")
} }
result, err := tx.NamedExec("INSERT INTO programmes (id, titre) VALUES (:id, :titre);", &programme) result, err := tx.NamedExec("INSERT INTO programmes (id, titre) VALUES (:id, :titre) ON CONFLICT DO NOTHING;", &programme)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return 0, err return 0, err

1
go.mod
View file

@ -3,6 +3,7 @@ module git.agecem.com/agecem/bottin/v5
go 1.20 go 1.20
require ( require (
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d
github.com/jackc/pgx v3.6.2+incompatible github.com/jackc/pgx v3.6.2+incompatible
github.com/jmoiron/sqlx v1.3.5 github.com/jmoiron/sqlx v1.3.5
github.com/labstack/echo/v4 v4.10.2 github.com/labstack/echo/v4 v4.10.2

2
go.sum
View file

@ -66,6 +66,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d h1:KbPOUXFUDJxwZ04vbmDOc3yuruGvVO+LOa7cVER3yWw=
github.com/gocarina/gocsv v0.0.0-20230616125104-99d496ca653d/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=

View file

@ -1,12 +1,16 @@
package handlers package handlers
import ( import (
"encoding/csv"
"io"
"net/http" "net/http"
"git.agecem.com/agecem/bottin/v5/data" "git.agecem.com/agecem/bottin/v5/data"
"git.agecem.com/agecem/bottin/v5/models" "git.agecem.com/agecem/bottin/v5/models"
"git.agecem.com/agecem/bottin/v5/responses" "git.agecem.com/agecem/bottin/v5/responses"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
"github.com/gocarina/gocsv"
) )
func PostMembres(c echo.Context) error { func PostMembres(c echo.Context) error {
@ -32,9 +36,21 @@ func PostMembres(c echo.Context) error {
return c.JSON(response.StatusCode, response) return c.JSON(response.StatusCode, response)
} }
case "text/csv": case "text/csv":
response.StatusCode = http.StatusNotImplemented body := c.Request().Body
response.Message = "Not Implemented" if body == nil {
response.StatusCode = http.StatusBadRequest
response.Message = "Request body is empty"
return c.JSON(response.StatusCode, response) return c.JSON(response.StatusCode, response)
}
defer body.Close()
// Parse the CSV data from the request body using gocsv.
if err := gocsv.Unmarshal(body, &membres); err != nil {
response.StatusCode = http.StatusBadRequest
response.Message = "Could not unmarshal into membres"
response.Error = err.Error()
return c.JSON(response.StatusCode, response)
}
default: default:
response.StatusCode = http.StatusBadRequest response.StatusCode = http.StatusBadRequest
response.Message = "Invalid Content-Type" response.Message = "Invalid Content-Type"
@ -85,9 +101,27 @@ func PostProgrammes(c echo.Context) error {
return c.JSON(response.StatusCode, response) return c.JSON(response.StatusCode, response)
} }
case "text/csv": case "text/csv":
response.StatusCode = http.StatusNotImplemented body := c.Request().Body
response.Message = "Not Implemented" if body == nil {
response.StatusCode = http.StatusBadRequest
response.Message = "Request body is empty"
return c.JSON(response.StatusCode, response) return c.JSON(response.StatusCode, response)
}
defer body.Close()
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
r := csv.NewReader(in)
r.Comma = ';'
return r // Allows use ; as delimiter
})
// Parse the CSV data from the request body using gocsv.
if err := gocsv.Unmarshal(body, &programmes); err != nil {
response.StatusCode = http.StatusBadRequest
response.Message = "Could not unmarshal into programmes"
response.Error = err.Error()
return c.JSON(response.StatusCode, response)
}
default: default:
response.StatusCode = http.StatusBadRequest response.StatusCode = http.StatusBadRequest
response.Message = "Invalid Content-Type" response.Message = "Invalid Content-Type"

View file

@ -16,16 +16,16 @@ CREATE TABLE IF NOT EXISTS membres (
` `
type Programme struct { type Programme struct {
ID string `db:"id" json:"programme_id"` ID string `db:"id" json:"programme_id" csv:"programme_id"`
Titre string `db:"titre" json:"nom_programme"` Titre string `db:"titre" json:"nom_programme" csv:"nom_programme"`
} }
type Membre struct { type Membre struct {
ID string `db:"id" json:"membre_id"` ID string `db:"id" json:"membre_id" csv:"membre_id"`
LastName string `db:"last_name" json:"last_name"` LastName string `db:"last_name" json:"last_name" csv:"last_name"`
FirstName string `db:"first_name" json:"first_name"` FirstName string `db:"first_name" json:"first_name" csv:"first_name"`
PreferedName string `db:"prefered_name" json:"prefered_name"` PreferedName string `db:"prefered_name" json:"prefered_name" csv:"prefered_name"`
ProgrammeID string `db:"programme_id" json:"programme_id"` ProgrammeID string `db:"programme_id" json:"programme_id" csv:"programme_id"`
} }
type Entry interface { type Entry interface {