diff --git a/tools/altares/concat_2.sh b/tools/altares/concat_2.sh new file mode 100755 index 000000000..b4463eb7f --- /dev/null +++ b/tools/altares/concat_2.sh @@ -0,0 +1,13 @@ +echo "stock" +go run main.go resources/SF_DATA_20230706.txt resources/S_202011095834-3_202311010315.csv resources/S_202011095834-3_202310020319.csv output_2.csv +#echo "increment 1" +#go run convert_increment.go resources/S_202011095834-3_202311010315.csv >> output.csv +#echo "increment 2" +#go run convert_increment.go resources/S_202011095834-3_202310020319.csv >> output.csv +echo "résultat" +head output_2.csv +echo "." +echo "." +echo "." +tail output_2.csv +#tar -czf altares_2.tar.gz output_2.csv diff --git a/tools/altares/convert_increment.go b/tools/altares/convert_increment.go deleted file mode 100644 index 47fc0b1b8..000000000 --- a/tools/altares/convert_increment.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "encoding/csv" - "io" - "log/slog" - "os" - - "github.com/pkg/errors" - "golang.org/x/text/encoding/charmap" -) - -var FIELDS = []int{ - 1, - 18, - 21, - 21, - 22, - 23, - 24, - 26, - 27, - 30, -} - -func main() { - inputFile, err := os.Open(os.Args[1]) - if err != nil { - panic(err) - } - defer func() { - closeErr := inputFile.Close() - if closeErr != nil { - panic(errors.Wrap(closeErr, "erreur à la fermeture du fichier")) - } - }() - - fromISO8859_15toUTF8 := charmap.ISO8859_15.NewDecoder() - convertReader := fromISO8859_15toUTF8.Reader(inputFile) - reader := csv.NewReader(convertReader) - reader.Comma = ';' - - w := csv.NewWriter(os.Stdout) - defer w.Flush() - - // discard headers - headers, err := reader.Read() - if err != nil { - panic(err) - } - slog.Debug("description des headers", slog.Any("headers", headers)) - - for { - record, err := reader.Read() - if err, ok := err.(*csv.ParseError); ok && err.Err != csv.ErrFieldCount { - continue - } - if err == io.EOF { - return - } - out := selectFields(record) - - if out != nil { - err = w.Write(out) - if err != nil { - panic(err) - } - } - } -} - -func selectFields(record []string) []string { - var data []string - for _, field := range FIELDS { - if field > len(record)-1 { - slog.Error( - "erreur de longueur de ligne", - slog.Any("record", record), - ) - return nil - } - data = append(data, record[field]) - } - return data -} - -func init() { - loglevel := new(slog.LevelVar) - loglevel.Set(slog.LevelDebug) - handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - Level: loglevel, - }) - - logger := slog.New( - handler) - slog.SetDefault(logger) -} diff --git a/tools/altares/main.go b/tools/altares/main.go new file mode 100644 index 000000000..7065c427b --- /dev/null +++ b/tools/altares/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "io" + "log/slog" + "os" + + "opensignauxfaibles/tools/altares/pkg/altares" + "opensignauxfaibles/tools/altares/pkg/utils" +) + +var loglevel *slog.LevelVar + +func main() { + inputs, o := readArgs() + output, err := os.Create(o) + utils.ManageError(err, "erreur à la création du fichier de sortie") + slog.Debug("fichier de sortie créé", slog.String("filename", output.Name())) + defer utils.CloseIt(output, "fermeture du fichier de sortie : "+os.Args[1]) + convertAndConcat(inputs, output) +} + +func convertAndConcat(altaresFiles []string, outputCsv io.Writer) { + slog.Debug("démarrage de la conversion et de la concaténation", slog.Any("inputs", altaresFiles)) + altares.ConvertStock(altaresFiles[0], outputCsv) + if len(altaresFiles) == 1 { + slog.Info("terminé, pas de fichier incrément") + } + for _, filename := range altaresFiles[1:] { + altares.ConvertIncrement(filename, outputCsv) + } +} + +func readArgs() (inputs []string, output string) { + slog.Debug("lecture des arguments", slog.String("status", "start"), slog.Any("all", os.Args)) + if len(os.Args) <= 2 { + slog.Warn("rien à faire, car pas de fichiers altares ou pas de fichier source") + os.Exit(0) + } + output = os.Args[len(os.Args)-1] + inputs = os.Args[1 : len(os.Args)-1] + if len(inputs) == 0 { + slog.Warn("rien à faire, car pas de fichiers altares") + os.Exit(0) + } + slog.Debug("lecture des arguments", slog.String("status", "end"), slog.String("output", output), slog.Any("inputs", inputs)) + return inputs, output +} + +func init() { + loglevel = new(slog.LevelVar) + loglevel.Set(slog.LevelDebug) + handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ + Level: loglevel, + }) + + logger := slog.New( + handler) + slog.SetDefault(logger) +} diff --git a/tools/altares/main_test.go b/tools/altares/main_test.go new file mode 100644 index 000000000..f81352489 --- /dev/null +++ b/tools/altares/main_test.go @@ -0,0 +1,48 @@ +package main + +import ( + "os" + "reflect" + "runtime" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_convertAndConcat(t *testing.T) { + output, err := os.CreateTemp(t.TempDir(), "output_*.csv") + require.NoError(t, err) + convertAndConcat( + []string{"resources/SF_DATA_20230706.txt", "resources/S_202011095834-3_202310020319.csv", "resources/S_202011095834-3_202311010315.csv"}, + output, + ) +} + +func Test_readArgs(t *testing.T) { + _, here, _, _ := runtime.Caller(0) + tests := []struct { + name string + args []string + wantInputs []string + wantOutput string + }{ + { + name: "tout va bien", + args: []string{here, "stock", "increment1", "increment2", "output"}, + wantInputs: []string{"stock", "increment1", "increment2"}, + wantOutput: "output", + }, + } + for _, tt := range tests { + os.Args = tt.args + t.Run(tt.name, func(t *testing.T) { + gotInputs, gotOutput := readArgs() + if !reflect.DeepEqual(gotInputs, tt.wantInputs) { + t.Errorf("readArgs() gotInputs = %v, want %v", gotInputs, tt.wantInputs) + } + if gotOutput != tt.wantOutput { + t.Errorf("readArgs() gotOutput = %v, want %v", gotOutput, tt.wantOutput) + } + }) + } +} diff --git a/tools/altares/pkg/altares/convertIncrement.go b/tools/altares/pkg/altares/convertIncrement.go new file mode 100644 index 000000000..947836c07 --- /dev/null +++ b/tools/altares/pkg/altares/convertIncrement.go @@ -0,0 +1,110 @@ +package altares + +import ( + "encoding/csv" + "fmt" + "io" + "log/slog" + "os" + "regexp" + + "github.com/pkg/errors" + "golang.org/x/text/encoding/charmap" + + "opensignauxfaibles/tools/altares/pkg/utils" +) + +var FIELDS = []int{ + 1, + 18, + 21, + 21, + 22, + 23, + 24, + 26, + 27, + 30, +} + +var END_OF_FILE_REGEXP = regexp.MustCompile("Fin du fichier : total (?P\\d+) ligne\\(s\\)") + +func ConvertIncrement(incrementFilename string, output io.Writer) { + slog.Info("démarrage de la conversion du fichier incrémental", slog.String("filename", incrementFilename)) + inputFile, err := os.Open(incrementFilename) + if err != nil { + panic(err) + } + defer func() { + closeErr := inputFile.Close() + if closeErr != nil { + panic(errors.Wrap(closeErr, "erreur à la fermeture du fichier")) + } + }() + + fromISO8859_15toUTF8 := charmap.ISO8859_15.NewDecoder() + convertReader := fromISO8859_15toUTF8.Reader(inputFile) + reader := csv.NewReader(convertReader) + reader.Comma = ';' + + w := csv.NewWriter(output) + defer w.Flush() + + // discard headers + headers, err := reader.Read() + utils.ManageError(err, "erreur lors de la lecture des headers") + slog.Debug("description des headers", slog.Any("headers", headers)) + + for { + record, err := reader.Read() + if err, ok := err.(*csv.ParseError); ok && err.Err != csv.ErrFieldCount { + slog.Warn("prbleme", slog.Any("error", err)) + continue + } + //if err == io.EOF { + // return + //} + if isIncrementalEndOfFile(record) { + return + } + out := selectFields(record) + + if out != nil { + err = w.Write(out) + if err != nil { + panic(err) + } + } + } +} + +func selectFields(record []string) []string { + var data []string + for _, field := range FIELDS { + //if field > len(record)-1 { + // if + // slog.Error( + // "erreur de longueur de ligne", + // slog.Any("record", record), + // ) + // utils.ManageError(fmt.Errorf("erreur de longueur de ligne, attendu : %d, obtenu %d", field, len(record)-1), "mais que se passe t il ?")) + // return nil + //} + data = append(data, record[field]) + } + return data +} + +func isIncrementalEndOfFile(record []string) bool { + if len(record) > 2 { + return false + } + //names := END_OF_FILE_REGEXP.SubexpNames() + //slog.Info("les captures", slog.Any("names", names)) + line := END_OF_FILE_REGEXP.FindStringSubmatch(record[0]) + if len(line) != 2 { + utils.ManageError(fmt.Errorf("erreur de fin de fichier : %+v", record), "problème avec la fin de fichier") + } + slog.Info("fin du fichier incrémental", slog.Any("expectedLines", line[1])) + return true +} diff --git a/tools/altares/pkg/altares/convertIncrement_test.go b/tools/altares/pkg/altares/convertIncrement_test.go new file mode 100644 index 000000000..110458092 --- /dev/null +++ b/tools/altares/pkg/altares/convertIncrement_test.go @@ -0,0 +1,32 @@ +package altares + +import ( + "encoding/csv" + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_isIncrementalEndOfFile(t *testing.T) { + recordEOF, err := csv.NewReader(strings.NewReader("Fin du fichier : total 122909 ligne(s);")).Read() + t.Log("match -> ", END_OF_FILE_REGEXP.MatchString(recordEOF[0])) + require.NoError(t, err) + type args struct { + record []string + } + tests := []struct { + name string + args args + want bool + }{ + {"ok", args{recordEOF}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isIncrementalEndOfFile(tt.args.record); got != tt.want { + t.Errorf("isIncrementalEndOfFile() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/tools/altares/convert_stock.go b/tools/altares/pkg/altares/convertStock.go similarity index 71% rename from tools/altares/convert_stock.go rename to tools/altares/pkg/altares/convertStock.go index 11330ea9b..fa42b050c 100644 --- a/tools/altares/convert_stock.go +++ b/tools/altares/pkg/altares/convertStock.go @@ -1,4 +1,4 @@ -package main +package altares import ( "encoding/csv" @@ -9,9 +9,9 @@ import ( "github.com/iancoleman/strcase" "github.com/pkg/errors" -) -var loglevel *slog.LevelVar + "opensignauxfaibles/tools/altares/pkg/utils" +) const HEADER_TO_REMOVE = "NBR_EXPERIENCES_PAIEMENT" @@ -19,9 +19,9 @@ var FORMATTERS = map[int]func(string) string{ 9: datifyStock, } -func main() { - - inputFile, err := os.Open(os.Args[1]) +func ConvertStock(stockFilename string, output io.Writer) { + slog.Info("conversion du fichier stock", slog.String("filename", stockFilename)) + inputFile, err := os.Open(stockFilename) if err != nil { panic(err) } @@ -34,29 +34,32 @@ func main() { reader := csv.NewReader(inputFile) reader.Comma = ';' - w := csv.NewWriter(os.Stdout) + w := csv.NewWriter(output) defer w.Flush() - slog.Info("manipule les headers") + slog.Debug("manipule les headers") columnToRemove := manageHeaders(reader, w) - slog.Info("suppression d'une colonne", slog.String("status", "start"), slog.Int("index", columnToRemove)) + slog.Debug("suppression d'une colonne", slog.String("status", "start"), slog.Int("index", columnToRemove)) for { record, err := reader.Read() if err, ok := err.(*csv.ParseError); ok && err.Err != csv.ErrFieldCount { slog.Warn("erreur lors de la lecture", slog.Any("error", err)) - continue + utils.ManageError(err, "erreur pendant la suppression de colonne") } if err == io.EOF { - slog.Info("suppression d'une colonne", slog.String("status", "end"), slog.Int("index", columnToRemove)) + slog.Info( + "suppression d'une colonne", + slog.String("status", "end"), + slog.Int("index", columnToRemove), + slog.Any("row", reader.InputOffset()), + ) return } newRecord := removeColumn(record, columnToRemove) formatValues(newRecord) err = w.Write(newRecord) - if err != nil { - panic(err) - } + utils.ManageError(err, "erreur pendant l'écriture du fichier de sortie") } } @@ -76,9 +79,7 @@ func datifyStock(s string) string { func manageHeaders(reader *csv.Reader, w *csv.Writer) int { columnToRemove := -1 headers, err := reader.Read() - if err != nil { - panic(err) - } + utils.ManageError(err, "erreur à la lecture des headers du fichier stock") slog.Debug("description des headers", slog.Any("headers", headers)) for idx, header := range headers { if header == HEADER_TO_REMOVE { @@ -111,15 +112,3 @@ func formatHeader(input string) string { r = strings.Replace(r, "etudies", "étudiés", 1) return r } - -func init() { - loglevel = new(slog.LevelVar) - loglevel.Set(slog.LevelDebug) - handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ - Level: loglevel, - }) - - logger := slog.New( - handler) - slog.SetDefault(logger) -} diff --git a/tools/altares/pkg/crypto/crypto_test.go b/tools/altares/pkg/crypto/crypto_test.go index 0476cd361..70e12acc7 100644 --- a/tools/altares/pkg/crypto/crypto_test.go +++ b/tools/altares/pkg/crypto/crypto_test.go @@ -52,7 +52,7 @@ func Test_ExampleEncrypt(t *testing.T) { var wg sync.WaitGroup go func() { - defer cclose(writer, "fermeture du writer encrypté ") + defer utils.CloseIt(writer, "fermeture du writer encrypté ") slog.Info("encrypte", slog.String("status", "start")) wg.Add(1) _, err := sio.Encrypt(gzw, stock, sio.Config{Key: key[:]}) @@ -71,7 +71,7 @@ func Test_ExampleEncrypt(t *testing.T) { slog.Any("wrote", size), slog.Time("lastModified", lastModified), ) - cclose(reader, "fermeture du pipe reader") + utils.CloseIt(reader, "fermeture du pipe reader") wg.Wait() files := mc.ListAltaresFiles() @@ -83,7 +83,7 @@ func Test_ExampleEncrypt(t *testing.T) { localTarget, err := os.Create(filepath.Join(os.TempDir(), remoteFileName)) require.NoError(t, err) slog.Info("copie locale créée", slog.String("path", localTarget.Name())) - defer cclose(localTarget, "fermeture de la copie locale") + defer utils.CloseIt(localTarget, "fermeture de la copie locale") slog.Info("récupère le fichier sur OOS", slog.String("status", "start")) remote := mc.GetAltaresFile(remoteFileName) @@ -105,7 +105,7 @@ func Test_ExampleEncrypt(t *testing.T) { utils.ManageError(err, "erreur de déchiffrage des données") // add error handling } slog.Debug("fichier décrypté", slog.Any("decrypted", decrypted)) - cclose(remote, "fermeture du fichier remote") + utils.CloseIt(remote, "fermeture du fichier remote") require.NoError(t, err) } @@ -130,12 +130,6 @@ func buildKey() [32]byte { return key } -func cclose(o io.Closer, s string) { - err := o.Close() - slog.Debug(s) - utils.ManageError(err, "erreur "+s) -} - func ExampleDecrypt() { // the master key used to derive encryption keys masterkey, err := hex.DecodeString("000102030405060708090A0B0C0D0E0FF0E0D0C0B0A090807060504030201000") // use your own key here diff --git a/tools/altares/pkg/minoos/minio_test.go b/tools/altares/pkg/minoos/minio_test.go index d15aef30c..67c9432ac 100644 --- a/tools/altares/pkg/minoos/minio_test.go +++ b/tools/altares/pkg/minoos/minio_test.go @@ -34,10 +34,3 @@ func Test_something(t *testing.T) { assert.Len(t, records, expectedLines+1) // +1 pour la ligne des headers assert.Len(t, records[0], 11) } - -func Test_newLocalS3(t *testing.T) { - // Initialize minio client object. - minioClient := test.NewS3ForTest(t) - mc := New(minioClient, test.FakeBucketName()) - assert.NotNil(t, mc) -} diff --git a/tools/altares/pkg/utils/misc.go b/tools/altares/pkg/utils/misc.go new file mode 100644 index 000000000..fd6b239ca --- /dev/null +++ b/tools/altares/pkg/utils/misc.go @@ -0,0 +1,12 @@ +package utils + +import ( + "io" + "log/slog" +) + +func CloseIt(o io.Closer, s string) { + err := o.Close() + slog.Debug(s) + ManageError(err, "erreur "+s) +} diff --git a/tools/altares/test/docker.go b/tools/altares/test/docker.go index 56ab11013..36c78cdb6 100644 --- a/tools/altares/test/docker.go +++ b/tools/altares/test/docker.go @@ -35,6 +35,14 @@ func NewS3ForTest(t *testing.T) *minio.Client { return client } +func getMountedDirectory(s3 *dockertest.Resource) string { + mounts := s3.Container.Mounts + if len(mounts) != 1 { + panic(fmt.Sprintf("pas assez ou trop de répertoire local sur ce S3 : %d", len(mounts))) + } + return mounts[0].Source +} + func startMinio(t *testing.T) *dockertest.Resource { s3ContainerName := s3ContainerName + Fake.Lorem().Word() + "_" + Fake.Lorem().Word() dir := t.TempDir()