feat(import): warn on generation monotonicity violations (#689)
Inject RelationshipService into CanonicalImportOrchestrator and walk PARENT_OF edges in the family network after both person loaders finish (before documents). For every edge where child.generation is set and not strictly deeper than parent.generation, log a WARN — soft check, never fails the batch. Reads through getFamilyNetwork() per the layering rule (orchestrator never touches PersonRelationshipRepository directly). Curators see the warning in the import log; the rest of the pipeline is unaffected so data with curatorial gaps still loads cleanly. Refs #689 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,13 +4,21 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.raddatz.familienarchiv.exception.DomainException;
|
||||
import org.raddatz.familienarchiv.exception.ErrorCode;
|
||||
import org.raddatz.familienarchiv.person.relationship.RelationType;
|
||||
import org.raddatz.familienarchiv.person.relationship.RelationshipService;
|
||||
import org.raddatz.familienarchiv.person.relationship.dto.NetworkDTO;
|
||||
import org.raddatz.familienarchiv.person.relationship.dto.PersonNodeDTO;
|
||||
import org.raddatz.familienarchiv.person.relationship.dto.RelationshipDTO;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Runs the four canonical loaders in their real dependency order — encoded explicitly
|
||||
@@ -34,6 +42,7 @@ public class CanonicalImportOrchestrator {
|
||||
private final PersonRegisterImporter personRegisterImporter;
|
||||
private final PersonTreeImporter personTreeImporter;
|
||||
private final DocumentImporter documentImporter;
|
||||
private final RelationshipService relationshipService;
|
||||
|
||||
@Value("${app.import.dir:/import}")
|
||||
private String canonicalDir;
|
||||
@@ -67,6 +76,7 @@ public class CanonicalImportOrchestrator {
|
||||
tagTreeImporter.load(tagTree);
|
||||
personRegisterImporter.load(persons);
|
||||
personTreeImporter.load(personsTree);
|
||||
warnOnGenerationMonotonicityViolations();
|
||||
DocumentImporter.LoadResult result = documentImporter.load(documents);
|
||||
|
||||
currentStatus = new ImportStatus(ImportStatus.State.DONE, "IMPORT_DONE",
|
||||
@@ -91,4 +101,31 @@ public class CanonicalImportOrchestrator {
|
||||
}
|
||||
return artifact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks every PARENT_OF edge in the family graph and logs a WARN whenever a child's
|
||||
* generation is not strictly deeper than its parent's. Soft check only — the import
|
||||
* is never aborted; the warning is a forensic signal for the curator. Reads through
|
||||
* {@link RelationshipService} so the orchestrator stays within the layering rule
|
||||
* (no direct repository access).
|
||||
*/
|
||||
private void warnOnGenerationMonotonicityViolations() {
|
||||
NetworkDTO network = relationshipService.getFamilyNetwork();
|
||||
Map<UUID, PersonNodeDTO> byId = new HashMap<>(network.nodes().size());
|
||||
for (PersonNodeDTO node : network.nodes()) {
|
||||
byId.put(node.id(), node);
|
||||
}
|
||||
for (RelationshipDTO edge : network.edges()) {
|
||||
if (edge.relationType() != RelationType.PARENT_OF) continue;
|
||||
PersonNodeDTO parent = byId.get(edge.personId());
|
||||
PersonNodeDTO child = byId.get(edge.relatedPersonId());
|
||||
if (parent == null || child == null) continue;
|
||||
Integer pg = parent.generation();
|
||||
Integer cg = child.generation();
|
||||
if (pg != null && cg != null && cg <= pg) {
|
||||
log.warn("Generation monotonicity violation: parent {} (G{}) -> child {} (G{})",
|
||||
parent.displayName(), pg, child.displayName(), cg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user