sync current state

This commit is contained in:
2026-03-01 11:43:38 +01:00
parent 147ced281f
commit 4c8bcba514
12 changed files with 538 additions and 131 deletions
@@ -194,6 +194,11 @@ final class ImportFlowViewModel: ObservableObject {
.dateGermanDayMonthYearHourMinuteToISO8601,
.dateUnixSecondsToISO8601
]
case .number:
return [
.passthrough,
.integerLookupToNumber
]
default:
return [.passthrough]
}
@@ -203,6 +208,8 @@ final class ImportFlowViewModel: ObservableObject {
switch type {
case .passthrough:
return String(localized: "formatter.option.passthrough")
case .integerLookupToNumber:
return String(localized: "formatter.option.integer_lookup")
case .dateISO8601ToISO8601:
return String(localized: "formatter.option.date_iso")
case .dateGermanDayMonthYearToISO8601:
@@ -214,6 +221,89 @@ final class ImportFlowViewModel: ObservableObject {
}
}
func distinctSourceValues(for field: TemplateField) -> [String] {
let sourceKey = mappedSourceKey(for: field.id)
guard !sourceKey.isEmpty else {
return []
}
let values = entries
.compactMap { $0.sourceDictionary[sourceKey]?.sourceString() }
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
let unique = Array(Set(values))
return unique.sorted { lhs, rhs in
if let left = Double(lhs), let right = Double(rhs) {
return left < right
}
return lhs.localizedCaseInsensitiveCompare(rhs) == .orderedAscending
}
}
func sourceValueHistogram(for field: TemplateField) -> [(value: String, count: Int)] {
let sourceKey = mappedSourceKey(for: field.id)
guard !sourceKey.isEmpty else {
return []
}
var counts: [String: Int] = [:]
for entry in entries {
guard let value = entry.sourceDictionary[sourceKey]?.sourceString() else {
continue
}
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else {
continue
}
counts[trimmed, default: 0] += 1
}
return counts
.map { (key: $0.key, value: $0.value) }
.sorted { lhs, rhs in
if let left = Double(lhs.key), let right = Double(rhs.key) {
return left < right
}
return lhs.key.localizedCaseInsensitiveCompare(rhs.key) == .orderedAscending
}
.map { (value: $0.key, count: $0.value) }
}
func isValidTargetValue(for field: TemplateField, value: String) -> Bool {
MoodwellStateOfMindTemplate.validateFormattedTarget(
fieldID: field.id,
formatted: value,
moodValenceScale: moodValenceScale
)
}
func integerLookupCoverage(for field: TemplateField) -> (mappedDistinct: Int, totalDistinct: Int, mappedRows: Int, totalRows: Int) {
let histogram = sourceValueHistogram(for: field)
let totalRows = histogram.reduce(0) { $0 + $1.count }
let formatter = formatter(for: field.id)
guard formatter.formatterType == .integerLookupToNumber else {
return (0, histogram.count, 0, totalRows)
}
var mappedDistinct = 0
var mappedRows = 0
for item in histogram {
guard let mapped = formatter.integerLookup[item.value]?.trimmingCharacters(in: .whitespacesAndNewlines), !mapped.isEmpty else {
continue
}
if isValidTargetValue(for: field, value: mapped) {
mappedDistinct += 1
mappedRows += item.count
}
}
return (mappedDistinct, histogram.count, mappedRows, totalRows)
}
func fieldState(for field: TemplateField) -> FieldMappingState {
let sourceKey = mappedSourceKey(for: field.id)
guard !sourceKey.isEmpty else {