Skip to content

Commit

Permalink
Adapt mapping file parsing to EI 3.9.1/3.10 schema differences.
Browse files Browse the repository at this point in the history
  • Loading branch information
jedesroches committed Dec 4, 2023
1 parent 4d937cb commit 9f8f559
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.apache.commons.csv.CSVFormat
import org.apache.commons.csv.CSVParser
import org.apache.commons.csv.CSVRecord
import java.io.Reader
import kotlin.reflect.full.memberProperties
import kotlin.streams.asSequence

typealias ID = String
Expand Down Expand Up @@ -43,6 +44,48 @@ data class FoundMappingExchange(
override val comment: String = "",
) : MappingExchange

data class MethodMappingHeaders(
val flowIdHeader: String,
val flowNameHeader: String,
val flowUnitNameHeader: String,
val flowFlowStatusHeader: String,
val methodNameHeader: String,
val methodUnitHeader: String,
val methodCompartmentHeader: String,
val methodSubCompartmentHeader: String,
val conversionFactorHeader: String,
val compartmentStatusHeader: String,
) {
companion object Versions {
val ecoInvent39 = MethodMappingHeaders(
flowIdHeader = "id",
flowNameHeader = "name",
flowUnitNameHeader = "unitName",
flowFlowStatusHeader = "flow_status",
methodNameHeader = "method_name",
methodUnitHeader = "method_unit",
methodCompartmentHeader = "method_compartment",
methodSubCompartmentHeader = "method_subcompartment",
conversionFactorHeader = "conversion_factor",
compartmentStatusHeader = "compartment_status",
)

val ecoInvent310 = MethodMappingHeaders(
flowIdHeader = "elementary_flow_id",
flowNameHeader = "elementary_flow_name",
flowUnitNameHeader = "unit_name",
flowFlowStatusHeader = "flow_status",
methodNameHeader = "method_elementary_flow_name",
methodUnitHeader = "method_unit",
methodCompartmentHeader = "method_compartment",
methodSubCompartmentHeader = "method_subcompartment",
conversionFactorHeader = "conversion_factor",
compartmentStatusHeader = "compartment_status",
)
}
}


object EcospoldMethodMapper {
private val csvFormat: CSVFormat = CSVFormat.Builder.create().setHeader().build()

Expand All @@ -64,52 +107,47 @@ object EcospoldMethodMapper {

fun buildMapping(mapData: Reader): Map<ID, MappingExchange> =
CSVParser.parse(mapData, csvFormat).use { parser ->
validateHeaders(parser.headerMap)
val mappingHeaders = validateHeaders(parser.headerMap)
parser.stream().asSequence().mapNotNull { record ->
when {
record["flow_status"] == "ecoinvent orphan" -> {
record["id"] to OrphanMappingExchange(record["id"])
record[mappingHeaders.flowFlowStatusHeader] == "ecoinvent orphan" -> {
record[mappingHeaders.flowIdHeader] to OrphanMappingExchange(record[mappingHeaders.flowIdHeader])
}

record["compartment_status"].isEmpty() -> {
record["id"] to UnknownMappingExchange(record["id"])
record[mappingHeaders.compartmentStatusHeader].isEmpty() -> {
record[mappingHeaders.flowIdHeader] to UnknownMappingExchange(record[mappingHeaders.flowIdHeader])
}

else -> mappedElement(record)
else -> mappedElement(mappingHeaders, record)
}
}.toMap()
}

private fun validateHeaders(headers: Map<String, Int>) =
sequenceOf(
"compartment_status",
"conversion_factor",
"flow_status",
"id",
"method_compartment",
"method_name",
"method_subcompartment",
"method_unit",
"name",
"unitName"
)
.forEach { header ->
if (!headers.containsKey(header)) {
throw IllegalArgumentException("could not find $header in file headers. Is it a valid mapping file ?")
}
}
fun validateHeaders(headers: Map<String, Int>): MethodMappingHeaders =
when {
MethodMappingHeaders.ecoInvent39::class.memberProperties.map {
it.getter.call(MethodMappingHeaders.ecoInvent39).toString()
}.all { header -> headers.containsKey(header) } -> MethodMappingHeaders.ecoInvent39

MethodMappingHeaders.ecoInvent310::class.memberProperties.map {
it.getter.call(MethodMappingHeaders.ecoInvent310).toString()
}.all { header -> headers.containsKey(header) } -> MethodMappingHeaders.ecoInvent310

else -> throw IllegalArgumentException("Method mapping file could not be matched to the EcoInvent 3.9.1 or 3.10 header schema. Is it a valid mapping file ?")

}

private fun mappedElement(record: CSVRecord): Pair<ID, FoundMappingExchange>? =
private fun mappedElement(headers: MethodMappingHeaders, record: CSVRecord): Pair<ID, FoundMappingExchange>? =
try {
val id = record["id"]
val id = record[headers.flowIdHeader]
id to FoundMappingExchange(
id,
getConversionFactor(record["conversion_factor"]),
record["method_name"].nullIfEmpty(),
record["method_unit"].nullIfEmpty()?.let { pefUnitException(it, record["unitName"]) },
record["method_compartment"].nullIfEmpty(),
record["method_subcompartment"].nullIfEmpty(),
"Ecoinvent ID: $id. Flow, compartment status: ${record["flow_status"]}, ${record["compartment_status"]}",
getConversionFactor(record[headers.conversionFactorHeader]),
record[headers.methodNameHeader].nullIfEmpty(),
record[headers.methodUnitHeader].nullIfEmpty()?.let { pefUnitException(it, record[headers.flowUnitNameHeader]) },
record[headers.methodCompartmentHeader].nullIfEmpty(),
record[headers.methodSubCompartmentHeader].nullIfEmpty(),
"Ecoinvent ID: $id. Flow, compartment status: ${record[headers.flowFlowStatusHeader]}, ${record[headers.compartmentStatusHeader]}",
)
} catch (_: IllegalArgumentException) {
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,53 @@ class EcospoldMethodMapperTest {
result.first().value
)
}

@Test
fun `validateHeaders recognizes valid EcoInvent 391 mapping file header`() {
// given
val headerStrings = listOf(
"id",
"name",
"unitName",
"flow_status",
"method_name",
"method_unit",
"method_compartment",
"method_subcompartment",
"conversion_factor",
"compartment_status"
)
val headers = headerStrings.zip(headerStrings.indices).toMap()


// when
val result = EcospoldMethodMapper.validateHeaders(headers)

// then
assertEquals(MethodMappingHeaders.Versions.ecoInvent39, result)
}

@Test
fun `validateHeaders recognizes valid EcoInvent 310 mapping file header`() {
// given
val headerStrings = listOf(
"elementary_flow_id",
"elementary_flow_name",
"unit_name",
"flow_status",
"method_elementary_flow_name",
"method_unit",
"method_compartment",
"method_subcompartment",
"conversion_factor",
"compartment_status",
)
val headers = headerStrings.zip(headerStrings.indices).toMap()

// when
val result = EcospoldMethodMapper.validateHeaders(headers)

// then
assertEquals(MethodMappingHeaders.Versions.ecoInvent310, result)
}
}

0 comments on commit 9f8f559

Please sign in to comment.