diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 55946499..321410df 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -14,9 +14,9 @@ jobs: - name: Fetch Sources uses: actions/checkout@v3.5.1 - # Validate wrapper - - name: Gradle Wrapper Validation - uses: gradle/wrapper-validation-action@v1.0.6 + # Setup Gradle + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 # Setup Java 17 environment for the next steps - name: Setup Java diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f36516d7..6ee8d7ef 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -21,9 +21,9 @@ jobs: - name: Fetch Sources uses: actions/checkout@v3.5.1 - # Validate wrapper - - name: Gradle Wrapper Validation - uses: gradle/wrapper-validation-action@v1.0.6 + # Setup Gradle + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 # Setup Java 17 environment for the next steps - name: Setup Java diff --git a/README.md b/README.md index f20e6ddf..c5326f3c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ See our [book](https://lca-as-code.com/book) to learn more about the language. From the source ```bash -git checkout v1.8.0 +git checkout v1.8.1 ./gradlew :cli:installDist alias lcaac=$GIT_ROOT/cli/build/install/lcaac/bin/lcaac lcaac version diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt index 3fb6abda..bbc6c89c 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/AssessCommand.kt @@ -1,9 +1,9 @@ package ch.kleis.lcaac.cli.cmd -import ch.kleis.lcaac.cli.csv.CsvProcessor +import ch.kleis.lcaac.cli.csv.assess.AssessCsvProcessor import ch.kleis.lcaac.cli.csv.CsvRequest import ch.kleis.lcaac.cli.csv.CsvRequestReader -import ch.kleis.lcaac.cli.csv.CsvResultWriter +import ch.kleis.lcaac.cli.csv.assess.AssessCsvResultWriter import ch.kleis.lcaac.core.config.LcaacConfig import ch.kleis.lcaac.core.math.basic.BasicOperations import ch.kleis.lcaac.grammar.Loader @@ -67,9 +67,9 @@ class AssessCommand : CliktCommand(name = "assess", help = "Returns the unitary overriddenGlobals = dataExpressionMap(BasicOperations, globals), ).load(files, listOf(LoaderOption.WITH_PRELUDE)) - val processor = CsvProcessor(yamlConfig, symbolTable, workingDirectory.path) + val processor = AssessCsvProcessor(yamlConfig, symbolTable, workingDirectory.path) val iterator = loadRequests() - val writer = CsvResultWriter() + val writer = AssessCsvResultWriter() var first = true while (iterator.hasNext()) { val request = iterator.next() diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TraceCommand.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TraceCommand.kt index 10b41c50..815ac0b6 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TraceCommand.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/cmd/TraceCommand.kt @@ -1,20 +1,11 @@ package ch.kleis.lcaac.cli.cmd -import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram +import ch.kleis.lcaac.cli.csv.CsvRequest +import ch.kleis.lcaac.cli.csv.CsvRequestReader +import ch.kleis.lcaac.cli.csv.trace.TraceCsvProcessor +import ch.kleis.lcaac.cli.csv.trace.TraceCsvResultWriter import ch.kleis.lcaac.core.config.LcaacConfig -import ch.kleis.lcaac.core.datasource.ConnectorFactory -import ch.kleis.lcaac.core.datasource.DefaultDataSourceOperations -import ch.kleis.lcaac.core.datasource.csv.CsvConnectorBuilder -import ch.kleis.lcaac.core.lang.evaluator.Evaluator -import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException -import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer -import ch.kleis.lcaac.core.lang.value.FullyQualifiedSubstanceValue -import ch.kleis.lcaac.core.lang.value.IndicatorValue -import ch.kleis.lcaac.core.lang.value.PartiallyQualifiedSubstanceValue -import ch.kleis.lcaac.core.lang.value.ProductValue import ch.kleis.lcaac.core.math.basic.BasicOperations -import ch.kleis.lcaac.core.math.basic.BasicOperations.toDouble -import ch.kleis.lcaac.core.prelude.Prelude.Companion.sanitize import ch.kleis.lcaac.grammar.Loader import ch.kleis.lcaac.grammar.LoaderOption import com.charleskorn.kaml.decodeFromStream @@ -26,8 +17,6 @@ import com.github.ajalt.clikt.parameters.options.default import com.github.ajalt.clikt.parameters.options.help import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.types.file -import org.apache.commons.csv.CSVFormat -import org.apache.commons.csv.CSVPrinter import java.io.File @Suppress("MemberVisibilityCanBePrivate", "DuplicatedCode") @@ -45,6 +34,11 @@ class TraceCommand : CliktCommand(name = "trace", help = "Trace the contribution .help("Path to project folder or yaml file.") val projectPath: File by getProjectPath + val file: File? by option("-f", "--file").file(canBeDir = false) + .help(""" + CSV file with parameter values. + Example: `lcaac trace -f params.csv`. + """.trimIndent()) val arguments: Map by option("-D", "--parameter") .help( """ @@ -67,159 +61,48 @@ class TraceCommand : CliktCommand(name = "trace", help = "Trace the contribution } else LcaacConfig() - val ops = BasicOperations val files = lcaFiles(workingDirectory) val symbolTable = Loader( ops = BasicOperations, overriddenGlobals = dataExpressionMap(BasicOperations, globals), ).load(files, listOf(LoaderOption.WITH_PRELUDE)) - val factory = ConnectorFactory( - workingDirectory.path, - yamlConfig, - ops, - symbolTable, - listOf(CsvConnectorBuilder()) - ) - val sourceOps = DefaultDataSourceOperations(ops, yamlConfig, factory.buildConnectors()) - - val evaluator = Evaluator(symbolTable, ops, sourceOps) - val template = symbolTable.getTemplate(name, labels) - ?: throw EvaluatorException("unknown template $name$labels") - val args = prepareArguments( - DataExpressionReducer(symbolTable.data, symbolTable.dataSources, ops, sourceOps), - template, - arguments, - ) - val trace = evaluator.trace(template, args) - val system = trace.getSystemValue() - val entryPoint = trace.getEntryPoint() - - val program = ContributionAnalysisProgram(system, entryPoint) - val analysis = program.run() - - val observablePorts = analysis.getObservablePorts() - .getElements() - .sortedWith(trace.getComparator()) - val controllablePorts = analysis.getControllablePorts().getElements() - .sortedBy { it.getShortName() } - - val header = listOf( - "depth", "d_amount", "d_unit", "d_product", "alloc", - "name", "a", "b", "c", "amount", "unit", - ).plus( - controllablePorts.flatMap { - listOf( - sanitize(it.getDisplayName()), - "${sanitize(it.getDisplayName())}_unit" - ) + val processor = TraceCsvProcessor(yamlConfig, symbolTable, workingDirectory.path) + val iterator = loadRequests() + val writer = TraceCsvResultWriter() + var first = true + while (iterator.hasNext()) { + val request = iterator.next() + val result = processor.process(request) + if (first) { + echo(writer.header(result), trailingNewline = false) + first = false + } + writer.rows(result).forEach { + echo(it, trailingNewline = false) } - ) - - val products = entryPoint.products.asSequence() - val lines = products.flatMap { demandedProduct -> - val demandedAmount = demandedProduct.quantity.amount - val demandedUnit = demandedProduct.quantity.unit - val demandedProductName = demandedProduct.product.name - val allocationAmount = (demandedProduct.allocation?.amount?.toDouble() - ?: 1.0) * (demandedProduct.allocation?.unit?.scale ?: 1.0) - observablePorts.asSequence() - .map { row -> - val supply = analysis.supplyOf(row) - val depth = trace.getDepthOf(row)?.toString() ?: "" - val supplyAmount = supply.amount.value * allocationAmount - val prefix = when (row) { - is IndicatorValue -> { - listOf( - depth, - demandedAmount.toString(), - demandedUnit.toString(), - demandedProductName, - allocationAmount.toString(), - row.name, - "", - "", - "", - supplyAmount.toString(), - supply.unit.toString(), - ) - } - - is ProductValue -> { - listOf( - depth, - demandedAmount.toString(), - demandedUnit.toString(), - demandedProductName, - allocationAmount.toString(), - row.name, - row.fromProcessRef?.name ?: "", - row.fromProcessRef?.matchLabels?.toString() ?: "", - row.fromProcessRef?.arguments?.toString() ?: "", - supplyAmount.toString(), - supply.unit.toString(), - ) - } - - is FullyQualifiedSubstanceValue -> { - listOf( - depth, - demandedAmount.toString(), - demandedUnit.toString(), - demandedProductName, - allocationAmount.toString(), - row.name, - row.compartment, - row.subcompartment ?: "", - row.type.toString(), - supplyAmount.toString(), - supply.unit.toString(), - ) - } - - is PartiallyQualifiedSubstanceValue -> { - listOf( - depth, - demandedAmount.toString(), - demandedUnit.toString(), - demandedProductName, - allocationAmount.toString(), - row.name, - "", - "", - "", - supplyAmount.toString(), - supply.unit.toString(), - ) - } - } - val impacts = controllablePorts.flatMap { col -> - val impact = analysis.getPortContribution(row, col) - val impactAmount = impact.amount.value * allocationAmount - listOf( - impactAmount.toString(), - impact.unit.toString(), - ) - } - prefix.plus(impacts) - } } + } + private fun loadRequests(): Iterator { + return file?.let { loadRequestsFrom(it) } + ?: listOf(defaultRequest()).iterator() + } - val s = StringBuilder() - CSVPrinter(s, format).printRecord(header) - echo(s.toString(), trailingNewline = false) - lines - .forEach { - s.clear() - CSVPrinter(s, format).printRecord(it) - echo(s.toString(), trailingNewline = false) - } + private fun loadRequestsFrom(file: File): Iterator { + val reader = CsvRequestReader(name, labels, file.inputStream(), arguments) + return reader.iterator() } - private val format = CSVFormat.DEFAULT.builder() - .setHeader() - .setSkipHeaderRecord(true) - .setRecordSeparator(System.lineSeparator()) - .build() + private fun defaultRequest(): CsvRequest { + val pairs = arguments.toList() + val header = pairs.mapIndexed { index, pair -> pair.first to index }.toMap() + val record = pairs.map { it.second } + return CsvRequest( + name, + labels, + header, + record, + ) + } } diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvProcessor.kt similarity index 91% rename from cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt rename to cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvProcessor.kt index aa029551..e2104df3 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessor.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvProcessor.kt @@ -1,6 +1,7 @@ -package ch.kleis.lcaac.cli.csv +package ch.kleis.lcaac.cli.csv.assess import ch.kleis.lcaac.cli.cmd.prepareArguments +import ch.kleis.lcaac.cli.csv.CsvRequest import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram import ch.kleis.lcaac.core.config.LcaacConfig import ch.kleis.lcaac.core.datasource.ConnectorFactory @@ -13,7 +14,7 @@ import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer import ch.kleis.lcaac.core.math.basic.BasicNumber import ch.kleis.lcaac.core.math.basic.BasicOperations -class CsvProcessor( +class AssessCsvProcessor( config: LcaacConfig, private val symbolTable: SymbolTable, workingDirectory: String, @@ -30,7 +31,7 @@ class CsvProcessor( private val dataReducer = DataExpressionReducer(symbolTable.data, symbolTable.dataSources, ops, sourceOps) private val evaluator = Evaluator(symbolTable, ops, sourceOps) - fun process(request: CsvRequest): List { + fun process(request: CsvRequest): List { val reqName = request.processName val reqLabels = request.matchLabels val template = symbolTable.getTemplate(reqName, reqLabels) @@ -44,7 +45,7 @@ class CsvProcessor( return entryPoint.products.map { output -> val outputPort = output.product val impacts = analysis.getUnitaryImpacts(outputPort) - CsvResult( + AssessCsvResult( request, outputPort, impacts, diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvResult.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResult.kt similarity index 75% rename from cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvResult.kt rename to cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResult.kt index 69389641..948f4931 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvResult.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResult.kt @@ -1,10 +1,11 @@ -package ch.kleis.lcaac.cli.csv +package ch.kleis.lcaac.cli.csv.assess +import ch.kleis.lcaac.cli.csv.CsvRequest import ch.kleis.lcaac.core.lang.value.MatrixColumnIndex import ch.kleis.lcaac.core.lang.value.QuantityValue import ch.kleis.lcaac.core.math.basic.BasicNumber -data class CsvResult( +data class AssessCsvResult( val request: CsvRequest, val output: MatrixColumnIndex, val impacts: Map, QuantityValue>, diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvResultWriter.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResultWriter.kt similarity index 88% rename from cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvResultWriter.kt rename to cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResultWriter.kt index f2b668e7..ebecff53 100644 --- a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/CsvResultWriter.kt +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResultWriter.kt @@ -1,17 +1,17 @@ -package ch.kleis.lcaac.cli.csv +package ch.kleis.lcaac.cli.csv.assess import ch.kleis.lcaac.core.prelude.Prelude import org.apache.commons.csv.CSVFormat import org.apache.commons.csv.CSVPrinter -class CsvResultWriter { +class AssessCsvResultWriter { private val format = CSVFormat.DEFAULT.builder() .setHeader() .setSkipHeaderRecord(true) .setRecordSeparator(System.lineSeparator()) .build() - fun header(first: CsvResult): String { + fun header(first: AssessCsvResult): String { val header = first.request.columns() .plus(listOf("product", "amount", "reference unit")) .plus(first.impacts.toList() @@ -26,7 +26,7 @@ class CsvResultWriter { return s.toString() } - fun row(result: CsvResult): String { + fun row(result: AssessCsvResult): String { val line = result.request.arguments() .plus(listOf(result.output.getShortName(), "1.0", result.output.referenceUnit().symbol)) .plus(result.impacts.toList() diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvProcessor.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvProcessor.kt new file mode 100644 index 00000000..a12a7d73 --- /dev/null +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvProcessor.kt @@ -0,0 +1,72 @@ +package ch.kleis.lcaac.cli.csv.trace + +import ch.kleis.lcaac.cli.cmd.prepareArguments +import ch.kleis.lcaac.cli.csv.CsvRequest +import ch.kleis.lcaac.core.assessment.ContributionAnalysisProgram +import ch.kleis.lcaac.core.config.LcaacConfig +import ch.kleis.lcaac.core.datasource.ConnectorFactory +import ch.kleis.lcaac.core.datasource.DefaultDataSourceOperations +import ch.kleis.lcaac.core.datasource.csv.CsvConnectorBuilder +import ch.kleis.lcaac.core.lang.SymbolTable +import ch.kleis.lcaac.core.lang.evaluator.Evaluator +import ch.kleis.lcaac.core.lang.evaluator.EvaluatorException +import ch.kleis.lcaac.core.lang.evaluator.reducer.DataExpressionReducer +import ch.kleis.lcaac.core.math.basic.BasicNumber +import ch.kleis.lcaac.core.math.basic.BasicOperations + +class TraceCsvProcessor( + config: LcaacConfig, + private val symbolTable: SymbolTable, + workingDirectory: String, +) { + private val ops = BasicOperations + private val factory = ConnectorFactory( + workingDirectory, + config, + ops, + symbolTable, + listOf(CsvConnectorBuilder()) + ) + private val sourceOps = DefaultDataSourceOperations(ops, config, factory.buildConnectors()) + private val dataReducer = DataExpressionReducer(symbolTable.data, symbolTable.dataSources, ops, sourceOps) + private val evaluator = Evaluator(symbolTable, ops, sourceOps) + + fun process(request: CsvRequest): TraceCsvResult { + val reqName = request.processName + val reqLabels = request.matchLabels + val template = symbolTable.getTemplate(reqName, reqLabels) + ?: throw EvaluatorException("Could not get template for ${reqName}${reqLabels}") + val arguments = prepareArguments(dataReducer, template, request.toMap()) + val trace = evaluator.trace(template, arguments) + val systemValue = trace.getSystemValue() + val entryPoint = trace.getEntryPoint() + val program = ContributionAnalysisProgram(systemValue, entryPoint) + val analysis = program.run() + val observablePorts = analysis.getObservablePorts() + .getElements() + .sortedWith(trace.getComparator()) + val controllablePorts = analysis.getControllablePorts().getElements() + .sortedBy { it.getShortName() } + val products = entryPoint.products.asSequence() + + val items = products.flatMap { demandedProduct -> + observablePorts.asSequence().map { + val depth = trace.getDepthOf(it) + val supply = analysis.supplyOf(it) + TraceCsvResultItem( + depth ?: -1, + demandedProduct, + supply, + it, + controllablePorts.associateWith { col -> + analysis.getPortContribution(it, col) + }, + ) + } + }.toList() + return TraceCsvResult( + request, + trace = items, + ) + } +} diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResult.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResult.kt new file mode 100644 index 00000000..05f029f3 --- /dev/null +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResult.kt @@ -0,0 +1,20 @@ +package ch.kleis.lcaac.cli.csv.trace + +import ch.kleis.lcaac.cli.csv.CsvRequest +import ch.kleis.lcaac.core.lang.value.MatrixColumnIndex +import ch.kleis.lcaac.core.lang.value.QuantityValue +import ch.kleis.lcaac.core.lang.value.TechnoExchangeValue +import ch.kleis.lcaac.core.math.basic.BasicNumber + +data class TraceCsvResult( + val request: CsvRequest, + val trace: List, +) + +data class TraceCsvResultItem( + val depth: Int, + val demandedProduct: TechnoExchangeValue, + val supply: QuantityValue, + val output: MatrixColumnIndex, + val impacts: Map, QuantityValue>, +) diff --git a/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResultWriter.kt b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResultWriter.kt new file mode 100644 index 00000000..46f75104 --- /dev/null +++ b/cli/src/main/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResultWriter.kt @@ -0,0 +1,117 @@ +package ch.kleis.lcaac.cli.csv.trace + +import ch.kleis.lcaac.core.lang.value.FullyQualifiedSubstanceValue +import ch.kleis.lcaac.core.lang.value.IndicatorValue +import ch.kleis.lcaac.core.lang.value.PartiallyQualifiedSubstanceValue +import ch.kleis.lcaac.core.lang.value.ProductValue +import ch.kleis.lcaac.core.math.basic.BasicOperations.toDouble +import org.apache.commons.csv.CSVFormat +import org.apache.commons.csv.CSVPrinter + +class TraceCsvResultWriter { + private val format = CSVFormat.DEFAULT.builder() + .setHeader() + .setSkipHeaderRecord(true) + .setRecordSeparator(System.lineSeparator()) + .build() + + fun header(first: TraceCsvResult): String { + val header = first.request.columns() + .plus(listOf("depth", "d_amount", "d_unit", "d_product", "alloc", "name", "a", "b", "c", "amount", "unit")) + .plus( + first.trace.first().impacts.toList() + .flatMap { + listOf( + it.first.getShortName(), + "${it.first.getShortName()}_unit", + ) + }) + val s = StringBuilder() + CSVPrinter(s, format).printRecord(header) + return s.toString() + } + + @Suppress("DuplicatedCode") + fun rows(result: TraceCsvResult): List { + val lines = result.trace.map { line -> + val demandedAmount = line.demandedProduct.quantity.amount + val demandedUnit = line.demandedProduct.quantity.unit + val demandedProductName = line.demandedProduct.product.name + val allocationAmount = (line.demandedProduct.allocation?.amount?.toDouble() + ?: 1.0) * (line.demandedProduct.allocation?.unit?.scale ?: 1.0) + val supplyAmount = line.supply.amount.value * allocationAmount + val prefix = result.request.arguments().plus( + when (line.output) { + is IndicatorValue<*> -> listOf( + line.depth, + demandedAmount.toString(), + demandedUnit.toString(), + demandedProductName, + allocationAmount.toString(), + line.output.name, + "", + "", + "", + supplyAmount.toString(), + line.supply.unit.toString(), + ) + + is ProductValue<*> -> + listOf( + line.depth, + demandedAmount.toString(), + demandedUnit.toString(), + demandedProductName, + allocationAmount.toString(), + line.output.name, + line.output.fromProcessRef?.name ?: "", + line.output.fromProcessRef?.matchLabels?.toString() ?: "", + line.output.fromProcessRef?.arguments?.toString() ?: "", + supplyAmount.toString(), + line.supply.unit.toString(), + ) + + is FullyQualifiedSubstanceValue<*> -> listOf( + line.depth, + demandedAmount.toString(), + demandedUnit.toString(), + demandedProductName, + allocationAmount.toString(), + line.output.name, + line.output.compartment, + line.output.subcompartment ?: "", + line.output.type.toString(), + supplyAmount.toString(), + line.supply.unit.toString(), + ) + + is PartiallyQualifiedSubstanceValue<*> -> listOf( + line.depth, + demandedAmount.toString(), + demandedUnit.toString(), + demandedProductName, + allocationAmount.toString(), + line.output.name, + "", + "", + "", + supplyAmount.toString(), + line.supply.unit.toString(), + ) + } + ) + val impacts = line.impacts.flatMap { + val impact = it.value + val impactAmount = impact.amount.value * allocationAmount + listOf( + impactAmount.toString(), + impact.unit.toString(), + ) + } + val s = StringBuilder() + CSVPrinter(s, format).printRecord(prefix.plus(impacts)) + s.toString() + } + return lines + } +} diff --git a/cli/src/main/resources/META-INF/lcaac.properties b/cli/src/main/resources/META-INF/lcaac.properties index fe6b4765..f095f5b2 100644 --- a/cli/src/main/resources/META-INF/lcaac.properties +++ b/cli/src/main/resources/META-INF/lcaac.properties @@ -1,3 +1,3 @@ author=Kleis Technology description=LCA as Code CLI -version=1.8.0 +version=1.8.1 diff --git a/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessorTest.kt b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvProcessorTest.kt similarity index 95% rename from cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessorTest.kt rename to cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvProcessorTest.kt index daab872a..f52a19c1 100644 --- a/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvProcessorTest.kt +++ b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvProcessorTest.kt @@ -1,5 +1,6 @@ -package ch.kleis.lcaac.cli.csv +package ch.kleis.lcaac.cli.csv.assess +import ch.kleis.lcaac.cli.csv.CsvRequest import ch.kleis.lcaac.core.config.LcaacConfig import ch.kleis.lcaac.core.lang.SymbolTable import ch.kleis.lcaac.core.lang.dimension.Dimension @@ -16,7 +17,7 @@ import org.antlr.v4.runtime.CommonTokenStream import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -class CsvProcessorTest { +class AssessCsvProcessorTest { @Test fun processWithRecordParam_withMissingColumn_shouldThrow() { // given @@ -42,7 +43,7 @@ class CsvProcessorTest { """.trimIndent() val symbolTable = load(content) val config = LcaacConfig() - val processor = CsvProcessor(config, symbolTable, ".") + val processor = AssessCsvProcessor(config, symbolTable, ".") val request = CsvRequest( "main", emptyMap(), @@ -106,7 +107,7 @@ class CsvProcessorTest { """.trimIndent() val symbolTable = load(content) val config = LcaacConfig() - val processor = CsvProcessor(config, symbolTable, ".") + val processor = AssessCsvProcessor(config, symbolTable, ".") val request = CsvRequest( "main", emptyMap(), @@ -162,7 +163,7 @@ class CsvProcessorTest { """.trimIndent() val symbolTable = load(content) val config = LcaacConfig() - val processor = CsvProcessor(config, symbolTable, ".") + val processor = AssessCsvProcessor(config, symbolTable, ".") val request = CsvRequest( "main", emptyMap(), diff --git a/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvResultWriterTest.kt b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResultWriterTest.kt similarity index 88% rename from cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvResultWriterTest.kt rename to cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResultWriterTest.kt index ad87d2a9..e415f138 100644 --- a/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/CsvResultWriterTest.kt +++ b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/assess/AssessCsvResultWriterTest.kt @@ -1,5 +1,6 @@ -package ch.kleis.lcaac.cli.csv +package ch.kleis.lcaac.cli.csv.assess +import ch.kleis.lcaac.cli.csv.CsvRequest import ch.kleis.lcaac.core.lang.dimension.Dimension import ch.kleis.lcaac.core.lang.dimension.UnitSymbol import ch.kleis.lcaac.core.lang.value.* @@ -8,7 +9,7 @@ import kotlin.test.Test import kotlin.test.assertEquals -class CsvResultWriterTest { +class AssessCsvResultWriterTest { @Test fun header() { // given @@ -35,8 +36,8 @@ class CsvResultWriterTest { IndicatorValue("co2", kg) as MatrixColumnIndex to QuantityValue(BasicNumber(0.5), kg) ) - val result = CsvResult(request, output, impacts) - val writer = CsvResultWriter() + val result = AssessCsvResult(request, output, impacts) + val writer = AssessCsvResultWriter() // when val actual = writer.header(result) @@ -71,8 +72,8 @@ class CsvResultWriterTest { IndicatorValue("co2", kg) as MatrixColumnIndex to QuantityValue(BasicNumber(0.5), kg) ) - val result = CsvResult(request, output, impacts) - val writer = CsvResultWriter() + val result = AssessCsvResult(request, output, impacts) + val writer = AssessCsvResultWriter() // when val actual = writer.row(result) diff --git a/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResultWriterTest.kt b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResultWriterTest.kt new file mode 100644 index 00000000..28e6d3da --- /dev/null +++ b/cli/src/test/kotlin/ch/kleis/lcaac/cli/csv/trace/TraceCsvResultWriterTest.kt @@ -0,0 +1,126 @@ +package ch.kleis.lcaac.cli.csv.trace + +import ch.kleis.lcaac.cli.csv.CsvRequest +import ch.kleis.lcaac.core.lang.dimension.Dimension +import ch.kleis.lcaac.core.lang.dimension.UnitSymbol +import ch.kleis.lcaac.core.lang.value.* +import ch.kleis.lcaac.core.math.basic.BasicNumber +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + + +class TraceCsvResultWriterTest { + private val kWh = UnitValue(UnitSymbol.of("kWh"), 1000.0, Dimension.of("energy")) + private val kg = UnitValue(UnitSymbol.of("kg"), 1.0, Dimension.of("mass")) + private val u = UnitValue(UnitSymbol.of("u"), 1.0, Dimension.None) + + @Test + fun header() { + // given + val request = CsvRequest( + "main", + emptyMap(), + header = mapOf("id" to 0, "x" to 1), + record = listOf("id-0", "0.5") + ) + val demandedProduct = TechnoExchangeValue( + QuantityValue(BasicNumber(1.0), u), + ProductValue("foo", u, FromProcessRefValue("foo")), + ) + val output = ProductValue( + name = "electricity", + referenceUnit = kWh, + fromProcessRef = FromProcessRefValue( + "main", + arguments = mapOf( + "x" to QuantityValue(BasicNumber(0.5), u) + ) + ) + ) + val supply = QuantityValue(BasicNumber(1.0), kWh) + val impacts = mapOf( + IndicatorValue("co2", kg) as MatrixColumnIndex + to QuantityValue(BasicNumber(0.5), kg) + ) + val result = TraceCsvResult( + request, + trace = listOf( + TraceCsvResultItem( + 0, + demandedProduct, + supply, + output, + impacts, + ) + ) + ) + val writer = TraceCsvResultWriter() + + // when + val actual = writer.header(result) + + // then + assertEquals("id,x,depth,d_amount,d_unit,d_product,alloc,name,a,b,c,amount,unit,co2,co2_unit\n", actual) + } + + @Test + fun rows() { + // given + val request = CsvRequest( + "main", + emptyMap(), + header = mapOf("id" to 0, "x" to 1), + record = listOf("id-0", "0.5") + ) + val demandedProduct = TechnoExchangeValue( + QuantityValue(BasicNumber(1.0), u), + ProductValue("foo", u, FromProcessRefValue("foo")), + ) + val output = ProductValue( + name = "electricity", + referenceUnit = kWh, + fromProcessRef = FromProcessRefValue( + "main", + arguments = mapOf( + "x" to QuantityValue(BasicNumber(0.5), u) + ) + ) + ) + val supply = QuantityValue(BasicNumber(1.0), kWh) + val impacts = mapOf( + IndicatorValue("co2", kg) as MatrixColumnIndex + to QuantityValue(BasicNumber(0.5), kg) + ) + val result = TraceCsvResult( + request, + trace = listOf( + TraceCsvResultItem( + 0, + demandedProduct, + supply, + output, + impacts, + ), + TraceCsvResultItem( + 1, + demandedProduct, + supply, + output, + impacts, + ) + ) + ) + val writer = TraceCsvResultWriter() + + // when + val actual = writer.rows(result) + + // then + assertEquals( + listOf( + "id-0,0.5,0,1.0,u,foo,1.0,electricity,main,{},{x=0.5 u},1.0,kWh,0.5,kg\n", + "id-0,0.5,1,1.0,u,foo,1.0,electricity,main,{},{x=0.5 u},1.0,kWh,0.5,kg\n", + ), actual + ) + } +} diff --git a/gradle.properties b/gradle.properties index e29bb368..368f657e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,4 @@ javaVersion=17 gradleVersion=7.6 org.gradle.jvmargs=-Xmx4096m lcaacGroup=ch.kleis.lcaac -lcaacVersion=1.8.0 +lcaacVersion=1.8.1 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 070cb702..e1adfb49 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists