Fix story intelligence review regressions
This commit is contained in:
parent
99d818ed9f
commit
d2449a4683
@ -56,6 +56,7 @@ var tests = new (string Name, Action Test)[]
|
|||||||
("Relationship observations do not create event spam", RelationshipObservationsDoNotCreateEventSpam),
|
("Relationship observations do not create event spam", RelationshipObservationsDoNotCreateEventSpam),
|
||||||
("Relationship review orders by first story appearance", RelationshipReviewOrdersByFirstStoryAppearance),
|
("Relationship review orders by first story appearance", RelationshipReviewOrdersByFirstStoryAppearance),
|
||||||
("Relationship evidence priority preserves explicit semantics", RelationshipEvidencePriorityPreservesExplicitSemantics),
|
("Relationship evidence priority preserves explicit semantics", RelationshipEvidencePriorityPreservesExplicitSemantics),
|
||||||
|
("Relationship evidence handles retained hostile and incidental examples", RelationshipEvidenceHandlesRetainedHostileAndIncidentalExamples),
|
||||||
("Knowledge signals map to existing knowledge states", KnowledgeSignalsMapToExistingStates),
|
("Knowledge signals map to existing knowledge states", KnowledgeSignalsMapToExistingStates),
|
||||||
("Knowledge recipient resolves narrator through POV", KnowledgeRecipientResolvesNarratorThroughPov),
|
("Knowledge recipient resolves narrator through POV", KnowledgeRecipientResolvesNarratorThroughPov),
|
||||||
("Knowledge identity reveal preserves subjective timing", KnowledgeIdentityRevealPreservesSubjectiveTiming),
|
("Knowledge identity reveal preserves subjective timing", KnowledgeIdentityRevealPreservesSubjectiveTiming),
|
||||||
@ -114,6 +115,7 @@ var tests = new (string Name, Action Test)[]
|
|||||||
("Story Intelligence downstream imports prefer canonical SceneIDs", StoryIntelligenceDownstreamImportsPreferCanonicalSceneIds),
|
("Story Intelligence downstream imports prefer canonical SceneIDs", StoryIntelligenceDownstreamImportsPreferCanonicalSceneIds),
|
||||||
("Story Intelligence scene prompt no longer asks for duplicate factual summary", StoryIntelligenceScenePromptNoLongerRequestsDuplicateSummary),
|
("Story Intelligence scene prompt no longer asks for duplicate factual summary", StoryIntelligenceScenePromptNoLongerRequestsDuplicateSummary),
|
||||||
("Story Intelligence review uses canonical structural summaries and counts", StoryIntelligenceReviewUsesCanonicalStructuralSummariesAndCounts),
|
("Story Intelligence review uses canonical structural summaries and counts", StoryIntelligenceReviewUsesCanonicalStructuralSummariesAndCounts),
|
||||||
|
("Story Intelligence review centre completion is category scoped", StoryIntelligenceReviewCentreCompletionIsCategoryScoped),
|
||||||
("Word sync requires save acknowledgement", WordSyncRequiresSaveAcknowledgement),
|
("Word sync requires save acknowledgement", WordSyncRequiresSaveAcknowledgement),
|
||||||
("Word sync retry resumes existing structure", WordSyncRetryResumesExistingStructure),
|
("Word sync retry resumes existing structure", WordSyncRetryResumesExistingStructure),
|
||||||
("Story Intelligence enriches existing structural scenes", StoryIntelligenceEnrichesExistingStructuralScenes),
|
("Story Intelligence enriches existing structural scenes", StoryIntelligenceEnrichesExistingStructuralScenes),
|
||||||
@ -2264,6 +2266,18 @@ static void RelationshipEvidencePriorityPreservesExplicitSemantics()
|
|||||||
Assert(RelationshipTypeFromEvidence("affection", "Nora likes Alison and sees her as like an older sister.", "Alison is a nurse who recently moved back to town.", "Nora", "Alison") != "Medical", "Occupation-only context should not create a Medical relationship.");
|
Assert(RelationshipTypeFromEvidence("affection", "Nora likes Alison and sees her as like an older sister.", "Alison is a nurse who recently moved back to town.", "Nora", "Alison") != "Medical", "Occupation-only context should not create a Medical relationship.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void RelationshipEvidenceHandlesRetainedHostileAndIncidentalExamples()
|
||||||
|
{
|
||||||
|
Assert(RelationshipTypeFromEvidence("conflict", "Maggie slaps Chrissy, calls her a bitch and threatens violence after discovering Chrissy's involvement.", null, "Maggie", "Chrissy") == "Rival", "Slap/threat evidence from retained Scene JSON should classify as Rival.");
|
||||||
|
Assert(RelationshipTypeFromEvidence("mistrust", "Rebecca demands Chrissy never speak to her again and expresses disgust.", null, "Rebecca", "Chrissy") == "Rival", "Never-speak/disgust evidence should remain adversarial.");
|
||||||
|
Assert(RelationshipTypeFromEvidence("dependence", "Maggie accepts Greg's offer of a lift and his help driving Rosie home.", null, "Maggie", "Greg") == "Unknown", "One practical lift/help should not become Authority, Professional or Ally.");
|
||||||
|
Assert(RelationshipTypeFromEvidence("protection", "Greg offers to take Rosie to the hospital and pulls the car over when she is sick.", null, "Greg", "Rosie") == "Unknown", "An incidental hospital offer should not create a professional or medical relationship.");
|
||||||
|
Assert(RelationshipTypeFromEvidence("authority", "Mary is late and is Rosie's mum expected to arrive.", null, "Maggie", "Mary") != "Authority", "A broad authority signal without an authority role should not create Authority.");
|
||||||
|
Assert(RelationshipTypeFromEvidence("professional", "Greg offers to take Rosie to the hospital and pulls the car over when she is sick.", null, "Greg", "Rosie") != "Professional", "A broad professional signal without work-role evidence should not create Professional.");
|
||||||
|
Assert(RelationshipTypeFromEvidence("protection", "Rosie's mum Mary knocked at the door and drives them in her old Ford Cortina.", null, "Mary", "Rosie") == "Parent", "Named parent evidence should classify Mary/Rosie as Parent.");
|
||||||
|
Assert(RelationshipTypeFromEvidence("authority", "The social worker says Beth's life is another box on a checklist.", null, "social worker", "Beth") == "Authority", "Explicit social-worker role evidence should still classify Authority.");
|
||||||
|
}
|
||||||
|
|
||||||
static void KnowledgeSignalsMapToExistingStates()
|
static void KnowledgeSignalsMapToExistingStates()
|
||||||
{
|
{
|
||||||
var states = new List<KnowledgeState>
|
var states = new List<KnowledgeState>
|
||||||
@ -3308,6 +3322,37 @@ static void StoryIntelligenceReviewUsesCanonicalStructuralSummariesAndCounts()
|
|||||||
Assert(repository.Contains("SceneID = item.SceneID ??", StringComparison.Ordinal), "Commit should target canonical SceneID when one exists.");
|
Assert(repository.Contains("SceneID = item.SceneID ??", StringComparison.Ordinal), "Commit should target canonical SceneID when one exists.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void StoryIntelligenceReviewCentreCompletionIsCategoryScoped()
|
||||||
|
{
|
||||||
|
var root = Path.Combine(AppContext.BaseDirectory, "../../../../PlotLine");
|
||||||
|
var onboardingStory = File.ReadAllText(Path.Combine(root, "Services/OnboardingStoryIntelligenceService.cs"));
|
||||||
|
var controller = File.ReadAllText(Path.Combine(root, "Controllers/OnboardingController.cs"));
|
||||||
|
var reviewView = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceReviewCentre.cshtml"));
|
||||||
|
|
||||||
|
Assert(onboardingStory.Contains("LocationStageComplete = locationReview.IsComplete", StringComparison.Ordinal), "Location tile completion should come from the Location review service.");
|
||||||
|
Assert(onboardingStory.Contains("AssetStageComplete = assetReview.IsComplete", StringComparison.Ordinal), "Asset tile completion should come from the Asset review service.");
|
||||||
|
Assert(onboardingStory.Contains("RelationshipStageComplete = relationshipReview.IsComplete", StringComparison.Ordinal), "Relationship tile completion should come from the Relationship review service.");
|
||||||
|
Assert(onboardingStory.Contains("KnowledgeStageComplete = knowledgeReview.IsComplete", StringComparison.Ordinal), "Knowledge tile completion should come from the Knowledge review service.");
|
||||||
|
Assert(onboardingStory.Contains("NarrativeStageComplete = narrative.IsComplete", StringComparison.Ordinal), "Narrative tile completion should come from the Narrative review service.");
|
||||||
|
Assert(onboardingStory.Contains("batch.LocationStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Location completion from the single pipeline stage marker.");
|
||||||
|
Assert(onboardingStory.Contains("batch.AssetStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Asset completion from the single pipeline stage marker.");
|
||||||
|
Assert(onboardingStory.Contains("batch.RelationshipStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Relationship completion from the single pipeline stage marker.");
|
||||||
|
Assert(onboardingStory.Contains("batch.KnowledgeStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Knowledge completion from the single pipeline stage marker.");
|
||||||
|
Assert(onboardingStory.Contains("batch.NarrativeStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Narrative completion from the single pipeline stage marker.");
|
||||||
|
Assert(reviewView.Contains("Model.PipelineDashboard.RelationshipStageComplete", StringComparison.Ordinal), "Review Centre should render the scoped dashboard completion flags.");
|
||||||
|
|
||||||
|
var locationGet = ExtractMethod(controller, "StoryIntelligenceLocations");
|
||||||
|
var assetGet = ExtractMethod(controller, "StoryIntelligenceAssets");
|
||||||
|
var relationshipGet = ExtractMethod(controller, "StoryIntelligenceRelationships");
|
||||||
|
var knowledgeGet = ExtractMethod(controller, "StoryIntelligenceKnowledge");
|
||||||
|
var narrativeGet = ExtractMethod(controller, "StoryIntelligenceNarrative");
|
||||||
|
Assert(!locationGet.Contains("RecordLocationImportAsync", StringComparison.Ordinal), "Opening Location review should not mark Location complete.");
|
||||||
|
Assert(!assetGet.Contains("RecordAssetImportAsync", StringComparison.Ordinal), "Opening Asset review should not mark Asset complete.");
|
||||||
|
Assert(!relationshipGet.Contains("RecordRelationshipImportAsync", StringComparison.Ordinal), "Opening Relationship review should not mark Relationship complete.");
|
||||||
|
Assert(!knowledgeGet.Contains("RecordKnowledgeImportAsync", StringComparison.Ordinal), "Opening Knowledge review should not mark Knowledge complete.");
|
||||||
|
Assert(!narrativeGet.Contains("RecordNarrativeImportAsync", StringComparison.Ordinal), "Opening Narrative review should not mark Narrative complete.");
|
||||||
|
}
|
||||||
|
|
||||||
static void WordSyncRequiresSaveAcknowledgement()
|
static void WordSyncRequiresSaveAcknowledgement()
|
||||||
{
|
{
|
||||||
var root = Path.Combine(AppContext.BaseDirectory, "../../../../PlotLine");
|
var root = Path.Combine(AppContext.BaseDirectory, "../../../../PlotLine");
|
||||||
@ -5864,6 +5909,34 @@ static void Assert(bool condition, string message)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string ExtractMethod(string source, string methodName)
|
||||||
|
{
|
||||||
|
var signature = $" {methodName}(";
|
||||||
|
var signatureIndex = source.IndexOf(signature, StringComparison.Ordinal);
|
||||||
|
Assert(signatureIndex >= 0, $"{methodName} was not found.");
|
||||||
|
var bodyStart = source.IndexOf('{', signatureIndex);
|
||||||
|
Assert(bodyStart >= 0, $"{methodName} body was not found.");
|
||||||
|
|
||||||
|
var depth = 0;
|
||||||
|
for (var index = bodyStart; index < source.Length; index++)
|
||||||
|
{
|
||||||
|
if (source[index] == '{')
|
||||||
|
{
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
else if (source[index] == '}')
|
||||||
|
{
|
||||||
|
depth--;
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
return source.Substring(signatureIndex, index - signatureIndex + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException($"{methodName} body was not closed.");
|
||||||
|
}
|
||||||
|
|
||||||
static StoryIntelligenceClient BuildStoryIntelligenceClient(Func<HttpRequestMessage, HttpResponseMessage> respond)
|
static StoryIntelligenceClient BuildStoryIntelligenceClient(Func<HttpRequestMessage, HttpResponseMessage> respond)
|
||||||
=> new(
|
=> new(
|
||||||
new HttpClient(new CaptureImageRequestHandler(respond)),
|
new HttpClient(new CaptureImageRequestHandler(respond)),
|
||||||
|
|||||||
@ -691,24 +691,24 @@ public sealed class OnboardingStoryIntelligenceService(
|
|||||||
SceneAnalysisComplete = pipeline is not null && SceneAnalysisComplete(pipeline),
|
SceneAnalysisComplete = pipeline is not null && SceneAnalysisComplete(pipeline),
|
||||||
CharactersIdentified = characterReview.Candidates.Count + batch.CharacterDecisions.Count,
|
CharactersIdentified = characterReview.Candidates.Count + batch.CharacterDecisions.Count,
|
||||||
CharactersCreatedOrLinked = batch.CharacterDecisions.Count(decision => decision.CreatedOrLinked),
|
CharactersCreatedOrLinked = batch.CharacterDecisions.Count(decision => decision.CreatedOrLinked),
|
||||||
CharacterStageComplete = batch.CharacterStageComplete || characterReview.IsComplete,
|
CharacterStageComplete = characterReview.IsComplete,
|
||||||
LocationsIdentified = locationReview.Candidates.Count + batch.LocationDecisions.Count,
|
LocationsIdentified = locationReview.Candidates.Count + batch.LocationDecisions.Count,
|
||||||
LocationsCreatedOrLinked = batch.LocationDecisions.Count(decision => decision.CreatedOrLinked),
|
LocationsCreatedOrLinked = batch.LocationDecisions.Count(decision => decision.CreatedOrLinked),
|
||||||
LocationStageComplete = batch.LocationStageComplete || locationReview.IsComplete,
|
LocationStageComplete = locationReview.IsComplete,
|
||||||
AssetsIdentified = assetReview.Candidates.Count + batch.AssetDecisions.Count,
|
AssetsIdentified = assetReview.Candidates.Count + batch.AssetDecisions.Count,
|
||||||
AssetsCreatedOrLinked = batch.AssetDecisions.Count(decision => decision.CreatedOrLinked),
|
AssetsCreatedOrLinked = batch.AssetDecisions.Count(decision => decision.CreatedOrLinked),
|
||||||
AssetStageComplete = batch.AssetStageComplete || assetReview.IsComplete,
|
AssetStageComplete = assetReview.IsComplete,
|
||||||
RelationshipsIdentified = relationshipReview.Candidates.Count + batch.RelationshipDecisions.Count,
|
RelationshipsIdentified = relationshipReview.Candidates.Count + batch.RelationshipDecisions.Count,
|
||||||
RelationshipsCreatedOrLinked = batch.RelationshipDecisions.Count(decision => decision.CreatedOrLinked),
|
RelationshipsCreatedOrLinked = batch.RelationshipDecisions.Count(decision => decision.CreatedOrLinked),
|
||||||
RelationshipStageComplete = batch.RelationshipStageComplete || relationshipReview.IsComplete,
|
RelationshipStageComplete = relationshipReview.IsComplete,
|
||||||
KnowledgeIdentified = knowledgeReview.Candidates.Count + batch.KnowledgeDecisions.Count,
|
KnowledgeIdentified = knowledgeReview.Candidates.Count + batch.KnowledgeDecisions.Count,
|
||||||
KnowledgeCreatedOrLinked = batch.KnowledgeDecisions.Count(decision => decision.CreatedOrLinked),
|
KnowledgeCreatedOrLinked = batch.KnowledgeDecisions.Count(decision => decision.CreatedOrLinked),
|
||||||
KnowledgeStageComplete = batch.KnowledgeStageComplete || knowledgeReview.IsComplete,
|
KnowledgeStageComplete = knowledgeReview.IsComplete,
|
||||||
NarrativeCandidatesIdentified = narrative.Candidates.Count + (batch.LastNarrativeImportResult?.TotalReviewed ?? 0),
|
NarrativeCandidatesIdentified = narrative.Candidates.Count + (batch.LastNarrativeImportResult?.TotalReviewed ?? 0),
|
||||||
NarrativeCandidatesApplied = (batch.LastNarrativeImportResult?.PlotLinesCreated ?? 0)
|
NarrativeCandidatesApplied = (batch.LastNarrativeImportResult?.PlotLinesCreated ?? 0)
|
||||||
+ (batch.LastNarrativeImportResult?.PlotLinesMerged ?? 0)
|
+ (batch.LastNarrativeImportResult?.PlotLinesMerged ?? 0)
|
||||||
+ (batch.LastNarrativeImportResult?.ThreadsCreated ?? 0),
|
+ (batch.LastNarrativeImportResult?.ThreadsCreated ?? 0),
|
||||||
NarrativeStageComplete = batch.NarrativeStageComplete || narrative.IsComplete
|
NarrativeStageComplete = narrative.IsComplete
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1057,39 +1057,12 @@ public sealed class OnboardingStoryIntelligenceService(
|
|||||||
or StoryIntelligencePipelineStages.NarrativeReview
|
or StoryIntelligencePipelineStages.NarrativeReview
|
||||||
or StoryIntelligencePipelineStages.NarrativeImport
|
or StoryIntelligencePipelineStages.NarrativeImport
|
||||||
or StoryIntelligencePipelineStages.Complete;
|
or StoryIntelligencePipelineStages.Complete;
|
||||||
var locationStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.LocationImport)
|
|
||||||
|| state.CurrentStage is StoryIntelligencePipelineStages.AssetReview
|
|
||||||
or StoryIntelligencePipelineStages.AssetImport
|
|
||||||
or StoryIntelligencePipelineStages.RelationshipReview
|
|
||||||
or StoryIntelligencePipelineStages.RelationshipImport
|
|
||||||
or StoryIntelligencePipelineStages.KnowledgeReview
|
|
||||||
or StoryIntelligencePipelineStages.KnowledgeImport
|
|
||||||
or StoryIntelligencePipelineStages.NarrativeReview
|
|
||||||
or StoryIntelligencePipelineStages.NarrativeImport
|
|
||||||
or StoryIntelligencePipelineStages.Complete;
|
|
||||||
var assetStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.AssetImport)
|
|
||||||
|| state.CurrentStage is StoryIntelligencePipelineStages.RelationshipReview
|
|
||||||
or StoryIntelligencePipelineStages.RelationshipImport
|
|
||||||
or StoryIntelligencePipelineStages.KnowledgeReview
|
|
||||||
or StoryIntelligencePipelineStages.KnowledgeImport
|
|
||||||
or StoryIntelligencePipelineStages.NarrativeReview
|
|
||||||
or StoryIntelligencePipelineStages.NarrativeImport
|
|
||||||
or StoryIntelligencePipelineStages.Complete;
|
|
||||||
var relationshipStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.RelationshipImport)
|
|
||||||
|| state.CurrentStage is StoryIntelligencePipelineStages.KnowledgeReview
|
|
||||||
or StoryIntelligencePipelineStages.KnowledgeImport
|
|
||||||
or StoryIntelligencePipelineStages.NarrativeReview
|
|
||||||
or StoryIntelligencePipelineStages.NarrativeImport
|
|
||||||
or StoryIntelligencePipelineStages.Complete;
|
|
||||||
var knowledgeStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.KnowledgeImport);
|
|
||||||
var narrativeStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.NarrativeImport);
|
|
||||||
|
|
||||||
batch.CharacterStageComplete = characterStageComplete;
|
batch.CharacterStageComplete = characterStageComplete;
|
||||||
batch.LocationStageComplete = locationStageComplete;
|
batch.LocationStageComplete = false;
|
||||||
batch.AssetStageComplete = assetStageComplete;
|
batch.AssetStageComplete = false;
|
||||||
batch.RelationshipStageComplete = relationshipStageComplete;
|
batch.RelationshipStageComplete = false;
|
||||||
batch.KnowledgeStageComplete = knowledgeStageComplete;
|
batch.KnowledgeStageComplete = false;
|
||||||
batch.NarrativeStageComplete = narrativeStageComplete;
|
batch.NarrativeStageComplete = false;
|
||||||
|
|
||||||
await batchStore.SaveAsync(batch);
|
await batchStore.SaveAsync(batch);
|
||||||
|
|
||||||
|
|||||||
@ -575,7 +575,12 @@ public sealed class StoryIntelligenceRelationshipImportService(
|
|||||||
return new("Family", 420);
|
return new("Family", 420);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContainsAny(allText, "rival", "enemy", "adversarial", "conflict", "mistrust", "antagonist", "competitor", "confront", "attack", "attacks", "assault", "assaults", "insult", "anger", "hostile"))
|
if (ContainsAny(allText,
|
||||||
|
"rival", "enemy", "adversarial", "conflict", "mistrust", "distrust", "antagonist", "competitor",
|
||||||
|
"confront", "attack", "attacks", "attacked", "assault", "assaults", "assaulted", "slap", "slaps",
|
||||||
|
"slapped", "threat", "threaten", "threatens", "threatened", "violence", "never speak", "disgust",
|
||||||
|
"betray", "betrays", "betrayed", "spiked", "drugged", "grope", "gropes", "groped", "force",
|
||||||
|
"forces", "forced", "insult", "insults", "anger", "hostile"))
|
||||||
{
|
{
|
||||||
return new("Rival", 370);
|
return new("Rival", 370);
|
||||||
}
|
}
|
||||||
@ -590,7 +595,7 @@ public sealed class StoryIntelligenceRelationshipImportService(
|
|||||||
return new("Educational", 350);
|
return new("Educational", 350);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContainsAny(allText, "police", "detective", "suspect", "witness", "authority", "guard", "prisoner", "investigator", "person of interest"))
|
if (ContainsAny(explicitText, "police", "detective", "suspect", "witness", "guard", "prisoner", "investigator", "person of interest", "social worker", "social services"))
|
||||||
{
|
{
|
||||||
return new("Authority", 340);
|
return new("Authority", 340);
|
||||||
}
|
}
|
||||||
@ -610,7 +615,7 @@ public sealed class StoryIntelligenceRelationshipImportService(
|
|||||||
return new("Neighbour", 310);
|
return new("Neighbour", 310);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContainsAny(allText, "colleague", "coworker", "co worker", "boss", "employee", "manager", "client", "professional", "work relationship"))
|
if (ContainsAny(explicitText, "colleague", "coworker", "co worker", "co-worker", "boss", "employee", "manager", "client", "work relationship"))
|
||||||
{
|
{
|
||||||
return new("Professional", 300);
|
return new("Professional", 300);
|
||||||
}
|
}
|
||||||
@ -625,7 +630,7 @@ public sealed class StoryIntelligenceRelationshipImportService(
|
|||||||
return new("Friend", 270);
|
return new("Friend", 270);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ContainsAny(allText, "ally", "protector", "supporter", "confidant", "protects", "supports"))
|
if (ContainsAny(explicitText, "ally", "protector", "supporter", "confidant", "protects", "supports"))
|
||||||
{
|
{
|
||||||
return new("Ally", 250);
|
return new("Ally", 250);
|
||||||
}
|
}
|
||||||
@ -661,6 +666,8 @@ public sealed class StoryIntelligenceRelationshipImportService(
|
|||||||
{
|
{
|
||||||
var normalised = NormaliseForMatching(evidence);
|
var normalised = NormaliseForMatching(evidence);
|
||||||
return ContainsAny(normalised, "my mother", "my mum", "my mom", "my father", "my dad", "me and mum", "me and mother", "me and dad", "me and father")
|
return ContainsAny(normalised, "my mother", "my mum", "my mom", "my father", "my dad", "me and mum", "me and mother", "me and dad", "me and father")
|
||||||
|
|| HasNamedParentEvidence(normalised, characterAName, characterBName)
|
||||||
|
|| HasNamedParentEvidence(normalised, characterBName, characterAName)
|
||||||
|| HasOwnedKinshipBetweenParticipants("mother", characterAName, characterBName)
|
|| HasOwnedKinshipBetweenParticipants("mother", characterAName, characterBName)
|
||||||
|| HasOwnedKinshipBetweenParticipants("father", characterAName, characterBName)
|
|| HasOwnedKinshipBetweenParticipants("father", characterAName, characterBName)
|
||||||
|| HasOwnedKinshipBetweenParticipants("mum", characterAName, characterBName)
|
|| HasOwnedKinshipBetweenParticipants("mum", characterAName, characterBName)
|
||||||
@ -668,6 +675,24 @@ public sealed class StoryIntelligenceRelationshipImportService(
|
|||||||
|| HasOwnedKinshipBetweenParticipants("dad", characterAName, characterBName);
|
|| HasOwnedKinshipBetweenParticipants("dad", characterAName, characterBName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool HasNamedParentEvidence(string normalisedEvidence, string? possibleParentName, string? possibleChildName)
|
||||||
|
{
|
||||||
|
var parent = Clean(possibleParentName);
|
||||||
|
var child = Clean(possibleChildName);
|
||||||
|
if (string.IsNullOrWhiteSpace(parent) || string.IsNullOrWhiteSpace(child))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContainsAny(
|
||||||
|
normalisedEvidence,
|
||||||
|
$"{child}'s mother {parent}",
|
||||||
|
$"{child}'s mum {parent}",
|
||||||
|
$"{child}'s mom {parent}",
|
||||||
|
$"{child}'s father {parent}",
|
||||||
|
$"{child}'s dad {parent}");
|
||||||
|
}
|
||||||
|
|
||||||
private static bool HasOwnedKinshipBetweenParticipants(string kinship, string? characterAName, string? characterBName)
|
private static bool HasOwnedKinshipBetweenParticipants(string kinship, string? characterAName, string? characterBName)
|
||||||
=> IsOwnedKinshipFor(characterAName, characterBName, kinship)
|
=> IsOwnedKinshipFor(characterAName, characterBName, kinship)
|
||||||
|| IsOwnedKinshipFor(characterBName, characterAName, kinship);
|
|| IsOwnedKinshipFor(characterBName, characterAName, kinship);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user