Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,664 changes: 832 additions & 832 deletions Cards/Fallacies/Argumentum Fallacies - Taxonomy.csv

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,53 @@ public async Task ProdValidator_InspectsConceptsAndGenuinelyPassesOnLoadedOntolo
"the mappings were inspected and present, not because the concept list was empty");
}

// ─────────────────────────────────────────────────────────────────────────────
// (5) NEW LAYER — transverse cross-links + AIF attack typing (CSV→OWL wiring added with this
// change). Proves the second-pass emitter in OwlGeneratorConfig.CreateOwlDocument actually
// writes crossLink object-property assertions (predatesOn/denounces/leverages/allows/
// opposes/inverts/mirrors/isRelatedTo) resolved from the crossLink_* CSV columns, plus the
// aifAttackType literal + aifAttackedNode (RA/I/CA-node) resource for each AIF-typed leaf —
// and that, like the SKOS *Match resource-valued annotations in test 3, they SURVIVE the
// OWL2XML round-trip. Regression guard: if the wiring is dropped (or the committed ontology
// is not regenerated after the crosslink/AIF data is applied to the CSV), these counts fall
// to 0. Loads the same committed docs/ontology/argumentum.owl as the tests above.
// ─────────────────────────────────────────────────────────────────────────────

[Fact]
public void LoadedOntology_ContainsCrossLinkAndAifAttackAssertions()
{
int crossLinks =
CountAnnotations("#predatesOn")
+ CountAnnotations("#denounces")
+ CountAnnotations("#leverages")
+ CountAnnotations("#allows")
+ CountAnnotations("#opposes")
+ CountAnnotations("#inverts")
+ CountAnnotations("#mirrors")
+ CountAnnotations("#isRelatedTo");

crossLinks.Should().BeGreaterThan(0,
"the second-pass emitter writes transverse crossLink object-property annotation assertions " +
"resolved from the 8 crossLink_* CSV columns; like the resource-valued SKOS *Match annotations " +
"they survive the OWL2XML round-trip. Count 0 = the CSV→OWL crosslink wiring was removed, or the " +
"committed ontology was not regenerated after the crosslink data was applied to the taxonomy CSV.");

CountAnnotations("#isRelatedTo").Should().BeGreaterThan(0,
"isRelatedTo is the verb of the manually-seeded transverse links (e.g. PK3 '1.1.1' → '7.1.2.3'), " +
"so at minimum the pre-existing seeds must be emitted.");

int aifAttackType = CountAnnotations("#aifAttackType");
int aifAttackedNode = CountAnnotations("#aifAttackedNode");

aifAttackType.Should().BeGreaterThan(0,
"the emitter writes an aifAttackType literal (undercut/undermine/rebut) for every fallacy carrying " +
"AIF_attackType (the typed leaves). Count 0 = the AIF-attack wiring was removed or the ontology was " +
"not regenerated.");
aifAttackedNode.Should().BeGreaterThan(0,
"each AIF-typed fallacy also links to its AIF node (RA-node/I-node/CA-node) via aifAttackedNode — " +
"the formal ASPIC+/AIF attachment (undercut→RA, undermine→I, rebut→CA).");
}

private static object BuildProdValidator(OwlAdapter ontology)
{
// OwlOntologyValidationTests is a PUBLIC class in the PRODUCTION project (namespace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,21 @@ public class Fallacy : CsvBase<Fallacy, FallacyClassMap>, IMindMapItem, ICsvBase

public string AIFSkosMappingType { get; set; }

// Transverse relational layer (8 typed cross-links). Cell value = ';'-joined
// dotted target path(s) resolved against the taxonomy hierarchy.
public string CrossLinkPredatesOn { get; set; }
public string CrossLinkDenounces { get; set; }
public string CrossLinkLeverages { get; set; }
public string CrossLinkAllows { get; set; }
public string CrossLinkOpposes { get; set; }
public string CrossLinkInverts { get; set; }
public string CrossLinkMirrors { get; set; }
public string CrossLinkIsRelatedTo { get; set; }

// AIF/ASPIC+ attack typing (#498/#707§4): undercut→RA-node, undermine→I-node, rebut→CA-node.
public string AIFAttackType { get; set; }
public string AIFAttackedNode { get; set; }


public int? PrintAndPlay { get; set; }

Expand Down Expand Up @@ -281,6 +296,16 @@ public FallacyClassMap()
Map(m => m.AIFSkosExceptionRef).Name("AIF_skosExceptionRef").Optional();
Map(m => m.AIFSkosOther).Name("AIF_skosOther").Optional();
Map(m => m.AIFSkosMappingType).Name("AIF_skosMappingType").Optional();
Map(m => m.CrossLinkPredatesOn).Name("crossLink_PredatesOn").Optional();
Map(m => m.CrossLinkDenounces).Name("crossLink_Denounces").Optional();
Map(m => m.CrossLinkLeverages).Name("crossLink_Leverages").Optional();
Map(m => m.CrossLinkAllows).Name("crossLink_Allows").Optional();
Map(m => m.CrossLinkOpposes).Name("crossLink_Opposes").Optional();
Map(m => m.CrossLinkInverts).Name("crossLink_Inverts").Optional();
Map(m => m.CrossLinkMirrors).Name("crossLink_Mirrors").Optional();
Map(m => m.CrossLinkIsRelatedTo).Name("crossLink_IsRelatedTo").Optional();
Map(m => m.AIFAttackType).Name("AIF_attackType").Optional();
Map(m => m.AIFAttackedNode).Name("AIF_attackedNode").Optional();
Map(m => m.PrintAndPlay).Name("print_and_play").Optional();
Map(m => m.DécimalPathPadded).Name("decimal_path_padded");
Map(m => m.DepthMax4).Name("depth_max4");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,70 @@ Fallacy GetParent(Fallacy f)
}
}


// ── Transverse cross-links + AIF attack typing (2nd pass: concepts is now fully populated) ──
var crossLinkVerbs = new (string Predicate, Func<Fallacy, string> Get, bool Symmetric)[]
{
("predatesOn", f => f.CrossLinkPredatesOn, false),
("denounces", f => f.CrossLinkDenounces, false),
("leverages", f => f.CrossLinkLeverages, false),
("allows", f => f.CrossLinkAllows, false),
("opposes", f => f.CrossLinkOpposes, true),
("inverts", f => f.CrossLinkInverts, true),
("mirrors", f => f.CrossLinkMirrors, true),
("isRelatedTo", f => f.CrossLinkIsRelatedTo, true),
};

var crossLinkProps = new Dictionary<string, RDFResource>();
foreach (var v in crossLinkVerbs)
{
var prop = new RDFResource($"{OntologyNamespace}{v.Predicate}");
ontology.DeclareObjectProperty(prop);
crossLinkProps[v.Predicate] = prop;
}

var aifAttackTypeProp = new RDFResource($"{OntologyNamespace}aifAttackType");
var aifAttackedNodeProp = new RDFResource($"{OntologyNamespace}aifAttackedNode");
ontology.DeclareObjectProperty(aifAttackedNodeProp);
var aifNodeResources = new Dictionary<string, RDFResource>();
RDFResource AifNode(string nodeName)
{
if (!aifNodeResources.TryGetValue(nodeName, out var res))
{
res = new RDFResource($"{ExternalReferenceOntologyNamespaceURI}{nodeName}");
ontology.DeclareClass(res);
aifNodeResources[nodeName] = res;
}
return res;
}

foreach (var fallacy in fallacies)
{
var sourceConcept = concepts[fallacy];

foreach (var v in crossLinkVerbs)
{
var raw = v.Get(fallacy);
if (string.IsNullOrWhiteSpace(raw)) continue;
foreach (var targetPath in raw.Split(';').Select(x => x.Trim()).Where(x => x.Length > 0))
{
if (!fallaciesByPath.TryGetValue(targetPath, out var targetFallacy)) continue;
if (targetFallacy == fallacy) continue;
var targetConcept = concepts[targetFallacy];
ontology.AnnotateConceptWithResource(sourceConcept, crossLinkProps[v.Predicate], targetConcept);
if (v.Symmetric)
ontology.AnnotateConceptWithResource(targetConcept, crossLinkProps[v.Predicate], sourceConcept);
}
}

if (!string.IsNullOrWhiteSpace(fallacy.AIFAttackType))
{
ontology.AnnotateConcept(sourceConcept, aifAttackTypeProp, new RDFPlainLiteral(fallacy.AIFAttackType.Trim()));
if (!string.IsNullOrWhiteSpace(fallacy.AIFAttackedNode))
ontology.AnnotateConceptWithResource(sourceConcept, aifAttackedNodeProp, AifNode(fallacy.AIFAttackedNode.Trim()));
}
}

//Saving
await ontology.ToFileAsync(OWLEnums.OWLFormats.OWL2XML, fileName);
Logger.LogSuccess($"Owl document {fileName} successfully saved");
Expand Down
17 changes: 17 additions & 0 deletions Generation/Converters/Argumentum.AssetConverter/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,23 @@ static async Task Main(string[] args)
await cmykConfig.Apply().ConfigureAwait(false);
return;
}
else if (args[0].Equals("--generate-owl", StringComparison.OrdinalIgnoreCase))
{
// Regenerate ONLY the OWL ontologies (Fallacies + Virtues) from the current CSV
// datasets: no harvest, no PDF, no mindmap. Writes to <Target>/<lang>/Ontology/.
// This is the reproducible way to refresh the committed docs/ontology/argumentum.owl
// after taxonomy edits (e.g. the crossLink_* / AIF_attack* relational layers).
// Mirrors --pdf-cmyk: a single-stage entry point replacing the default harvest+PDF Mode.
Logger.LogTitle("Mode génération OWL (ontologies Fallacies + Virtues)");

var owlGenConfigFileName = Path.Combine(Environment.CurrentDirectory, "AssetConverterConfig.json");
var owlGenConfig = AssetConverterConfig.GetConfig(owlGenConfigFileName, out var _);

owlGenConfig.Mode = ConverterMode.OwlGenerator;

await owlGenConfig.Apply().ConfigureAwait(false);
return;
}
}

var configFileName = Path.Combine(Environment.CurrentDirectory, "AssetConverterConfig.json");
Expand Down
Loading
Loading