From 2ffd4ad2306e9ab729499ac01757c09bf98882f5 Mon Sep 17 00:00:00 2001
From: QDIBYS <alexis.szmundy@insee.fr>
Date: Thu, 18 Apr 2024 10:56:58 +0200
Subject: [PATCH 1/2] Add date time output folder

---
 CHANGELOG.md                                  |  4 +-
 .../kraftwerk/api/process/MainProcessing.java |  5 +-
 .../api/process/MainProcessingGenesis.java    |  5 +-
 .../api/services/StepByStepService.java       |  4 +-
 .../fr/insee/kraftwerk/core/Constants.java    |  1 +
 .../core/outputs/csv/CsvOutputFiles.java      |  2 +-
 .../core/sequence/WriterSequence.java         | 10 ++--
 .../functional_tests/LogFileDefinitions.java  |  6 ++-
 .../functional_tests/MainDefinitions.java     | 18 +++++--
 .../ReportingDataDefinitions.java             | 48 +++++++++++++++----
 10 files changed, 81 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1bba9c29..8169c51e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,10 @@
 # Changelog
 
 
-## X.X.X [Not released yet] 
+## X.X.X [Not released yet]
 
+### Changed
+- Output files are stored in separate execution time folders
 
 
 
diff --git a/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessing.java b/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessing.java
index 517f1352..ed537ce3 100644
--- a/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessing.java
+++ b/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessing.java
@@ -17,6 +17,7 @@
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -43,6 +44,7 @@ public class MainProcessing {
 	private VtlBindings vtlBindings = new VtlBindings();
 	private KraftwerkExecutionLog kraftwerkExecutionLog;
 	private List<KraftwerkError> errors = new ArrayList<>();
+	private LocalDateTime executionDateTime;
 	
 	/**
 	 * Map by mode
@@ -96,6 +98,7 @@ public void runMain() throws KraftwerkException {
 	/* Step 1 : Init */
 	public void init() throws KraftwerkException {
 		kraftwerkExecutionLog = new KraftwerkExecutionLog(); //Init logger
+		this.executionDateTime = LocalDateTime.now();
 		inDirectory = controlInputSequence.getInDirectory(inDirectoryParam);
 
 		String campaignName = inDirectory.getFileName().toString();
@@ -141,7 +144,7 @@ private void multimodalProcess() {
 	/* Step 4 : Write output files */
 	private void outputFileWriter() throws KraftwerkException {
 		WriterSequence writerSequence = new WriterSequence();
-		writerSequence.writeOutputFiles(inDirectory, vtlBindings, userInputsFile.getModeInputsMap(), metadataModels, errors, kraftwerkExecutionLog);
+		writerSequence.writeOutputFiles(inDirectory,executionDateTime, vtlBindings, userInputsFile.getModeInputsMap(), metadataModels, errors, kraftwerkExecutionLog);
 	}
 
 	/* Step 5 : Write errors */
diff --git a/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessingGenesis.java b/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessingGenesis.java
index 479d5a2f..ed9cdcc8 100644
--- a/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessingGenesis.java
+++ b/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/process/MainProcessingGenesis.java
@@ -22,6 +22,7 @@
 
 import java.io.IOException;
 import java.nio.file.Path;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -37,6 +38,7 @@ public class MainProcessingGenesis {
 	private List<KraftwerkError> errors = new ArrayList<>();
 	@Getter
 	private UserInputsGenesis userInputs;
+	private LocalDateTime executionDateTime;
 
 	/* SPECIFIC VARIABLES */
 	@Getter
@@ -55,6 +57,7 @@ public MainProcessingGenesis(ConfigProperties config) {
 
 	public void init(String idQuestionnaire) throws KraftwerkException, IOException {
 		log.info("Kraftwerk main service started for questionnaire: " + idQuestionnaire);
+		this.executionDateTime = LocalDateTime.now();
 		inDirectory = controlInputSequenceGenesis.getInDirectory(idQuestionnaire);
 		//First we check the modes present in database for the given questionnaire
 		//We build userInputs for the given questionnaire
@@ -101,7 +104,7 @@ private void multimodalProcess() {
 	/* Step 4 : Write output files */
 	private void outputFileWriter() throws KraftwerkException {
 		WriterSequence writerSequence = new WriterSequence();
-		writerSequence.writeOutputFiles(inDirectory, vtlBindings, userInputs.getModeInputsMap(), metadataModels, errors);
+		writerSequence.writeOutputFiles(inDirectory, executionDateTime, vtlBindings, userInputs.getModeInputsMap(), metadataModels, errors);
 	}
 
 	/* Step 5 : Write errors */
diff --git a/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/services/StepByStepService.java b/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/services/StepByStepService.java
index 4569d043..9f70fad4 100644
--- a/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/services/StepByStepService.java
+++ b/kraftwerk-api/src/main/java/fr/insee/kraftwerk/api/services/StepByStepService.java
@@ -20,6 +20,7 @@
 
 import java.io.File;
 import java.nio.file.Path;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -199,6 +200,7 @@ public ResponseEntity<String> writeOutputFiles(
 		} catch (KraftwerkException e) {
 			return ResponseEntity.status(e.getStatus()).body(e.getMessage());
 		}
+		LocalDateTime executionDateTime = LocalDateTime.now();
 		VtlBindings vtlBindings = new VtlBindings();
 		List<KraftwerkError> errors = new ArrayList<>();
 		// Read all bindings necessary to produce output
@@ -219,7 +221,7 @@ public ResponseEntity<String> writeOutputFiles(
 			return ResponseEntity.status(e.getStatus()).body(e.getMessage());
 		}
 		Map<String, MetadataModel> metadataModelMap = MetadataUtils.getMetadata(userInputsFile.getModeInputsMap());
-		writerSequence.writeOutputFiles(inDirectory, vtlBindings, userInputsFile.getModeInputsMap(), metadataModelMap, errors);
+		writerSequence.writeOutputFiles(inDirectory, executionDateTime, vtlBindings, userInputsFile.getModeInputsMap(), metadataModelMap, errors);
 		return ResponseEntity.ok(inDirectoryParam);
 
 	}
diff --git a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/Constants.java b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/Constants.java
index c4774848..a1589887 100644
--- a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/Constants.java
+++ b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/Constants.java
@@ -55,6 +55,7 @@ private Constants() {}
 	public static final String REPORTING_DATA_GROUP_NAME = "REPORTINGDATA";
 	public static final String REPORTING_DATA_INTERVIEWER_ID_NULL_PLACEHOLDER = "NON_AFFECTE_";
 	public static final String END_LINE = "\n";
+	public static final String OUTPUT_FOLDER_DATETIME_PATTERN = "yyyy_MM_dd_HH_mm_ss";
 
 
 	// ----- Explicit Variables Names
diff --git a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/outputs/csv/CsvOutputFiles.java b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/outputs/csv/CsvOutputFiles.java
index 5e0b739b..22a37aad 100644
--- a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/outputs/csv/CsvOutputFiles.java
+++ b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/outputs/csv/CsvOutputFiles.java
@@ -74,7 +74,7 @@ public void writeImportScripts(Map<String, MetadataModel> metadataModels, List<K
 	 */
 	@Override
 	public String outputFileName(String datasetName) {
-		return getOutputFolder().getFileName() + "_" + datasetName + ".csv";
+		return getOutputFolder().getParent().getFileName() + "_" + datasetName + ".csv";
 	}
 
 }
diff --git a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/sequence/WriterSequence.java b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/sequence/WriterSequence.java
index 6dd38892..93718d86 100644
--- a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/sequence/WriterSequence.java
+++ b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/sequence/WriterSequence.java
@@ -1,5 +1,6 @@
 package fr.insee.kraftwerk.core.sequence;
 
+import fr.insee.kraftwerk.core.Constants;
 import fr.insee.kraftwerk.core.KraftwerkError;
 import fr.insee.kraftwerk.core.exceptions.KraftwerkException;
 import fr.insee.kraftwerk.core.inputs.ModeInputs;
@@ -13,6 +14,8 @@
 import lombok.NoArgsConstructor;
 
 import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -20,12 +23,13 @@
 @NoArgsConstructor
 public class WriterSequence {
 
-	public void writeOutputFiles(Path inDirectory, VtlBindings vtlBindings, Map<String, ModeInputs> modeInputsMap, Map<String, MetadataModel> metadataModels, List<KraftwerkError> errors) throws KraftwerkException {
-		writeOutputFiles(inDirectory,vtlBindings,modeInputsMap,metadataModels,errors,null);
+	public void writeOutputFiles(Path inDirectory,LocalDateTime executionDateTime, VtlBindings vtlBindings, Map<String, ModeInputs> modeInputsMap, Map<String, MetadataModel> metadataModels, List<KraftwerkError> errors) throws KraftwerkException {
+		writeOutputFiles(inDirectory,executionDateTime,vtlBindings,modeInputsMap,metadataModels,errors,null);
 	}
 
-	public void writeOutputFiles(Path inDirectory, VtlBindings vtlBindings, Map<String, ModeInputs> modeInputsMap, Map<String, MetadataModel> metadataModels, List<KraftwerkError> errors, KraftwerkExecutionLog kraftwerkExecutionLog) throws KraftwerkException {
+	public void writeOutputFiles(Path inDirectory, LocalDateTime localDateTime, VtlBindings vtlBindings, Map<String, ModeInputs> modeInputsMap, Map<String, MetadataModel> metadataModels, List<KraftwerkError> errors, KraftwerkExecutionLog kraftwerkExecutionLog) throws KraftwerkException {
 		Path outDirectory = FileUtils.transformToOut(inDirectory);
+		outDirectory = outDirectory.resolve(localDateTime.format(DateTimeFormatter.ofPattern(Constants.OUTPUT_FOLDER_DATETIME_PATTERN)));
 		/* Step 4.1 : write csv output tables */
 		OutputFiles csvOutputFiles = new CsvOutputFiles(outDirectory, vtlBindings,  new ArrayList<>(modeInputsMap.keySet()),kraftwerkExecutionLog);
 		csvOutputFiles.writeOutputTables(metadataModels);
diff --git a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java
index 65e085f9..a8645731 100644
--- a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java
+++ b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java
@@ -3,10 +3,12 @@
 import io.cucumber.java.en.Then;
 import org.assertj.core.api.Assertions;
 
+import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Objects;
 import java.util.stream.Stream;
 
 import static cucumber.TestConstants.FUNCTIONAL_TESTS_OUTPUT_DIRECTORY;
@@ -15,7 +17,9 @@ public class LogFileDefinitions {
     static Path outDirectory = Paths.get(FUNCTIONAL_TESTS_OUTPUT_DIRECTORY);
     @Then("We should have a log file named in directory {string}")
     public void logFileExistenceCheck(String directory) throws IOException {
-        try(Stream<Path> folderStream = Files.list(outDirectory.resolve(directory))){
+        Path executionOutDirectory = outDirectory.resolve(Objects.requireNonNull(new File(outDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
+        try(Stream<Path> folderStream = Files.list(executionOutDirectory)){
             Assertions.assertThat(folderStream.filter(path ->
                     path.getFileName().toString().startsWith(directory + "_LOG_")
             )).isNotEmpty();
diff --git a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/MainDefinitions.java b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/MainDefinitions.java
index d826fc8d..b336f1f4 100644
--- a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/MainDefinitions.java
+++ b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/MainDefinitions.java
@@ -28,10 +28,12 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import static cucumber.TestConstants.*;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -140,16 +142,18 @@ public void aggregate_datasets() {
 	@When("Step 4 : We export the final version")
 	public void export_results() throws KraftwerkException {
 		WriterSequence writerSequence = new WriterSequence();
-		writerSequence.writeOutputFiles(inDirectory, vtlBindings, userInputs.getModeInputsMap(), metadataModelMap, errors);
+		writerSequence.writeOutputFiles(inDirectory, LocalDateTime.now(), vtlBindings, userInputs.getModeInputsMap(), metadataModelMap, errors);
 		writeErrorsFile(inDirectory, errors);
 		outputFiles = new CsvOutputFiles(outDirectory, vtlBindings, userInputs.getModes());
 	}
 
 	@Then("Step 5 : We check if we have {int} lines")
 	public void count_lines_in_root_tables(int expectedLineCount) throws CsvValidationException, IOException {
+		// Go to first datetime folder
+		Path executionOutDirectory = outDirectory.resolve(Objects.requireNonNull(new File(outDirectory.toString()).listFiles(File::isDirectory))[0].getName());
 		// Get reader to read the root table written in outputs
 		System.out.println("Check output file path : "
-				+ outDirectory.resolve(outputFiles.outputFileName(Constants.ROOT_GROUP_NAME)));
+				+ executionOutDirectory.resolve(outputFiles.outputFileName(Constants.ROOT_GROUP_NAME)));
 		CSVReader csvReader = CsvUtils
 				.getReader(outDirectory.resolve(outputFiles.outputFileName(Constants.ROOT_GROUP_NAME)));
 		// Count
@@ -166,8 +170,11 @@ public void count_lines_in_root_tables(int expectedLineCount) throws CsvValidati
 	@Then("Step 2 : We check root output file has {int} lines and {int} variables")
 	public void check_output_root_table(int expectedLineCount, int expectedVariablesCount)
 			throws IOException, CsvValidationException {
+		// Go to first datetime folder
+		Path executionOutDirectory = outDirectory.resolve(Objects.requireNonNull(new File(outDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
 		CSVReader csvReader = CsvUtils
-				.getReader(outDirectory.resolve(outDirectory.getFileName() + "_" + Constants.ROOT_GROUP_NAME + ".csv"));
+				.getReader(executionOutDirectory.resolve(executionOutDirectory.getFileName() + "_" + Constants.ROOT_GROUP_NAME + ".csv"));
 		// get header
 		String[] header = csvReader.readNext();
 		// Count
@@ -263,7 +270,10 @@ private void writeErrorsFile(Path inDirectory, List<KraftwerkError> errors) {
 
 	@Then("In a file named {string} there should be a {string} field")
 	public void check_field_existence(String fileName, String fieldName) throws IOException, CsvValidationException {
-		File outputReportingDataFile = new File(outDirectory + "/" + fileName);
+		// Go to first datetime folder
+		Path executionOutDirectory = outDirectory.resolve(Objects.requireNonNull(new File(outDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
+		File outputReportingDataFile = new File(executionOutDirectory + "/" + fileName);
 
 		// File existence assertion
 		assertThat(outputReportingDataFile).exists().isFile().canRead();
diff --git a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/ReportingDataDefinitions.java b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/ReportingDataDefinitions.java
index 68a9fe63..67005f99 100644
--- a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/ReportingDataDefinitions.java
+++ b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/ReportingDataDefinitions.java
@@ -16,6 +16,7 @@
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 import com.opencsv.CSVReader;
 import com.opencsv.exceptions.CsvException;
@@ -58,7 +59,11 @@ public class ReportingDataDefinitions {
     // Existence and structure tests
     @Then("We should have a file named {string} in directory {string} with {int} reporting data fields")
     public void check_contact_attempt_file(String fileName, String directory, int expectedFieldCount) throws IOException, CsvException {
-        File outputReportingDataFile = new File(outDirectory + "/" + directory + "/" + fileName);
+        // Go to first datetime folder
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
+        File outputReportingDataFile = new File(executionOutDirectory + "/" + fileName);
 
         // File existence assertion
         assertThat(outputReportingDataFile).exists().isFile().canRead();
@@ -79,7 +84,10 @@ public void check_contact_attempt_file(String fileName, String directory, int ex
 
     @Then("We shouldn't have any reporting data file in directory {string}")
     public void check_lack_of_contact_attempt_file(String directory) {
-        File outputReportingDataFile = new File(outDirectory + "/" + directory + "/" + directory + "_REPORTINGDATA.csv");
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
+        File outputReportingDataFile = new File(executionOutDirectory + "/" + directory + "_REPORTINGDATA.csv");
         assertThat(outputReportingDataFile).doesNotExist();
     }
 
@@ -87,8 +95,11 @@ public void check_lack_of_contact_attempt_file(String directory) {
     // Volumetry test
     @Then("We should have {int} lines different than header in a file named {string} in directory {string}")
     public void check_reporting_data_count(int expectedCount, String fileName, String directory) throws IOException, CsvException {
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
         CSVReader csvReader = CsvUtils.getReader(
-                Path.of(outDirectory + "/" + directory + "/" + fileName)
+                Path.of(executionOutDirectory + "/" + fileName)
         );
 
         // Get file content
@@ -105,8 +116,11 @@ public void check_reporting_data_count(int expectedCount, String fileName, Strin
     // Content tests
     @Then("For SurveyUnit {string} we should have {int} contact attempts with status {string} in a file named {string} in directory {string}")
     public void check_contact_attempt_content(String surveyUnitId, int expectedSpecificStatusCount, String expectedStatus, String fileName, String directory) throws IOException, CsvException {
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
         CSVReader csvReader = CsvUtils.getReader(
-                Path.of(outDirectory + "/" + directory + "/" + fileName)
+                Path.of(executionOutDirectory + "/" + fileName)
         );
 
         // Get file content
@@ -151,8 +165,11 @@ public void check_contact_attempt_content(String surveyUnitId, int expectedSpeci
 
     @Then("For SurveyUnit {string} we should have {int} contact states with status {string} in a file named {string} in directory {string}")
     public void check_contact_state_content(String surveyUnitId, int expectedSpecificStatusCount, String expectedStatus, String fileName, String directory) throws IOException, CsvException {
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
         CSVReader csvReader = CsvUtils.getReader(
-                Path.of(outDirectory + "/" + directory + "/" + fileName)
+                Path.of(executionOutDirectory + "/" + fileName)
         );
 
         // Get file content
@@ -197,8 +214,12 @@ public void check_contact_state_content(String surveyUnitId, int expectedSpecifi
 
     @Then("In file named {string} in directory {string} we should have the date format {string} for field {string}")
     public void check_contact_attempt_date_format(String fileName, String directory, String expectedDateFormat, String fieldName) throws IOException, CsvException {
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
+
         CSVReader csvReader = CsvUtils.getReader(
-                Path.of(outDirectory + "/" + directory + "/" + fileName)
+                Path.of(executionOutDirectory + "/" + fileName)
         );
 
         // Get file content
@@ -238,8 +259,11 @@ public void check_contact_attempt_date_format(String fileName, String directory,
 
     @Then("We shouldn't have any reporting data in {string} in directory {string}")
     public void check_reporting_data_in_root_file(String fileName, String directory) throws IOException, CsvValidationException {
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
         CSVReader csvReader = CsvUtils.getReader(
-                Path.of(outDirectory + "/" + directory + "/" + fileName)
+                Path.of(executionOutDirectory + "/" + fileName)
         );
 
         // Get header
@@ -252,8 +276,11 @@ public void check_reporting_data_in_root_file(String fileName, String directory)
 
     @Then("For SurveyUnit {string} in a file named {string} in directory {string} we should have {string} in the OUTCOME_SPOTTING field")
     public void check_outcome_spotting_result(String surveyUnitId, String fileName, String directory, String expectedOutcomeSpotting) throws IOException, CsvException {
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
         CSVReader csvReader = CsvUtils.getReader(
-                Path.of(outDirectory + "/" + directory + "/" + fileName)
+                Path.of(executionOutDirectory + "/" + fileName)
         );
 
         // Get file content
@@ -298,8 +325,11 @@ public String[] fetchConcernedSurveyUnitLineFromFile(String surveyUnitId, List<S
 
     @Then("For SurveyUnit {string} in a file named {string} in directory {string} we should have {string} in the identification field")
     public void check_identification(String surveyUnitId, String fileName, String directory, String expectedValue) throws IOException, CsvException {
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+
         CSVReader csvReader = CsvUtils.getReader(
-                Path.of(outDirectory + "/" + directory + "/" + fileName)
+                Path.of(executionOutDirectory + "/" + fileName)
         );
 
         // Get file content

From fc76ec1dd41d75c7041fd9b8393574c528654296 Mon Sep 17 00:00:00 2001
From: QDIBYS <alexis.szmundy@insee.fr>
Date: Fri, 19 Apr 2024 11:55:56 +0200
Subject: [PATCH 2/2] Fixed wrong log location

---
 .../java/fr/insee/kraftwerk/core/utils/TextFileWriter.java  | 6 +++++-
 .../java/cucumber/functional_tests/LogFileDefinitions.java  | 3 ++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/utils/TextFileWriter.java b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/utils/TextFileWriter.java
index 740ac125..905c8f6c 100644
--- a/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/utils/TextFileWriter.java
+++ b/kraftwerk-core/src/main/java/fr/insee/kraftwerk/core/utils/TextFileWriter.java
@@ -1,9 +1,11 @@
 package fr.insee.kraftwerk.core.utils;
 
+import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
+import java.util.Objects;
 
 import fr.insee.kraftwerk.core.KraftwerkError;
 import fr.insee.kraftwerk.core.utils.log.KraftwerkExecutionLog;
@@ -54,7 +56,9 @@ public static void writeErrorsFile(Path inDirectory, List<KraftwerkError> errors
 	}
 
 	public static void writeLogFile(Path inDirectory, KraftwerkExecutionLog kraftwerkExecutionLog){
-		Path tempOutputPath = FileUtils.transformToOut(inDirectory).resolve(inDirectory.getFileName() + "_LOG_" + kraftwerkExecutionLog.getStartTimeStamp() +".txt");
+		Path tempOutputPath = FileUtils.transformToOut(inDirectory);
+		tempOutputPath = tempOutputPath.resolve(Objects.requireNonNull(new File(tempOutputPath.toString()).listFiles(File::isDirectory))[0].getName())
+				.resolve(inDirectory.getFileName() + "_LOG_" + kraftwerkExecutionLog.getStartTimeStamp() +".txt");
 		FileUtils.createDirectoryIfNotExist(tempOutputPath.getParent());
 
 		try (FileWriter myWriter = new FileWriter(tempOutputPath.toFile(),true)){
diff --git a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java
index a8645731..462c3446 100644
--- a/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java
+++ b/kraftwerk-functional-tests/src/test/java/cucumber/functional_tests/LogFileDefinitions.java
@@ -17,7 +17,8 @@ public class LogFileDefinitions {
     static Path outDirectory = Paths.get(FUNCTIONAL_TESTS_OUTPUT_DIRECTORY);
     @Then("We should have a log file named in directory {string}")
     public void logFileExistenceCheck(String directory) throws IOException {
-        Path executionOutDirectory = outDirectory.resolve(Objects.requireNonNull(new File(outDirectory.toString()).listFiles(File::isDirectory))[0].getName());
+        Path executionOutDirectory = outDirectory.resolve(directory);
+        executionOutDirectory = executionOutDirectory.resolve(Objects.requireNonNull(new File(executionOutDirectory.toString()).listFiles(File::isDirectory))[0].getName());
 
         try(Stream<Path> folderStream = Files.list(executionOutDirectory)){
             Assertions.assertThat(folderStream.filter(path ->