Merge branch 'migration/tbox-v2.1'
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Pernod Ricard Data MetaModel — T-Box Rulebook
|
# Pernod Ricard Data MetaModel — T-Box Rulebook
|
||||||
|
|
||||||
**Version 1.2** — Draft for review — generated 2026-08-03
|
**Version 1.4** — Draft for review — generated 2026-08-03
|
||||||
|
|
||||||
> Generated from `rules.yaml`. Do not edit this document: edit the source and regenerate (EV-007, EV-008).
|
> Generated from `rules.yaml`. Do not edit this document: edit the source and regenerate (EV-007, EV-008).
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ The IRI is identity. It is immutable in the RDF sense — changing one is a migr
|
|||||||
|
|
||||||
## TN-009 — A relation begins with a verb
|
## TN-009 — A relation begins with a verb
|
||||||
|
|
||||||
**Statement.** The local name of an object property begins with a verb, in lower case. Both the active form and the passive or participial form are admitted: a past participle is a verb in first position. No relation begins with a preposition or with a noun.
|
**Statement.** The local name of an object property begins with a verb, in lower case. Both the active form and the passive or participial form are admitted: a past participle is a verb in first position. An adverb qualifying that verb is transparent and may precede it. No relation begins with a preposition or with a noun.
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -198,12 +198,12 @@ The IRI is identity. It is immutable in the RDF sense — changing one is a migr
|
|||||||
| Executor | `check_tbox_naming.py` |
|
| Executor | `check_tbox_naming.py` |
|
||||||
| Procedure | _pending_ |
|
| Procedure | _pending_ |
|
||||||
|
|
||||||
**Why.** A relation reads as a verb and an attribute reads as a noun. The distinction is what lets a reader tell an edge from a field without opening the declaration. The explicit clause on participial forms is required: read literally, a verb-first rule would condemn a whole family of sound relations such as computedBy, storedIn and derivedFrom.
|
**Why.** A relation reads as a verb and an attribute reads as a noun. The distinction is what lets a reader tell an edge from a field without opening the declaration. Two clauses are stated rather than left implicit, because a literal reading would condemn sound relations: the participial one, which covers a whole family such as computedBy, storedIn and derivedFrom, and the adverbial one, which covers a relation whose verb is qualified rather than replaced.
|
||||||
|
|
||||||
| From | To | Note |
|
| From | To | Note |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| pr:inDatabase | pr:isInDatabase | a preposition is not a verb |
|
| pr:inDatabase | pr:isInDatabase | a preposition is not a verb |
|
||||||
| pr:primaryLocation | pr:primarilyStoredIn | a noun is not a verb |
|
| pr:primaryLocation | pr:primarilyStoredIn | a noun is not a verb; the adverb is transparent and the verb follows |
|
||||||
| pr:computedBy | pr:computedBy | participial form is conforming |
|
| pr:computedBy | pr:computedBy | participial form is conforming |
|
||||||
|
|
||||||
## TN-010 — The is prefix is either a copula or a predicate
|
## TN-010 — The is prefix is either a copula or a predicate
|
||||||
@@ -507,7 +507,7 @@ Nothing is left to be guessed. What a term is, what it applies to, where it sits
|
|||||||
|
|
||||||
## TN-025 — Every property declares its domain and range
|
## TN-025 — Every property declares its domain and range
|
||||||
|
|
||||||
**Statement.** rdfs:domain and rdfs:range are mandatory on every property, EXCEPT where the property is deliberately polymorphic. A polymorphic property states so in its comment and has its scope declared in SHACL. Silent absence is a violation; documented absence is not.
|
**Statement.** rdfs:domain and rdfs:range are mandatory on every property, EXCEPT where the property is deliberately polymorphic. A polymorphic property declares pr:polymorphic true and has its scope declared in SHACL. Silent absence is a violation; declared absence is not.
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
@@ -517,30 +517,31 @@ Nothing is left to be guessed. What a term is, what it applies to, where it sits
|
|||||||
| Executor | `check_tbox_naming.py` |
|
| Executor | `check_tbox_naming.py` |
|
||||||
| Procedure | _pending_ |
|
| Procedure | _pending_ |
|
||||||
|
|
||||||
**Why.** A property with no domain cannot be targeted by any control. But some properties are polymorphic by design, their scope controlled class by class in SHACL rather than by twin properties; giving those an rdfs:domain would trigger the RDFS retyping described in TN-028. The rule therefore separates the two cases rather than demanding a domain everywhere.
|
**Why.** A property with no domain cannot be targeted by any control. But some properties are polymorphic by design, their scope controlled class by class in SHACL rather than by twin properties; giving those an rdfs:domain would trigger the RDFS retyping described in TN-028. The rule therefore separates the two cases rather than demanding a domain everywhere. The distinction is carried by an axiom and not by a sentence in the comment: a checker looking for a phrase in prose fails the moment someone words it differently, and what matters is declared rather than read, exactly as for abstractness.
|
||||||
|
|
||||||
| From | To | Note |
|
| From | To | Note |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| a polymorphic property with no domain and no comment | violation | silence is indistinguishable from omission |
|
| a property with no domain and no declaration | violation | silence is indistinguishable from omission |
|
||||||
| a polymorphic property with no domain | conforming | scope declared in SHACL |
|
| a property with no domain, declaring pr:polymorphic true | conforming | scope declared in SHACL |
|
||||||
|
|
||||||
## TN-026 — Every term declares how it was authored
|
## TN-026 — Every concrete class declares how its instances are produced
|
||||||
|
|
||||||
**Statement.** pr:authoringMode is mandatory on every term. pr:harvestSource is mandatory if and only if the mode is HARVESTED.
|
**Statement.** Every CONCRETE CLASS declares pr:authoringMode, stating whether its instances are ASSERTED by governance or HARVESTED from a system. pr:harvestSource is mandatory if and only if the mode is HARVESTED. The declared mode agrees with the provenance axis the class descends from: a class under CapturedObject declares HARVESTED, one under DefinedObject declares ASSERTED. Nothing else carries the property: not an abstract class, not a property, not a term of the vocabulary in its own right.
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Severity | **BLOCKING** |
|
| Severity | **BLOCKING** |
|
||||||
| Applies to | class, object_property, datatype_property, annotation_property |
|
| Applies to | class |
|
||||||
| Enforced at | script |
|
| Enforced at | script |
|
||||||
| Executor | `check_tbox_naming.py` |
|
| Executor | `check_tbox_naming.py` |
|
||||||
| Procedure | _pending_ |
|
| Procedure | _pending_ |
|
||||||
|
|
||||||
**Why.** Provenance decides who may edit a term and what a divergence means. A term stating where its data comes from without stating that it is harvested, or declaring itself harvested without naming a source, is half-declared in a way no control can catch. Declared symmetrically, provenance also makes a harvester specifiable from the model itself rather than from a side document.
|
**Why.** A term of the vocabulary is declared, always and by definition; recording that on every term would repeat one fact a hundred times and say nothing. It is the INSTANCES that have a provenance, and the class is where the model states which one: a governance object is asserted by a domain, a physical column is harvested from a system. The distinction matters because a class can be declared and describe captured things at once — CapturedObject itself is a declared term naming harvested instances, and conflating the two is what once put a harvesting mode on an abstraction. Nothing is inherited. A subclass declares its own mode rather than taking its parent's, so that the declaration is read where the instances are typed and not chased up a chain. The agreement clause is what keeps that free of contradiction: the axis says what kind of thing the class describes, the mode says how those things arrive, and a class under CapturedObject declaring ASSERTED is stating both at once. Declared this way, provenance also makes a harvester specifiable from the model itself rather than from a side document.
|
||||||
|
|
||||||
| From | To | Note |
|
| From | To | Note |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| harvestSource present | authoringMode HARVESTED | |
|
| authoringMode on an abstract class | nothing | an abstraction has no instances to produce |
|
||||||
|
| a class under CapturedObject declaring ASSERTED | violation | the mode contradicts the axis |
|
||||||
| authoringMode HARVESTED, harvestSource absent | harvestSource declared | |
|
| authoringMode HARVESTED, harvestSource absent | harvestSource declared | |
|
||||||
|
|
||||||
## TN-027 — A concrete class has one layer and one provenance
|
## TN-027 — A concrete class has one layer and one provenance
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,488 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Enforce the naming and declaration rules of the T-Box rulebook.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
python3 check_tbox_naming.py [--ontology PATH] [--json PATH] [--quiet]
|
||||||
|
|
||||||
|
Named as the executor of 25 rules. EV-011 forbids declaring a rule blocking
|
||||||
|
without one, so this script is what makes those declarations true.
|
||||||
|
|
||||||
|
THREE VERDICTS
|
||||||
|
VIOLATION the rule is settled mechanically and is broken -> exit 1
|
||||||
|
REVIEW the checker cannot conclude; a person must -> exit 0
|
||||||
|
PASS conforming
|
||||||
|
|
||||||
|
The middle verdict is the point. Without it the choice would be between
|
||||||
|
blocking on false positives and staying silent on real problems. What the
|
||||||
|
checker cannot decide alone comes from naming_lexicon.yaml; a word absent from
|
||||||
|
the lexicon produces a REVIEW, never a VIOLATION.
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from rdflib import Graph, Literal, Namespace, RDF, RDFS, OWL, URIRef, XSD
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
RULES = os.path.join(HERE, "rules.yaml")
|
||||||
|
LEXICON = os.path.join(HERE, "naming_lexicon.yaml")
|
||||||
|
DEFAULT_ONTOLOGY = os.path.normpath(os.path.join(HERE, "..", "ontology", "pr_metamodel.ttl"))
|
||||||
|
|
||||||
|
NS = "https://ontology.pernod-ricard.com/metamodel/"
|
||||||
|
PR = Namespace(NS)
|
||||||
|
|
||||||
|
VIOLATION, REVIEW = "VIOLATION", "REVIEW"
|
||||||
|
|
||||||
|
KINDS = OrderedDict([
|
||||||
|
(OWL.Class, "class"),
|
||||||
|
(OWL.ObjectProperty, "relation"),
|
||||||
|
(OWL.DatatypeProperty, "attribute"),
|
||||||
|
(OWL.AnnotationProperty, "annotation"),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------- findings
|
||||||
|
|
||||||
|
class Findings(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.items = []
|
||||||
|
|
||||||
|
def add(self, verdict, rule, term, message):
|
||||||
|
self.items.append({"verdict": verdict, "rule": rule,
|
||||||
|
"term": term, "message": message})
|
||||||
|
|
||||||
|
def violation(self, rule, term, message):
|
||||||
|
self.add(VIOLATION, rule, term, message)
|
||||||
|
|
||||||
|
def review(self, rule, term, message):
|
||||||
|
self.add(REVIEW, rule, term, message)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def violations(self):
|
||||||
|
return [i for i in self.items if i["verdict"] == VIOLATION]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reviews(self):
|
||||||
|
return [i for i in self.items if i["verdict"] == REVIEW]
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- helpers
|
||||||
|
|
||||||
|
def local(uri):
|
||||||
|
s = str(uri)
|
||||||
|
return s[len(NS):] if s.startswith(NS) else None
|
||||||
|
|
||||||
|
|
||||||
|
def tokens(name):
|
||||||
|
"""Split a CamelCase local name. A run of capitals is one token: an acronym."""
|
||||||
|
return re.findall(r"[A-Z]+(?![a-z])|[A-Z][a-z0-9]*|^[a-z0-9]+|[a-z0-9]+", name)
|
||||||
|
|
||||||
|
|
||||||
|
def decamelise(name, is_class):
|
||||||
|
"""TN-018. A run of capitals is kept together: it carries an acronym."""
|
||||||
|
spaced = re.sub(r"(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])", " ", name)
|
||||||
|
return spaced if is_class else spaced[0].lower() + spaced[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def acronyms_in(name, lex):
|
||||||
|
"""Runs of two or more capitals, plus any known short form appearing whole.
|
||||||
|
|
||||||
|
The negative lookahead matters: without it, BIField yields BIF rather than
|
||||||
|
BI, because the run swallows the initial capital of the word that follows.
|
||||||
|
"""
|
||||||
|
found = set(re.findall(r"[A-Z]{2,}(?![a-z])", name))
|
||||||
|
for short in lex["known_acronyms"]:
|
||||||
|
if re.search(r"(?:^|(?<=[a-z]))%s(?![a-z])" % re.escape(short), name):
|
||||||
|
found.add(short)
|
||||||
|
return sorted(found)
|
||||||
|
|
||||||
|
|
||||||
|
def strip_case(text):
|
||||||
|
return re.sub(r"[^a-z0-9]", "", (text or "").lower())
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------- checks
|
||||||
|
|
||||||
|
def provenance_axis(g, term):
|
||||||
|
"""The provenance axis a class descends from, or None."""
|
||||||
|
seen, stack = set(), [term]
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
for parent in g.objects(node, RDFS.subClassOf):
|
||||||
|
if isinstance(parent, URIRef) and parent not in seen:
|
||||||
|
seen.add(parent)
|
||||||
|
stack.append(parent)
|
||||||
|
if PR.CapturedObject in seen:
|
||||||
|
return "CapturedObject"
|
||||||
|
if PR.DefinedObject in seen:
|
||||||
|
return "DefinedObject"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_identifier(g, terms, lex, f):
|
||||||
|
"""TN-001 to TN-015 — the form of the IRI."""
|
||||||
|
short_labels = {t: str(v) for t in terms
|
||||||
|
for v in g.objects(t, PR.shortLabel)}
|
||||||
|
allowed_acronyms = set()
|
||||||
|
for value in short_labels.values():
|
||||||
|
allowed_acronyms |= set(re.findall(r"[A-Z]{2,}", value))
|
||||||
|
|
||||||
|
for term, kind in terms.items():
|
||||||
|
name = local(term)
|
||||||
|
if name is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TN-001 — CamelCase, no separator, correct initial
|
||||||
|
if re.search(r"[^A-Za-z0-9]", name):
|
||||||
|
f.violation("TN-001", name, "the local name carries a separator")
|
||||||
|
expect_upper = kind in ("class", "individual")
|
||||||
|
if name and name[0].isupper() != expect_upper:
|
||||||
|
f.violation("TN-001", name,
|
||||||
|
"a %s starts with %s case" %
|
||||||
|
(kind, "upper" if expect_upper else "lower"))
|
||||||
|
|
||||||
|
# TN-004 — no digit
|
||||||
|
if any(c.isdigit() for c in name):
|
||||||
|
f.violation("TN-004", name, "a digit belongs to a value, not an identity")
|
||||||
|
|
||||||
|
# TN-002 — no acronym, except one reproduced from a target short label
|
||||||
|
for a in acronyms_in(name, lex):
|
||||||
|
if kind == "relation" and a in allowed_acronyms:
|
||||||
|
continue # TN-011 exception
|
||||||
|
expansion = lex["known_acronyms"].get(a)
|
||||||
|
f.violation("TN-002", name, "carries the acronym %s%s"
|
||||||
|
% (a, " (%s)" % expansion if expansion else ""))
|
||||||
|
|
||||||
|
if kind == "class":
|
||||||
|
check_class_name(name, lex, f)
|
||||||
|
elif kind == "relation":
|
||||||
|
check_relation_name(g, term, name, lex, f)
|
||||||
|
elif kind == "attribute":
|
||||||
|
check_attribute_name(g, term, name, lex, f)
|
||||||
|
|
||||||
|
|
||||||
|
def check_class_name(name, lex, f):
|
||||||
|
"""TN-005 — a singular common noun, no type word."""
|
||||||
|
if name in lex["type_word_exceptions"]:
|
||||||
|
return
|
||||||
|
for w in lex["type_words"]:
|
||||||
|
if re.search(r"(?:^|(?<=[a-z]))%s(?![a-z])" % w, name):
|
||||||
|
f.violation("TN-005", name,
|
||||||
|
"carries the type word %s and is not a recorded abstraction" % w)
|
||||||
|
last = tokens(name)[-1] if tokens(name) else name
|
||||||
|
if last.endswith("s") and last not in lex["not_plural"]:
|
||||||
|
f.review("TN-005", name, "%s looks plural; a class name is singular" % last)
|
||||||
|
|
||||||
|
|
||||||
|
def check_relation_name(g, term, name, lex, f):
|
||||||
|
"""TN-009, TN-010, TN-011 — verb first, is disambiguated, target named whole."""
|
||||||
|
toks = tokens(name)
|
||||||
|
head = toks[0] if toks else name
|
||||||
|
|
||||||
|
# TN-009 — an adverb is transparent, the verb follows
|
||||||
|
if head in lex["adverbs"]:
|
||||||
|
toks = toks[1:]
|
||||||
|
head = toks[0].lower() if toks else head
|
||||||
|
head = head.lower()
|
||||||
|
|
||||||
|
if head in lex["copula"]:
|
||||||
|
pass # TN-010, copula use
|
||||||
|
elif head in lex["active_verbs"] or head in lex["participles"]:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
f.review("TN-009", name,
|
||||||
|
"%s is not a verb known to the lexicon; add it there or rename" % head)
|
||||||
|
|
||||||
|
# TN-011 — if the target class is named, it is named by its short label whole
|
||||||
|
for rng in g.objects(term, RDFS.range):
|
||||||
|
target = local(rng)
|
||||||
|
if not target:
|
||||||
|
continue
|
||||||
|
short = next((str(v) for v in g.objects(rng, PR.shortLabel)), None)
|
||||||
|
reference = strip_case(short) if short else strip_case(target)
|
||||||
|
tail = strip_case("".join(toks[1:]))
|
||||||
|
if not tail or tail == reference:
|
||||||
|
continue
|
||||||
|
if reference.endswith(tail) or tail in reference:
|
||||||
|
f.violation("TN-011", name,
|
||||||
|
"names its target by a truncation; %s expects %s"
|
||||||
|
% (target, short or target))
|
||||||
|
|
||||||
|
|
||||||
|
def check_attribute_name(g, term, name, lex, f):
|
||||||
|
"""TN-012 to TN-015 — a noun, no relational suffix, booleans predicative."""
|
||||||
|
ranges = [str(r) for r in g.objects(term, RDFS.range)]
|
||||||
|
is_boolean = str(XSD.boolean) in ranges
|
||||||
|
is_date = any(r in lex["date_ranges"] for r in ranges)
|
||||||
|
toks = tokens(name)
|
||||||
|
head = (toks[0] if toks else name).lower()
|
||||||
|
|
||||||
|
if is_boolean:
|
||||||
|
if head not in lex["copula"]:
|
||||||
|
f.violation("TN-015", name, "a boolean attribute is predicative and begins with is")
|
||||||
|
return
|
||||||
|
|
||||||
|
# TN-012 — a noun, and in particular no has
|
||||||
|
if head == "has":
|
||||||
|
f.violation("TN-012", name, "an attribute is a noun; the domain already says who has it")
|
||||||
|
elif head in lex["active_verbs"] or head in lex["copula"]:
|
||||||
|
f.violation("TN-012", name, "%s is a verb; an attribute is a noun" % head)
|
||||||
|
elif head in lex["participles"]:
|
||||||
|
f.review("TN-012", name, "%s is a participle; confirm this reads as a noun" % head)
|
||||||
|
|
||||||
|
# TN-014 — relational suffixes are reserved for relations
|
||||||
|
last = toks[-1] if toks else name
|
||||||
|
if last in lex["relational_suffixes"]:
|
||||||
|
f.violation("TN-014", name,
|
||||||
|
"ends in %s, a form reserved for object properties" % last)
|
||||||
|
|
||||||
|
# TN-013 — a date attribute is nominal
|
||||||
|
if is_date and (last in lex["relational_suffixes"] or head in lex["participles"]):
|
||||||
|
f.violation("TN-013", name, "a date attribute is a noun, not a participle")
|
||||||
|
|
||||||
|
|
||||||
|
def check_declaration(g, terms, lex, f, abstractness):
|
||||||
|
"""TN-003, TN-006, TN-007, TN-024, TN-025, TN-026, TN-027, TN-028."""
|
||||||
|
layer_roots = {t for t in terms
|
||||||
|
if terms[t] == "class"
|
||||||
|
and (local(t) or "").endswith("LayerObject")}
|
||||||
|
|
||||||
|
for term, kind in terms.items():
|
||||||
|
name = local(term)
|
||||||
|
if name is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TN-024 — every active term carries a comment
|
||||||
|
if not list(g.objects(term, RDFS.comment)):
|
||||||
|
f.violation("TN-024", name, "carries no rdfs:comment")
|
||||||
|
|
||||||
|
# TN-026 — only a concrete class declares how its instances are produced
|
||||||
|
modes = [str(m) for m in g.objects(term, PR.authoringMode)]
|
||||||
|
concrete = (kind == "class"
|
||||||
|
and Literal(False) in set(g.objects(term, PR.isAbstract)))
|
||||||
|
if modes and not concrete:
|
||||||
|
f.violation("TN-026", name,
|
||||||
|
"declares pr:authoringMode; only a concrete class does, "
|
||||||
|
"since a vocabulary term is declared by definition")
|
||||||
|
elif concrete:
|
||||||
|
if not modes:
|
||||||
|
f.violation("TN-026", name, "does not declare pr:authoringMode")
|
||||||
|
else:
|
||||||
|
if "HARVESTED" in modes and not list(g.objects(term, PR.harvestSource)):
|
||||||
|
f.violation("TN-026", name, "is HARVESTED and names no harvestSource")
|
||||||
|
axis = provenance_axis(g, term)
|
||||||
|
if axis == "CapturedObject" and "HARVESTED" not in modes:
|
||||||
|
f.violation("TN-026", name,
|
||||||
|
"descends from CapturedObject and must declare HARVESTED")
|
||||||
|
if axis == "DefinedObject" and "ASSERTED" not in modes:
|
||||||
|
f.violation("TN-026", name,
|
||||||
|
"descends from DefinedObject and must declare ASSERTED")
|
||||||
|
|
||||||
|
if kind == "class":
|
||||||
|
values = list(g.objects(term, PR.isAbstract))
|
||||||
|
# TN-007 — declared exactly once, explicitly
|
||||||
|
if len(values) != 1:
|
||||||
|
f.violation("TN-007", name,
|
||||||
|
"declares pr:isAbstract %d times; exactly one is required"
|
||||||
|
% len(values))
|
||||||
|
# TN-006 — a layer root is abstract
|
||||||
|
elif term in layer_roots and values[0].toPython() is not True:
|
||||||
|
f.violation("TN-006", name, "is a layer root and must be abstract")
|
||||||
|
# TN-027 — a concrete class has one layer and one provenance
|
||||||
|
elif values[0].toPython() is False:
|
||||||
|
check_attachment(g, term, name, layer_roots, f)
|
||||||
|
|
||||||
|
if kind in ("relation", "attribute"):
|
||||||
|
check_property_typing(g, term, name, lex, f)
|
||||||
|
|
||||||
|
# TN-003 — the display properties are annotations
|
||||||
|
for display in (PR.shortLabel, PR.acronym):
|
||||||
|
name = local(display)
|
||||||
|
types = set(g.objects(display, RDF.type))
|
||||||
|
if not types:
|
||||||
|
continue
|
||||||
|
if OWL.AnnotationProperty not in types:
|
||||||
|
f.violation("TN-003", name, "is declared %s and must be an annotation property"
|
||||||
|
% ", ".join(sorted(local(t) or str(t) for t in types)))
|
||||||
|
|
||||||
|
|
||||||
|
def check_attachment(g, term, name, layer_roots, f):
|
||||||
|
"""TN-027 — exactly one layer root and one provenance axis above."""
|
||||||
|
seen, stack = set(), [term]
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
for parent in g.objects(node, RDFS.subClassOf):
|
||||||
|
if isinstance(parent, URIRef) and parent not in seen:
|
||||||
|
seen.add(parent)
|
||||||
|
stack.append(parent)
|
||||||
|
layers = seen & layer_roots
|
||||||
|
axes = seen & {PR.DefinedObject, PR.CapturedObject}
|
||||||
|
if len(layers) != 1:
|
||||||
|
f.violation("TN-027", name, "descends from %d layer roots; exactly one is required"
|
||||||
|
% len(layers))
|
||||||
|
if len(axes) != 1:
|
||||||
|
f.violation("TN-027", name, "descends from %d provenance axes; exactly one is required"
|
||||||
|
% len(axes))
|
||||||
|
|
||||||
|
|
||||||
|
def check_property_typing(g, term, name, lex, f):
|
||||||
|
"""TN-025 and TN-028 — domain, range, and inherited domain."""
|
||||||
|
domains = list(g.objects(term, RDFS.domain))
|
||||||
|
ranges = list(g.objects(term, RDFS.range))
|
||||||
|
|
||||||
|
if not ranges and Literal(True) not in set(g.objects(term, PR.polymorphic)):
|
||||||
|
f.violation("TN-025", name, "declares no rdfs:range")
|
||||||
|
polymorphic = Literal(True) in set(g.objects(term, PR.polymorphic))
|
||||||
|
if not domains:
|
||||||
|
if polymorphic:
|
||||||
|
f.review("TN-025", name,
|
||||||
|
"declares itself polymorphic; confirm the scope is declared in SHACL")
|
||||||
|
else:
|
||||||
|
f.violation("TN-025", name,
|
||||||
|
"omits its domain in silence; declare pr:polymorphic true "
|
||||||
|
"or state the domain")
|
||||||
|
|
||||||
|
# TN-028 — a sub-property inherits the domain of its parent
|
||||||
|
for parent in g.objects(term, RDFS.subPropertyOf):
|
||||||
|
if not isinstance(parent, URIRef) or local(parent) is None:
|
||||||
|
continue
|
||||||
|
parent_domains = set(g.objects(parent, RDFS.domain))
|
||||||
|
if not parent_domains or not domains:
|
||||||
|
continue
|
||||||
|
if set(domains) & parent_domains:
|
||||||
|
continue
|
||||||
|
compatible = False
|
||||||
|
for d in domains:
|
||||||
|
ancestors, stack = set(), [d]
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
for up in g.objects(node, RDFS.subClassOf):
|
||||||
|
if isinstance(up, URIRef) and up not in ancestors:
|
||||||
|
ancestors.add(up)
|
||||||
|
stack.append(up)
|
||||||
|
if ancestors & parent_domains:
|
||||||
|
compatible = True
|
||||||
|
if not compatible:
|
||||||
|
f.review("TN-028", name,
|
||||||
|
"is a sub-property of %s, whose domain does not cover its own; "
|
||||||
|
"asserting it will retype the subject"
|
||||||
|
% local(parent))
|
||||||
|
|
||||||
|
|
||||||
|
def check_label(g, terms, f):
|
||||||
|
"""TN-018 to TN-023 — the label is derived, and carries nothing else."""
|
||||||
|
for term, kind in terms.items():
|
||||||
|
name = local(term)
|
||||||
|
if name is None:
|
||||||
|
continue
|
||||||
|
labels = list(g.objects(term, RDFS.label))
|
||||||
|
if not labels:
|
||||||
|
f.violation("TN-023", name, "carries no rdfs:label")
|
||||||
|
continue
|
||||||
|
if len(labels) > 1:
|
||||||
|
f.violation("TN-023", name, "carries %d labels; the derivation yields one"
|
||||||
|
% len(labels))
|
||||||
|
text = str(labels[0])
|
||||||
|
expected = decamelise(name, kind in ("class", "individual"))
|
||||||
|
if text != expected:
|
||||||
|
f.violation("TN-018", name, "label is %r; the derivation yields %r"
|
||||||
|
% (text, expected))
|
||||||
|
if "(" in text or ")" in text:
|
||||||
|
f.violation("TN-021", name, "the label carries a gloss; it belongs in the comment")
|
||||||
|
if re.search(r"\b(deprecated|obsolete|draft|published|v\d)\b", text, re.I):
|
||||||
|
f.violation("TN-020", name, "the label carries state; the axiom carries it")
|
||||||
|
|
||||||
|
|
||||||
|
def check_namespace(g, f):
|
||||||
|
"""EV-014 — the term namespace is never versioned."""
|
||||||
|
for prefix, uri in g.namespaces():
|
||||||
|
if str(uri).startswith("https://ontology.pernod-ricard.com/metamodel/") \
|
||||||
|
and str(uri) != NS:
|
||||||
|
f.violation("EV-014", str(uri),
|
||||||
|
"the prefix %s binds a versioned namespace; the version lives "
|
||||||
|
"in owl:versionIRI alone" % prefix)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------ main
|
||||||
|
|
||||||
|
def collect(g):
|
||||||
|
terms = OrderedDict()
|
||||||
|
for rdf_type, kind in KINDS.items():
|
||||||
|
for term in sorted(g.subjects(RDF.type, rdf_type), key=str):
|
||||||
|
if local(term) is None:
|
||||||
|
continue
|
||||||
|
if (term, OWL.deprecated, Literal(True)) in g:
|
||||||
|
continue
|
||||||
|
terms[term] = kind
|
||||||
|
return terms
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--ontology", default=DEFAULT_ONTOLOGY)
|
||||||
|
ap.add_argument("--json", default=None, help="write the findings as JSON")
|
||||||
|
ap.add_argument("--quiet", action="store_true", help="print the summary only")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
lex = yaml.safe_load(open(LEXICON, encoding="utf-8"))
|
||||||
|
rules = yaml.safe_load(open(RULES, encoding="utf-8"))
|
||||||
|
abstractness = {a["term"]: a["is_abstract"] for a in rules["abstractness"]}
|
||||||
|
|
||||||
|
g = Graph()
|
||||||
|
g.parse(args.ontology, format="turtle")
|
||||||
|
terms = collect(g)
|
||||||
|
|
||||||
|
f = Findings()
|
||||||
|
check_identifier(g, terms, lex, f)
|
||||||
|
check_declaration(g, terms, lex, f, abstractness)
|
||||||
|
check_label(g, terms, f)
|
||||||
|
check_namespace(g, f)
|
||||||
|
|
||||||
|
titles = {r["id"]: r["title"] for r in rules["rules"]}
|
||||||
|
print("=" * 78)
|
||||||
|
print("T-BOX NAMING CHECK")
|
||||||
|
print(" ontology : %s" % args.ontology)
|
||||||
|
print(" terms : %d active" % len(terms))
|
||||||
|
print("=" * 78)
|
||||||
|
|
||||||
|
if not args.quiet:
|
||||||
|
for verdict in (VIOLATION, REVIEW):
|
||||||
|
items = [i for i in f.items if i["verdict"] == verdict]
|
||||||
|
if not items:
|
||||||
|
continue
|
||||||
|
print("\n%s — %d" % (verdict, len(items)))
|
||||||
|
by_rule = OrderedDict()
|
||||||
|
for i in items:
|
||||||
|
by_rule.setdefault(i["rule"], []).append(i)
|
||||||
|
for rule, group in sorted(by_rule.items()):
|
||||||
|
print("\n [%s] %s (%d)" % (rule, titles.get(rule, ""), len(group)))
|
||||||
|
for i in group[:12]:
|
||||||
|
print(" %-34s %s" % (i["term"], i["message"]))
|
||||||
|
if len(group) > 12:
|
||||||
|
print(" ... and %d more" % (len(group) - 12))
|
||||||
|
|
||||||
|
print("\n" + "=" * 78)
|
||||||
|
print("RESULT : %d violation(s) | %d for review | %d term(s) checked"
|
||||||
|
% (len(f.violations), len(f.reviews), len(terms)))
|
||||||
|
print("=" * 78)
|
||||||
|
|
||||||
|
if args.json:
|
||||||
|
directory = os.path.dirname(os.path.abspath(args.json))
|
||||||
|
if directory:
|
||||||
|
os.makedirs(directory, exist_ok=True)
|
||||||
|
json.dump({"ontology": args.ontology, "terms": len(terms),
|
||||||
|
"violations": len(f.violations), "reviews": len(f.reviews),
|
||||||
|
"findings": f.items},
|
||||||
|
open(args.json, "w", encoding="utf-8"), indent=2)
|
||||||
|
print("json: %s" % args.json)
|
||||||
|
|
||||||
|
sys.exit(1 if f.violations else 0)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,416 @@
|
|||||||
|
{
|
||||||
|
"ontology": "ontology/pr_metamodel.ttl",
|
||||||
|
"terms": 130,
|
||||||
|
"violations": 49,
|
||||||
|
"reviews": 19,
|
||||||
|
"findings": [
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-026",
|
||||||
|
"term": "BaseTable",
|
||||||
|
"message": "is HARVESTED and names no harvestSource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-026",
|
||||||
|
"term": "ExternalTable",
|
||||||
|
"message": "is HARVESTED and names no harvestSource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-026",
|
||||||
|
"term": "ForeignKey",
|
||||||
|
"message": "is HARVESTED and names no harvestSource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-026",
|
||||||
|
"term": "PrimaryKey",
|
||||||
|
"message": "is HARVESTED and names no harvestSource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-026",
|
||||||
|
"term": "SystemType",
|
||||||
|
"message": "does not declare pr:authoringMode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-027",
|
||||||
|
"term": "SystemType",
|
||||||
|
"message": "descends from 0 layer roots; exactly one is required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-027",
|
||||||
|
"term": "SystemType",
|
||||||
|
"message": "descends from 0 provenance axes; exactly one is required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-026",
|
||||||
|
"term": "View",
|
||||||
|
"message": "is HARVESTED and names no harvestSource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "belongsTo",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "consumes",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "containsBIField",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "governedBy",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "hasDomainOwner",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "hasElement",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "hasGovernanceLead",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "hasMetric",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "hasSubDomainOwner",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "hasSystemType",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "hostedOn",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "isInBIDataSource",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "isInBIWorkspace",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "isInDatabase",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "livesIn",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "measures",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "ownedBy",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "ownedBy",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "ownedByDomain",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "produces",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "referencesSourceField",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "referencesTargetField",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "represents",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "sourcedFrom",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "storedIn",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "usesBIDataSource",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "activationStatus",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "arbitrationStatus",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "businessRule",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "canonicalName",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "canonicalName",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "creationDate",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "creationDate",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "environment",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "exampleValue",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "exampleValue",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "formula",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "fullyQualifiedName",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "harvestDate",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "identifier",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "identifier",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "isNullable",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "lastReviewDate",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "lastReviewDate",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "logicalFormat",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "ordinalPosition",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "physicalDataType",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "physicalName",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "queryCount",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "sourceIdentifier",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "status",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "status",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "synonym",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "technicalDefinition",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "timeAggregation",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "unit",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "version",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "REVIEW",
|
||||||
|
"rule": "TN-025",
|
||||||
|
"term": "version",
|
||||||
|
"message": "declares itself polymorphic; confirm the scope is declared in SHACL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "viewDefinition",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"verdict": "VIOLATION",
|
||||||
|
"rule": "TN-024",
|
||||||
|
"term": "acronym",
|
||||||
|
"message": "carries no rdfs:comment"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
# T-BOX AND A-BOX MIGRATION TO v2.2 — GOVERNANCE ASSIGNMENTS
|
||||||
|
# =============================================================================
|
||||||
|
# Data consumed by migrate_tbox_v2_2.py.
|
||||||
|
#
|
||||||
|
# An actor node is an ASSIGNMENT, not a person. The post is stable and carries
|
||||||
|
# the canonical name, the identifier and the domain it answers to; the person
|
||||||
|
# holding it is a value that changes. Modelled the other way round, a change of
|
||||||
|
# incumbent would move nodes and edges rather than one literal, and the same
|
||||||
|
# person covering two domains would exist twice.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
meta:
|
||||||
|
from_version: "2.1"
|
||||||
|
to_version: "2.2"
|
||||||
|
namespace: "https://ontology.pernod-ricard.com/metamodel/"
|
||||||
|
instance_namespace: "https://data.pernod-ricard.com/sodh/"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# T-BOX
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
declare_properties:
|
||||||
|
- term: personName
|
||||||
|
kind: DatatypeProperty
|
||||||
|
domain: Actor
|
||||||
|
range: "http://www.w3.org/2001/XMLSchema#string"
|
||||||
|
comment: >
|
||||||
|
The name of the person currently holding the post. Declared on Actor rather
|
||||||
|
than on each role, since every role shares it and Actor is their smallest
|
||||||
|
common ancestor below the layer root. Held as a value rather than a node
|
||||||
|
because what changes is the person, not the assignment: the post keeps its
|
||||||
|
identifier, its domain and every edge that points at it, and a change of
|
||||||
|
holder rewrites one literal. Used on actors and nowhere else.
|
||||||
|
|
||||||
|
display_updates:
|
||||||
|
- {term: DataSteward, acronym: DST}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# A-BOX — RENAME
|
||||||
|
# ST is not the short form of Data Steward; DST is, and the rulebook now
|
||||||
|
# declares it.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
instance_renames:
|
||||||
|
- {from: ST_DD_04, to: DST_DD_04}
|
||||||
|
- {from: ST_DD_05, to: DST_DD_05}
|
||||||
|
- {from: ST_DD_06, to: DST_DD_06}
|
||||||
|
- {from: ST_DD_10, to: DST_DD_10}
|
||||||
|
- {from: ST_DD_16, to: DST_DD_16}
|
||||||
|
- {from: ST_DD_21, to: DST_DD_21}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# A-BOX — COMPLETE THE EXISTING ASSIGNMENTS
|
||||||
|
# The canonical name becomes the name of the POST. The person moves to heldBy.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
complete:
|
||||||
|
- {node: DDO_DD_04, identifier: "DDO-04", name: "Data Domain Owner", held_by: "David Carpio", domain: DD_04}
|
||||||
|
- {node: DDO_DD_05, identifier: "DDO-05", name: "Data Domain Owner", held_by: "David Carpio", domain: DD_05}
|
||||||
|
- {node: DDO_DD_06, identifier: "DDO-06", name: "Data Domain Owner", held_by: "Ludovic Cottier", domain: DD_06}
|
||||||
|
- {node: DDO_DD_10, identifier: "DDO-10", name: "Data Domain Owner", held_by: "Alberto Lupano", domain: DD_10}
|
||||||
|
- {node: DDO_DD_16, identifier: "DDO-16", name: "Data Domain Owner", held_by: "Julien Soisson", domain: DD_16}
|
||||||
|
- {node: DDO_DD_21, identifier: "DDO-21", name: "Data Domain Owner", held_by: "Vincent Meunier", domain: DD_21}
|
||||||
|
- {node: DGL_DD_04, identifier: "DGL-04", name: "Data Governance Lead", held_by: "Marie Carabin", domain: DD_04}
|
||||||
|
- {node: DGL_DD_05, identifier: "DGL-05", name: "Data Governance Lead", held_by: "Alberic Piot", domain: DD_05}
|
||||||
|
- {node: DGL_DD_06, identifier: "DGL-06", name: "Data Governance Lead", held_by: "Bastien Gourdon", domain: DD_06}
|
||||||
|
- {node: DGL_DD_10, identifier: "DGL-10", name: "Data Governance Lead", held_by: "Anas El Kesri", domain: DD_10}
|
||||||
|
- {node: DGL_DD_16, identifier: "DGL-16", name: "Data Governance Lead", held_by: "Gaelle Seret", domain: DD_16}
|
||||||
|
- {node: DGL_DD_21, identifier: "DGL-21", name: "Data Governance Lead", held_by: "Bastien Gourdon", domain: DD_21}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# A-BOX — SUB-DOMAIN OWNERS, 10
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
create_sub_domain_owners:
|
||||||
|
- {node: SDO_SD_04_01, identifier: "SDO-04.01", held_by: "Etienne Coulon", sub_domain: SD_04_01, domain: DD_04}
|
||||||
|
- {node: SDO_SD_04_02, identifier: "SDO-04.02", held_by: "Etienne Coulon", sub_domain: SD_04_02, domain: DD_04}
|
||||||
|
- {node: SDO_SD_04_03, identifier: "SDO-04.03", held_by: "Etienne Coulon", sub_domain: SD_04_03, domain: DD_04}
|
||||||
|
- {node: SDO_SD_05_01, identifier: "SDO-05.01", held_by: "David Carpio", sub_domain: SD_05_01, domain: DD_05}
|
||||||
|
- {node: SDO_SD_06_01, identifier: "SDO-06.01", held_by: "Loic Berger", sub_domain: SD_06_01, domain: DD_06}
|
||||||
|
- {node: SDO_SD_10_01, identifier: "SDO-10.01", held_by: "Alberto Lupano", sub_domain: SD_10_01, domain: DD_10}
|
||||||
|
- {node: SDO_SD_16_01, identifier: "SDO-16.01", held_by: "Julien Soisson", sub_domain: SD_16_01, domain: DD_16}
|
||||||
|
- {node: SDO_SD_16_02, identifier: "SDO-16.02", held_by: "Julien Soisson", sub_domain: SD_16_02, domain: DD_16}
|
||||||
|
- {node: SDO_SD_16_03, identifier: "SDO-16.03", held_by: "Julien Soisson", sub_domain: SD_16_03, domain: DD_16}
|
||||||
|
- {node: SDO_SD_21_01, identifier: "SDO-21.01", held_by: "Vincent Meunier", sub_domain: SD_21_01, domain: DD_21}
|
||||||
|
|
||||||
|
sub_domain_owner_name: Data Sub Domain Owner
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# A-BOX — PRODUCT OWNER, 1
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
create_product_owners:
|
||||||
|
- {node: PO_SODH, identifier: "PO-06.01-001", held_by: "Guilherme Muller", product: DP_06_01_001, domain: DD_06}
|
||||||
|
|
||||||
|
product_owner_name: Data Product Owner
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# A-BOX — STEWARDS, ONE ASSIGNMENT PER BUSINESS OBJECT
|
||||||
|
# Derived from the graph rather than listed: the business objects are the source
|
||||||
|
# of truth for how many assignments exist and which domain each answers to.
|
||||||
|
# The incumbent is read from this map by domain.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
steward_holders:
|
||||||
|
DD_04: Marc Chambouleyron
|
||||||
|
DD_05: Francois Vadrouille
|
||||||
|
DD_06: Michael Flores
|
||||||
|
DD_10: Magda Kugli
|
||||||
|
DD_16: Clementine Sauries
|
||||||
|
DD_21: Alberic Piot
|
||||||
|
|
||||||
|
steward_name: Data Steward
|
||||||
|
|
||||||
|
# The six per-domain steward nodes are replaced by one per business object.
|
||||||
|
# monitoredBy is repointed from the old node to the new one, and the old node is
|
||||||
|
# withdrawn once nothing names it (EV-003).
|
||||||
|
retire_domain_stewards: true
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# A-BOX — GOVERN A TRANSVERSE CONCEPT
|
||||||
|
# Panel Coverage is used by four business objects through usesConcept and is the
|
||||||
|
# subject of none. A concept mobilised without an object that carries it has no
|
||||||
|
# owner, no steward and no governance chain: it is used without being governed.
|
||||||
|
# Creating the missing business object closes the chain BC -> BO -> SD -> DD,
|
||||||
|
# and the steward assignment follows automatically since assignments are derived
|
||||||
|
# from the business objects present in the graph.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
create_business_objects:
|
||||||
|
- node: BO_06_01_006
|
||||||
|
identifier: "BO-06.01-006"
|
||||||
|
name: Panel Coverage
|
||||||
|
is_about: BC_06_01_003
|
||||||
|
sub_domain: SD_06_01
|
||||||
|
domain: DD_06
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# A-BOX — ownedBy AGAINST THE OLD DOMAIN-LEVEL STEWARDS
|
||||||
|
# 53 subjects declared they belonged to the per-domain steward node. That node
|
||||||
|
# disappears, so each reference is resolved rather than dropped in silence.
|
||||||
|
#
|
||||||
|
# business object -> removed. It already carries monitoredBy towards the
|
||||||
|
# same assignment, and two properties holding one fact
|
||||||
|
# are two answers to one question waiting to diverge.
|
||||||
|
# sub-domain -> repointed to its Data Sub Domain Owner, whose function
|
||||||
|
# this is; a steward had no business holding it.
|
||||||
|
# everything else -> repointed to the steward assignment of the business
|
||||||
|
# object it belongs to, followed through the graph.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
owned_by_resolution:
|
||||||
|
remove_on: [BusinessObject]
|
||||||
|
repoint_sub_domains: true
|
||||||
|
repoint_to_business_object_assignment: true
|
||||||
|
# Chains followed to reach the business object, in order. The first that
|
||||||
|
# resolves wins; anything unresolved is reported and left untouched.
|
||||||
|
chains:
|
||||||
|
- {via: isAbout, direction: inbound} # a BO is about this concept
|
||||||
|
- {via: represents, direction: outbound} # this object represents a BO
|
||||||
|
- {via: measures, direction: outbound} # a metric measures a concept, then isAbout
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# COMMON
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
status: PUBLISHED
|
||||||
|
version: "1.0"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# SHAPES — NR-002 EXEMPTION
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
shape_notes:
|
||||||
|
- rule: NR-002
|
||||||
|
change: exempt descendants of Actor from the canonical-name uniqueness query
|
||||||
|
reason: >
|
||||||
|
An assignment is a post, not a catalogued object. Five steward posts in one
|
||||||
|
domain are legitimately homonymous; what tells them apart is the identifier
|
||||||
|
and the business object that names them. Applying a uniqueness rule written
|
||||||
|
for catalogued objects would force fifteen invented names for one job.
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
# T-BOX MIGRATION TO v2.1 — DECLARATIONS
|
||||||
|
# =============================================================================
|
||||||
|
# Data consumed by migrate_tbox_v2_1.py. No renaming here: v2.1 declares what
|
||||||
|
# v2.0 left silent.
|
||||||
|
#
|
||||||
|
# Three subjects, each closing a class of TN-025, TN-026 and TN-027 findings:
|
||||||
|
# 1. polymorphism made explicit rather than inferred from prose
|
||||||
|
# 2. domains stated wherever a single class of use exists
|
||||||
|
# 3. provenance moved off the terms and onto the concrete classes
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
meta:
|
||||||
|
from_version: "2.0"
|
||||||
|
to_version: "2.1"
|
||||||
|
namespace: "https://ontology.pernod-ricard.com/metamodel/"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# THE ANNOTATION ITSELF (TN-025)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
declare_annotations:
|
||||||
|
- term: polymorphic
|
||||||
|
label: polymorphic
|
||||||
|
comment: >
|
||||||
|
Declares that a property deliberately carries no rdfs:domain because it
|
||||||
|
serves several classes with no useful common ancestor. Its scope is
|
||||||
|
controlled class by class in SHACL instead. Without this axiom a deliberate
|
||||||
|
omission and a forgotten one look identical, and no check can tell them
|
||||||
|
apart.
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# POLYMORPHIC PROPERTIES — 18
|
||||||
|
#
|
||||||
|
# The test: a property takes as domain the smallest common ancestor of its
|
||||||
|
# classes of use, PROVIDED that ancestor sits below a layer root. A layer root,
|
||||||
|
# a provenance axis and MetaModelObject are all too high to say anything; a
|
||||||
|
# property whose smallest common ancestor is one of those is polymorphic.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
polymorphic:
|
||||||
|
- {term: identifier, reason: "borne by every object in the graph"}
|
||||||
|
- {term: canonicalName, reason: "borne by every object in the graph"}
|
||||||
|
- {term: status, reason: "borne by every object in the graph"}
|
||||||
|
- {term: version, reason: "borne by every object in the graph"}
|
||||||
|
- {term: creationDate, reason: "borne by every object in the graph"}
|
||||||
|
- {term: lastReviewDate, reason: "borne by every object in the graph"}
|
||||||
|
- {term: arbitrationStatus, reason: "any declared object may be under arbitration"}
|
||||||
|
- {term: ownedByDomain, reason: "smallest common ancestor is a provenance axis, not a layer class"}
|
||||||
|
- {term: harvestDate, reason: "smallest common ancestor is a provenance axis, not a layer class"}
|
||||||
|
- {term: sourceIdentifier, reason: "smallest common ancestor is a provenance axis, not a layer class"}
|
||||||
|
- {term: belongsTo, reason: "sub-domain to domain and business object to sub-domain: two couples with no useful ancestor"}
|
||||||
|
- {term: storedIn, reason: "two granularities, data object to structure and data element to field"}
|
||||||
|
- {term: represents, reason: "data object to business object and data element to business concept"}
|
||||||
|
- {term: unit, reason: "data element and metric sit in different layers"}
|
||||||
|
- {term: sourcedFrom, reason: "data object and data structure sit in different layers"}
|
||||||
|
- {term: fullyQualifiedName, reason: "DATABASE.SCHEMA.RELATION[.COLUMN] names a structure and a column alike"}
|
||||||
|
- {term: ownedBy, reason: "any object may have an owner; Actor is the range, the domain is free"}
|
||||||
|
- {term: exampleValue, reason: "observed on a field, documented on a data element"}
|
||||||
|
- {term: physicalName, reason: "a data object names its table as a data element names its column; BR-013 names one carrier where there are two"}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# DOMAINS TO DECLARE — 18
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
domains:
|
||||||
|
- {term: formula, domain: Metric, basis: "shape; forbidden on BusinessConcept, which is what distinguishes a metric"}
|
||||||
|
- {term: timeAggregation, domain: Metric, basis: "belongs to the calculation, alongside formula"}
|
||||||
|
- {term: businessDefinition, domain: BusinessConcept, basis: "shape; forbidden on BusinessObject, whose meaning lives in its concept"}
|
||||||
|
- {term: businessRule, domain: BusinessConcept, basis: "meaning belongs to the concept"}
|
||||||
|
- {term: synonym, domain: BusinessConcept, basis: "meaning belongs to the concept"}
|
||||||
|
- {term: technicalDefinition, domain: BusinessConcept, basis: "meaning belongs to the concept"}
|
||||||
|
- {term: monitoredBy, domain: BusinessObject, basis: "shape; forbidden on DataElement and DataObject, where stewardship is inherited"}
|
||||||
|
- {term: logicalFormat, domain: DataElement, basis: "a logical characteristic of the element"}
|
||||||
|
- {term: isNullable, domain: Field, basis: "a property of the column"}
|
||||||
|
- {term: ordinalPosition, domain: Field, basis: "a property of the column"}
|
||||||
|
- {term: physicalDataType, domain: Field, basis: "a property of the column"}
|
||||||
|
- {term: isInSchema, domain: DataStructure, basis: "shape"}
|
||||||
|
- {term: viewDefinition, domain: View, basis: "a table has no SQL definition"}
|
||||||
|
- {term: queryCount, domain: DataStructure, basis: "ACCESS_HISTORY traces tables, views and external tables alike"}
|
||||||
|
- {term: lastQueryDate, domain: DataStructure, basis: "ACCESS_HISTORY traces tables, views and external tables alike"}
|
||||||
|
- {term: definitionAddress, domain: Transformation, basis: "the Git URI of the code behind a transformation; a view uses viewDefinition"}
|
||||||
|
- {term: expression, domain: BusinessIntelligenceField, basis: "the calculated-field formula, compared against the metric formula to detect drift"}
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# NEW TERM (procedure C1)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
declare_properties:
|
||||||
|
- term: serviceCommitment
|
||||||
|
kind: DatatypeProperty
|
||||||
|
domain: DataContract
|
||||||
|
range: "http://www.w3.org/2001/XMLSchema#string"
|
||||||
|
comment: >
|
||||||
|
What the contract guarantees about the product it covers: refresh schedule,
|
||||||
|
freshness, quality thresholds, support channel, notice period on a breaking
|
||||||
|
change. Held as prose for now, which no shape can verify; the structured
|
||||||
|
form, aligned on ODCS, is the next step. Recorded here because the
|
||||||
|
commitment had been written into businessRule for want of anywhere else,
|
||||||
|
where it sat next to genuine business rules and could not be told apart.
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# RANGES MISSING ON POLYMORPHIC PROPERTIES
|
||||||
|
# A polymorphic property may still have a single range: what it points AT can be
|
||||||
|
# settled even when what it starts FROM cannot.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ranges: []
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# PROVENANCE (TN-026)
|
||||||
|
#
|
||||||
|
# A vocabulary term is declared, always and by definition. What has a provenance
|
||||||
|
# is the INSTANCES, and the concrete class is where the model states which one.
|
||||||
|
# Nothing is inherited: each class declares its own mode, and the mode agrees
|
||||||
|
# with the provenance axis the class descends from.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
provenance:
|
||||||
|
strip_from_non_concrete: true # abstract classes, properties, vocabulary terms
|
||||||
|
derive_from_axis: true # CapturedObject -> HARVESTED, DefinedObject -> ASSERTED
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# ATTACHMENT (TN-027)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
attach:
|
||||||
|
- term: GovernanceLayerObject
|
||||||
|
parent: DefinedObject
|
||||||
|
basis: >
|
||||||
|
A layer root sits on a provenance axis like any other. Actors and governance
|
||||||
|
objects are declared by governance, never harvested, so the governance layer
|
||||||
|
descends from DefinedObject and its five roles inherit the axis.
|
||||||
|
|
||||||
|
# SystemType remains outside the layers: it is a controlled-vocabulary class,
|
||||||
|
# the named and closed exception of TN-027. Recorded as an open arbitration
|
||||||
|
# rather than silently resolved here.
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# INSTANCE CORRECTIONS
|
||||||
|
# Each one was surfaced by declaring a domain: an instance carrying a property
|
||||||
|
# outside its domain is retyped by RDFS inference, and the shapes of the wrong
|
||||||
|
# class then fire on it.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
instance_fixes:
|
||||||
|
- action: remove
|
||||||
|
property: businessDefinition
|
||||||
|
subjects: [DI_06_01_001, DI_06_01_002]
|
||||||
|
reason: >
|
||||||
|
Meaning belongs to the Business Concept. An interface is a channel through
|
||||||
|
which data is delivered; it defines nothing. Same rule the shapes already
|
||||||
|
enforce on Business Object.
|
||||||
|
- action: remove
|
||||||
|
property: sourcedFrom
|
||||||
|
subjects: [DI_06_01_001, DI_06_01_002]
|
||||||
|
reason: >
|
||||||
|
An interface has no system of truth: the data it exposes is sourced by the
|
||||||
|
Data Objects behind it, and those name the system.
|
||||||
|
- action: move
|
||||||
|
property: businessRule
|
||||||
|
to: serviceCommitment
|
||||||
|
subjects: [DC_06_01_001]
|
||||||
|
reason: >
|
||||||
|
The text is a service commitment, not a business rule: refresh window,
|
||||||
|
notice period, support channel. It was written into businessRule for want
|
||||||
|
of a property to hold it.
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T12:21:45",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.0",
|
||||||
|
"to_version": "2.1",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "32929c506d402738807b8b30c04fbb48"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"annotations_declared": 1,
|
||||||
|
"polymorphic_declared": 18,
|
||||||
|
"domains_declared": 18,
|
||||||
|
"attached": 1,
|
||||||
|
"provenance_stripped": 5,
|
||||||
|
"provenance_declared": 29,
|
||||||
|
"provenance_no_axis": 1,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 74,
|
||||||
|
"triples_before": 723,
|
||||||
|
"triples_after": 786,
|
||||||
|
"notes": [
|
||||||
|
"annotations_declared: polymorphic",
|
||||||
|
"attached: GovernanceLayerObject -> DefinedObject",
|
||||||
|
"provenance_stripped: storedIn",
|
||||||
|
"provenance_stripped: primarilyStoredIn",
|
||||||
|
"provenance_stripped: DefinedObject",
|
||||||
|
"provenance_stripped: exposesMetric",
|
||||||
|
"provenance_stripped: CapturedObject",
|
||||||
|
"provenance_no_axis: SystemType",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T12:22:48",
|
||||||
|
"mode": "apply",
|
||||||
|
"from_version": "2.0",
|
||||||
|
"to_version": "2.1",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "32929c506d402738807b8b30c04fbb48"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"annotations_declared": 1,
|
||||||
|
"polymorphic_declared": 18,
|
||||||
|
"domains_declared": 18,
|
||||||
|
"attached": 1,
|
||||||
|
"provenance_stripped": 5,
|
||||||
|
"provenance_declared": 29,
|
||||||
|
"provenance_no_axis": 1,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 74,
|
||||||
|
"triples_before": 723,
|
||||||
|
"triples_after": 786,
|
||||||
|
"notes": [
|
||||||
|
"annotations_declared: polymorphic",
|
||||||
|
"attached: GovernanceLayerObject -> DefinedObject",
|
||||||
|
"provenance_stripped: DefinedObject",
|
||||||
|
"provenance_stripped: CapturedObject",
|
||||||
|
"provenance_stripped: storedIn",
|
||||||
|
"provenance_stripped: exposesMetric",
|
||||||
|
"provenance_stripped: primarilyStoredIn",
|
||||||
|
"provenance_no_axis: SystemType",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T12:22:50",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.0",
|
||||||
|
"to_version": "2.1",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "04273379898d04b9952de68004de5768"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"provenance_no_axis": 1
|
||||||
|
},
|
||||||
|
"total_changes": 1,
|
||||||
|
"triples_before": 786,
|
||||||
|
"triples_after": 786,
|
||||||
|
"notes": [
|
||||||
|
"provenance_no_axis: SystemType"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T14:34:17",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.0",
|
||||||
|
"to_version": "2.1",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "32929c506d402738807b8b30c04fbb48",
|
||||||
|
"instances/sodh.ttl": "6bef7bad65c091738cd58cf5f0f1a395"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"annotations_declared": 1,
|
||||||
|
"properties_declared": 1,
|
||||||
|
"polymorphic_declared": 19,
|
||||||
|
"domains_declared": 17,
|
||||||
|
"attached": 1,
|
||||||
|
"provenance_stripped": 5,
|
||||||
|
"provenance_declared": 29,
|
||||||
|
"provenance_no_axis": 1,
|
||||||
|
"instances_removed": 4,
|
||||||
|
"instances_moved": 1,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 80,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 723,
|
||||||
|
"instances/sodh.ttl": 2308
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"annotations_declared: polymorphic",
|
||||||
|
"properties_declared: serviceCommitment",
|
||||||
|
"attached: GovernanceLayerObject -> DefinedObject",
|
||||||
|
"provenance_stripped: CapturedObject",
|
||||||
|
"provenance_stripped: storedIn",
|
||||||
|
"provenance_stripped: primarilyStoredIn",
|
||||||
|
"provenance_stripped: DefinedObject",
|
||||||
|
"provenance_stripped: exposesMetric",
|
||||||
|
"provenance_no_axis: SystemType",
|
||||||
|
"instances_removed: DI_06_01_001 businessDefinition",
|
||||||
|
"instances_removed: DI_06_01_002 businessDefinition",
|
||||||
|
"instances_removed: DI_06_01_001 sourcedFrom",
|
||||||
|
"instances_removed: DI_06_01_002 sourcedFrom",
|
||||||
|
"instances_moved: DC_06_01_001 businessRule",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T14:35:03",
|
||||||
|
"mode": "apply",
|
||||||
|
"from_version": "2.0",
|
||||||
|
"to_version": "2.1",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "32929c506d402738807b8b30c04fbb48",
|
||||||
|
"instances/sodh.ttl": "6bef7bad65c091738cd58cf5f0f1a395"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"annotations_declared": 1,
|
||||||
|
"properties_declared": 1,
|
||||||
|
"polymorphic_declared": 19,
|
||||||
|
"domains_declared": 17,
|
||||||
|
"attached": 1,
|
||||||
|
"provenance_stripped": 5,
|
||||||
|
"provenance_declared": 29,
|
||||||
|
"provenance_no_axis": 1,
|
||||||
|
"instances_removed": 4,
|
||||||
|
"instances_moved": 1,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 80,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 723,
|
||||||
|
"instances/sodh.ttl": 2308
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"annotations_declared: polymorphic",
|
||||||
|
"properties_declared: serviceCommitment",
|
||||||
|
"attached: GovernanceLayerObject -> DefinedObject",
|
||||||
|
"provenance_stripped: storedIn",
|
||||||
|
"provenance_stripped: primarilyStoredIn",
|
||||||
|
"provenance_stripped: DefinedObject",
|
||||||
|
"provenance_stripped: CapturedObject",
|
||||||
|
"provenance_stripped: exposesMetric",
|
||||||
|
"provenance_no_axis: SystemType",
|
||||||
|
"instances_removed: DI_06_01_001 businessDefinition",
|
||||||
|
"instances_removed: DI_06_01_002 businessDefinition",
|
||||||
|
"instances_removed: DI_06_01_001 sourcedFrom",
|
||||||
|
"instances_removed: DI_06_01_002 sourcedFrom",
|
||||||
|
"instances_moved: DC_06_01_001 businessRule",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T14:35:07",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.0",
|
||||||
|
"to_version": "2.1",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "d264f924c61faa2fe8caf7be8ec7490c",
|
||||||
|
"instances/sodh.ttl": "135278dd413486cb1273fe193a5aa590"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"provenance_no_axis": 1
|
||||||
|
},
|
||||||
|
"total_changes": 1,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"provenance_no_axis: SystemType"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-07T10:27:29",
|
||||||
|
"mode": "apply",
|
||||||
|
"from_version": "2.0",
|
||||||
|
"to_version": "2.1",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "32929c506d402738807b8b30c04fbb48",
|
||||||
|
"instances/sodh.ttl": "6bef7bad65c091738cd58cf5f0f1a395"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"annotations_declared": 1,
|
||||||
|
"properties_declared": 1,
|
||||||
|
"polymorphic_declared": 19,
|
||||||
|
"domains_declared": 17,
|
||||||
|
"attached": 1,
|
||||||
|
"provenance_stripped": 5,
|
||||||
|
"provenance_declared": 29,
|
||||||
|
"provenance_no_axis": 1,
|
||||||
|
"instances_removed": 4,
|
||||||
|
"instances_moved": 1,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 80,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 723,
|
||||||
|
"instances/sodh.ttl": 2308
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"annotations_declared: polymorphic",
|
||||||
|
"properties_declared: serviceCommitment",
|
||||||
|
"attached: GovernanceLayerObject -> DefinedObject",
|
||||||
|
"provenance_stripped: storedIn",
|
||||||
|
"provenance_stripped: exposesMetric",
|
||||||
|
"provenance_stripped: DefinedObject",
|
||||||
|
"provenance_stripped: CapturedObject",
|
||||||
|
"provenance_stripped: primarilyStoredIn",
|
||||||
|
"provenance_no_axis: SystemType",
|
||||||
|
"instances_removed: DI_06_01_001 businessDefinition",
|
||||||
|
"instances_removed: DI_06_01_002 businessDefinition",
|
||||||
|
"instances_removed: DI_06_01_001 sourcedFrom",
|
||||||
|
"instances_removed: DI_06_01_002 sourcedFrom",
|
||||||
|
"instances_moved: DC_06_01_001 businessRule",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T15:17:04",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "d264f924c61faa2fe8caf7be8ec7490c",
|
||||||
|
"instances/sodh.ttl": "135278dd413486cb1273fe193a5aa590"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"properties_declared": 1,
|
||||||
|
"display_updated": 1,
|
||||||
|
"instances_renamed": 80,
|
||||||
|
"assignments_completed": 12,
|
||||||
|
"sub_domain_owners_created": 10,
|
||||||
|
"product_owners_created": 1,
|
||||||
|
"stewards_created": 15,
|
||||||
|
"steward_still_referenced": 6,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 127,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 796,
|
||||||
|
"instances/sodh.ttl": 2557
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"properties_declared: heldBy",
|
||||||
|
"display_updated: DataSteward -> DST",
|
||||||
|
"steward_still_referenced: DST_DD_04 cited by 8 subject(s)",
|
||||||
|
"steward_still_referenced: DST_DD_05 cited by 3 subject(s)",
|
||||||
|
"steward_still_referenced: DST_DD_06 cited by 23 subject(s)",
|
||||||
|
"steward_still_referenced: DST_DD_10 cited by 4 subject(s)",
|
||||||
|
"steward_still_referenced: DST_DD_16 cited by 11 subject(s)",
|
||||||
|
"steward_still_referenced: DST_DD_21 cited by 4 subject(s)",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T15:23:27",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "d264f924c61faa2fe8caf7be8ec7490c",
|
||||||
|
"instances/sodh.ttl": "135278dd413486cb1273fe193a5aa590"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"properties_declared": 1,
|
||||||
|
"display_updated": 1,
|
||||||
|
"instances_renamed": 80,
|
||||||
|
"assignments_completed": 12,
|
||||||
|
"sub_domain_owners_created": 10,
|
||||||
|
"product_owners_created": 1,
|
||||||
|
"stewards_created": 15,
|
||||||
|
"ownedby_removed": 15,
|
||||||
|
"ownedby_repointed_to_sdo": 7,
|
||||||
|
"ownedby_repointed_to_steward": 30,
|
||||||
|
"ownedby_unresolved": 1,
|
||||||
|
"domain_stewards_retired": 5,
|
||||||
|
"steward_still_referenced": 1,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 180,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 796,
|
||||||
|
"instances/sodh.ttl": 2532
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"properties_declared: heldBy",
|
||||||
|
"display_updated: DataSteward -> DST",
|
||||||
|
"ownedby_unresolved: BC_06_01_003 reaches no business object",
|
||||||
|
"domain_stewards_retired: DST_DD_04",
|
||||||
|
"domain_stewards_retired: DST_DD_05",
|
||||||
|
"steward_still_referenced: DST_DD_06 cited by 1 subject(s)",
|
||||||
|
"domain_stewards_retired: DST_DD_10",
|
||||||
|
"domain_stewards_retired: DST_DD_16",
|
||||||
|
"domain_stewards_retired: DST_DD_21",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T19:49:37",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "d264f924c61faa2fe8caf7be8ec7490c",
|
||||||
|
"instances/sodh.ttl": "135278dd413486cb1273fe193a5aa590"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"properties_declared": 1,
|
||||||
|
"display_updated": 1,
|
||||||
|
"instances_renamed": 80,
|
||||||
|
"assignments_completed": 12,
|
||||||
|
"sub_domain_owners_created": 10,
|
||||||
|
"product_owners_created": 1,
|
||||||
|
"business_objects_created": 1,
|
||||||
|
"stewards_created": 16,
|
||||||
|
"ownedby_repointed_to_steward": 31,
|
||||||
|
"ownedby_removed": 15,
|
||||||
|
"ownedby_repointed_to_sdo": 7,
|
||||||
|
"domain_stewards_retired": 6,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 182,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 796,
|
||||||
|
"instances/sodh.ttl": 2546
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"properties_declared: heldBy",
|
||||||
|
"display_updated: DataSteward -> DST",
|
||||||
|
"business_objects_created: BO_06_01_006",
|
||||||
|
"domain_stewards_retired: DST_DD_04",
|
||||||
|
"domain_stewards_retired: DST_DD_05",
|
||||||
|
"domain_stewards_retired: DST_DD_06",
|
||||||
|
"domain_stewards_retired: DST_DD_10",
|
||||||
|
"domain_stewards_retired: DST_DD_16",
|
||||||
|
"domain_stewards_retired: DST_DD_21",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T19:51:46",
|
||||||
|
"mode": "apply",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "d264f924c61faa2fe8caf7be8ec7490c",
|
||||||
|
"instances/sodh.ttl": "135278dd413486cb1273fe193a5aa590"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"properties_declared": 1,
|
||||||
|
"display_updated": 1,
|
||||||
|
"instances_renamed": 80,
|
||||||
|
"assignments_completed": 12,
|
||||||
|
"sub_domain_owners_created": 10,
|
||||||
|
"product_owners_created": 1,
|
||||||
|
"business_objects_created": 1,
|
||||||
|
"stewards_created": 16,
|
||||||
|
"ownedby_repointed_to_steward": 31,
|
||||||
|
"ownedby_removed": 15,
|
||||||
|
"ownedby_repointed_to_sdo": 7,
|
||||||
|
"domain_stewards_retired": 6,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 182,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 796,
|
||||||
|
"instances/sodh.ttl": 2546
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"properties_declared: heldBy",
|
||||||
|
"display_updated: DataSteward -> DST",
|
||||||
|
"business_objects_created: BO_06_01_006",
|
||||||
|
"domain_stewards_retired: DST_DD_04",
|
||||||
|
"domain_stewards_retired: DST_DD_05",
|
||||||
|
"domain_stewards_retired: DST_DD_06",
|
||||||
|
"domain_stewards_retired: DST_DD_10",
|
||||||
|
"domain_stewards_retired: DST_DD_16",
|
||||||
|
"domain_stewards_retired: DST_DD_21",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-06T19:51:51",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "e705cde06956d746631e412719b6cc6d",
|
||||||
|
"instances/sodh.ttl": "82ced2220c1e0525c1dcd5028afbc546"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"instances_renamed": 0
|
||||||
|
},
|
||||||
|
"total_changes": 0,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 796,
|
||||||
|
"instances/sodh.ttl": 2546
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 796,
|
||||||
|
"instances/sodh.ttl": 2546
|
||||||
|
},
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-07T10:21:51",
|
||||||
|
"mode": "apply",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "32929c506d402738807b8b30c04fbb48",
|
||||||
|
"instances/sodh.ttl": "6bef7bad65c091738cd58cf5f0f1a395"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"properties_declared": 1,
|
||||||
|
"display_updated": 1,
|
||||||
|
"instances_renamed": 80,
|
||||||
|
"assignments_completed": 12,
|
||||||
|
"sub_domain_owners_created": 10,
|
||||||
|
"product_owners_created": 1,
|
||||||
|
"business_objects_created": 1,
|
||||||
|
"stewards_created": 16,
|
||||||
|
"ownedby_repointed_to_steward": 31,
|
||||||
|
"ownedby_removed": 15,
|
||||||
|
"ownedby_repointed_to_sdo": 7,
|
||||||
|
"domain_stewards_retired": 6,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 182,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 723,
|
||||||
|
"instances/sodh.ttl": 2308
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 729,
|
||||||
|
"instances/sodh.ttl": 2550
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"properties_declared: personName",
|
||||||
|
"display_updated: DataSteward -> DST",
|
||||||
|
"business_objects_created: BO_06_01_006",
|
||||||
|
"domain_stewards_retired: DST_DD_04",
|
||||||
|
"domain_stewards_retired: DST_DD_05",
|
||||||
|
"domain_stewards_retired: DST_DD_06",
|
||||||
|
"domain_stewards_retired: DST_DD_10",
|
||||||
|
"domain_stewards_retired: DST_DD_16",
|
||||||
|
"domain_stewards_retired: DST_DD_21",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-07T10:21:59",
|
||||||
|
"mode": "dry-run",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "6f2deb192272d0bceecac31fcb525ad1",
|
||||||
|
"instances/sodh.ttl": "7c0d42f33590d2208b0af7b2068f559a"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"instances_renamed": 0
|
||||||
|
},
|
||||||
|
"total_changes": 0,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 729,
|
||||||
|
"instances/sodh.ttl": 2550
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 729,
|
||||||
|
"instances/sodh.ttl": 2550
|
||||||
|
},
|
||||||
|
"notes": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"attempt_timestamp": "2026-08-07T10:27:33",
|
||||||
|
"mode": "apply",
|
||||||
|
"from_version": "2.1",
|
||||||
|
"to_version": "2.2",
|
||||||
|
"input_checksums": {
|
||||||
|
"ontology/pr_metamodel.ttl": "96687771a06f85759e7b894f9d248083",
|
||||||
|
"instances/sodh.ttl": "135278dd413486cb1273fe193a5aa590"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"properties_declared": 1,
|
||||||
|
"display_updated": 1,
|
||||||
|
"instances_renamed": 80,
|
||||||
|
"assignments_completed": 12,
|
||||||
|
"sub_domain_owners_created": 10,
|
||||||
|
"product_owners_created": 1,
|
||||||
|
"business_objects_created": 1,
|
||||||
|
"stewards_created": 16,
|
||||||
|
"ownedby_repointed_to_steward": 31,
|
||||||
|
"ownedby_removed": 15,
|
||||||
|
"ownedby_repointed_to_sdo": 7,
|
||||||
|
"domain_stewards_retired": 6,
|
||||||
|
"version_bumped": 1
|
||||||
|
},
|
||||||
|
"total_changes": 182,
|
||||||
|
"triples_before": {
|
||||||
|
"ontology/pr_metamodel.ttl": 791,
|
||||||
|
"instances/sodh.ttl": 2304
|
||||||
|
},
|
||||||
|
"triples_after": {
|
||||||
|
"ontology/pr_metamodel.ttl": 797,
|
||||||
|
"instances/sodh.ttl": 2546
|
||||||
|
},
|
||||||
|
"notes": [
|
||||||
|
"properties_declared: personName",
|
||||||
|
"display_updated: DataSteward -> DST",
|
||||||
|
"business_objects_created: BO_06_01_006",
|
||||||
|
"domain_stewards_retired: DST_DD_04",
|
||||||
|
"domain_stewards_retired: DST_DD_05",
|
||||||
|
"domain_stewards_retired: DST_DD_06",
|
||||||
|
"domain_stewards_retired: DST_DD_10",
|
||||||
|
"domain_stewards_retired: DST_DD_16",
|
||||||
|
"domain_stewards_retired: DST_DD_21",
|
||||||
|
"version_bumped: https://ontology.pernod-ricard.com/metamodel/2.2"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Migrate the Pernod Ricard Data MetaModel from v2.0 to v2.1.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
python3 migrate_tbox_v2_1.py # dry run, writes nothing
|
||||||
|
python3 migrate_tbox_v2_1.py --apply
|
||||||
|
|
||||||
|
v2.1 renames nothing. It declares what v2.0 left silent: which properties are
|
||||||
|
polymorphic on purpose, what domain the others take, and how the instances of
|
||||||
|
each concrete class are produced.
|
||||||
|
|
||||||
|
DESIGN
|
||||||
|
EV-004 dry run is the default
|
||||||
|
EV-005 every edit goes through rdflib
|
||||||
|
EV-006 guards test the target state, so a replay reports zero change
|
||||||
|
EV-015 an execution log is written for every attempt
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from rdflib import Graph, Literal, Namespace, RDF, RDFS, OWL, URIRef
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
SPEC = os.path.join(HERE, "declarations_v2_1.yaml")
|
||||||
|
|
||||||
|
|
||||||
|
class Report(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.steps = OrderedDict()
|
||||||
|
self.notes = []
|
||||||
|
|
||||||
|
def add(self, step, n=1, detail=None):
|
||||||
|
self.steps[step] = self.steps.get(step, 0) + n
|
||||||
|
if detail:
|
||||||
|
self.notes.append("%s: %s" % (step, detail))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total(self):
|
||||||
|
return sum(self.steps.values())
|
||||||
|
|
||||||
|
|
||||||
|
def md5(path):
|
||||||
|
h = hashlib.md5()
|
||||||
|
with open(path, "rb") as fh:
|
||||||
|
for chunk in iter(lambda: fh.read(65536), b""):
|
||||||
|
h.update(chunk)
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def derive_label(name):
|
||||||
|
"""TN-018 and TN-023: a label is derived from the local name, never typed."""
|
||||||
|
spaced = re.sub(r"(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])", " ", name)
|
||||||
|
return spaced[0].lower() + spaced[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def local(uri, ns):
|
||||||
|
s = str(uri)
|
||||||
|
return s[len(ns):] if s.startswith(ns) else None
|
||||||
|
|
||||||
|
|
||||||
|
def ancestors(g, term):
|
||||||
|
seen, stack = set(), [term]
|
||||||
|
while stack:
|
||||||
|
node = stack.pop()
|
||||||
|
for parent in g.objects(node, RDFS.subClassOf):
|
||||||
|
if isinstance(parent, URIRef) and parent not in seen:
|
||||||
|
seen.add(parent)
|
||||||
|
stack.append(parent)
|
||||||
|
return seen
|
||||||
|
|
||||||
|
|
||||||
|
def check_version(g, spec, path):
|
||||||
|
"""EV-010 applied to a migration script: refuse to run against the wrong input.
|
||||||
|
|
||||||
|
A script announcing 2.1 -> 2.2 will happily work on a file still in 2.0 and
|
||||||
|
report success, leaving a state that is neither version. The same silent
|
||||||
|
failure the shapes are guarded against, one layer down.
|
||||||
|
"""
|
||||||
|
ns = spec["meta"]["namespace"]
|
||||||
|
expected = URIRef(ns.rstrip("/") + "/" + spec["meta"]["from_version"])
|
||||||
|
target = URIRef(ns.rstrip("/") + "/" + spec["meta"]["to_version"])
|
||||||
|
found = set(g.objects(None, OWL.versionIRI))
|
||||||
|
if target in found:
|
||||||
|
print("ALREADY AT %s — nothing to do." % spec["meta"]["to_version"])
|
||||||
|
sys.exit(0)
|
||||||
|
if expected not in found:
|
||||||
|
print("ABORT — %s declares %s; this script migrates from %s."
|
||||||
|
% (os.path.basename(path),
|
||||||
|
", ".join(sorted(str(f) for f in found)) or "no version",
|
||||||
|
expected))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(g, instances, spec, report):
|
||||||
|
ns = spec["meta"]["namespace"]
|
||||||
|
pr = Namespace(ns)
|
||||||
|
|
||||||
|
# 1 — declare the annotation property itself
|
||||||
|
for a in spec.get("declare_annotations") or []:
|
||||||
|
term = pr[a["term"]]
|
||||||
|
if (term, RDF.type, OWL.AnnotationProperty) not in g:
|
||||||
|
g.add((term, RDF.type, OWL.AnnotationProperty))
|
||||||
|
g.add((term, RDFS.label, Literal(a["label"])))
|
||||||
|
g.add((term, RDFS.comment, Literal(" ".join(a["comment"].split()))))
|
||||||
|
report.add("annotations_declared", 1, a["term"])
|
||||||
|
|
||||||
|
# 2 — declare the new properties (procedure C1)
|
||||||
|
for d in spec.get("declare_properties") or []:
|
||||||
|
term = pr[d["term"]]
|
||||||
|
kind = getattr(OWL, d["kind"])
|
||||||
|
if (term, RDF.type, kind) not in g:
|
||||||
|
g.add((term, RDF.type, kind))
|
||||||
|
g.add((term, RDFS.label, Literal(derive_label(d["term"]))))
|
||||||
|
g.add((term, RDFS.domain, pr[d["domain"]]))
|
||||||
|
g.add((term, RDFS.range, URIRef(d["range"])))
|
||||||
|
g.add((term, RDFS.comment, Literal(" ".join(d["comment"].split()))))
|
||||||
|
report.add("properties_declared", 1, d["term"])
|
||||||
|
|
||||||
|
# 3 — mark the deliberately polymorphic properties (TN-025)
|
||||||
|
for p in spec.get("polymorphic") or []:
|
||||||
|
term = pr[p["term"]]
|
||||||
|
if (term, None, None) not in g:
|
||||||
|
report.add("polymorphic_missing", 1, p["term"])
|
||||||
|
continue
|
||||||
|
if (term, pr.polymorphic, Literal(True)) not in g:
|
||||||
|
g.add((term, pr.polymorphic, Literal(True)))
|
||||||
|
report.add("polymorphic_declared", 1)
|
||||||
|
if list(g.objects(term, RDFS.domain)):
|
||||||
|
report.add("polymorphic_has_domain", 1,
|
||||||
|
"%s is marked polymorphic and still declares a domain" % p["term"])
|
||||||
|
|
||||||
|
# 4 — declare the domains that were merely missing (TN-025)
|
||||||
|
for d in spec.get("domains") or []:
|
||||||
|
term, target = pr[d["term"]], pr[d["domain"]]
|
||||||
|
if (term, None, None) not in g:
|
||||||
|
report.add("domain_missing_term", 1, d["term"])
|
||||||
|
continue
|
||||||
|
if (target, RDF.type, OWL.Class) not in g:
|
||||||
|
report.add("domain_missing_class", 1, "%s -> %s" % (d["term"], d["domain"]))
|
||||||
|
continue
|
||||||
|
if (term, RDFS.domain, target) not in g:
|
||||||
|
g.remove((term, RDFS.domain, None))
|
||||||
|
g.add((term, RDFS.domain, target))
|
||||||
|
report.add("domains_declared", 1)
|
||||||
|
|
||||||
|
# 5 — ranges, where a polymorphic property still points at one class
|
||||||
|
for r in spec.get("ranges") or []:
|
||||||
|
term, target = pr[r["term"]], pr[r["range"]]
|
||||||
|
if (term, RDFS.range, target) not in g:
|
||||||
|
g.remove((term, RDFS.range, None))
|
||||||
|
g.add((term, RDFS.range, target))
|
||||||
|
report.add("ranges_declared", 1)
|
||||||
|
|
||||||
|
# 6 — attach a layer root to its provenance axis (TN-027)
|
||||||
|
for a in spec.get("attach") or []:
|
||||||
|
term, parent = pr[a["term"]], pr[a["parent"]]
|
||||||
|
if (term, RDFS.subClassOf, parent) not in g:
|
||||||
|
g.add((term, RDFS.subClassOf, parent))
|
||||||
|
report.add("attached", 1, "%s -> %s" % (a["term"], a["parent"]))
|
||||||
|
|
||||||
|
# 7 — provenance belongs to the concrete classes, and to nothing else (TN-026)
|
||||||
|
prov = spec.get("provenance") or {}
|
||||||
|
concrete = {c for c in g.subjects(RDF.type, OWL.Class)
|
||||||
|
if (c, pr.isAbstract, Literal(False)) in g}
|
||||||
|
|
||||||
|
if prov.get("strip_from_non_concrete"):
|
||||||
|
for s in set(g.subjects(pr.authoringMode, None)):
|
||||||
|
if s in concrete:
|
||||||
|
continue
|
||||||
|
g.remove((s, pr.authoringMode, None))
|
||||||
|
g.remove((s, pr.harvestSource, None))
|
||||||
|
report.add("provenance_stripped", 1, local(s, ns))
|
||||||
|
|
||||||
|
if prov.get("derive_from_axis"):
|
||||||
|
for cls in sorted(concrete, key=str):
|
||||||
|
up = ancestors(g, cls)
|
||||||
|
if pr.CapturedObject in up:
|
||||||
|
want = "HARVESTED"
|
||||||
|
elif pr.DefinedObject in up:
|
||||||
|
want = "ASSERTED"
|
||||||
|
else:
|
||||||
|
report.add("provenance_no_axis", 1, local(cls, ns))
|
||||||
|
continue
|
||||||
|
if (cls, pr.authoringMode, Literal(want)) in g:
|
||||||
|
continue
|
||||||
|
g.remove((cls, pr.authoringMode, None))
|
||||||
|
g.add((cls, pr.authoringMode, Literal(want)))
|
||||||
|
report.add("provenance_declared", 1)
|
||||||
|
|
||||||
|
# 8 — instance corrections, each one surfaced by declaring a domain
|
||||||
|
ex = Namespace("https://data.pernod-ricard.com/sodh/")
|
||||||
|
for fix in spec.get("instance_fixes") or []:
|
||||||
|
prop = pr[fix["property"]]
|
||||||
|
dest = pr[fix["to"]] if fix.get("to") else None
|
||||||
|
for subject in fix["subjects"]:
|
||||||
|
node = ex[subject]
|
||||||
|
for gi in instances:
|
||||||
|
for _, _, value in list(gi.triples((node, prop, None))):
|
||||||
|
gi.remove((node, prop, value))
|
||||||
|
if dest is not None:
|
||||||
|
gi.add((node, dest, value))
|
||||||
|
report.add("instances_%sd" % fix["action"], 1,
|
||||||
|
"%s %s" % (subject, fix["property"]))
|
||||||
|
|
||||||
|
# 9 — bump the version (EV-014: the namespace itself never moves)
|
||||||
|
target = URIRef(ns.rstrip("/") + "/" + spec["meta"]["to_version"])
|
||||||
|
for onto in set(g.subjects(RDF.type, OWL.Ontology)):
|
||||||
|
if (onto, OWL.versionIRI, target) not in g:
|
||||||
|
g.remove((onto, OWL.versionIRI, None))
|
||||||
|
g.add((onto, OWL.versionIRI, target))
|
||||||
|
report.add("version_bumped", 1, str(target))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--apply", action="store_true")
|
||||||
|
ap.add_argument("--ontology", default=os.path.join(HERE, "..", "..", "ontology",
|
||||||
|
"pr_metamodel.ttl"))
|
||||||
|
ap.add_argument("--instances", action="append", default=[])
|
||||||
|
ap.add_argument("--log-dir", default=os.path.join(HERE, "logs"))
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if not os.path.exists(args.ontology):
|
||||||
|
print("MISSING INPUT: %s" % args.ontology)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
spec = yaml.safe_load(open(SPEC, encoding="utf-8"))
|
||||||
|
checksums = OrderedDict((p, md5(p)) for p in [args.ontology] + args.instances)
|
||||||
|
|
||||||
|
g = Graph()
|
||||||
|
g.parse(args.ontology, format="turtle")
|
||||||
|
instances = []
|
||||||
|
for path in args.instances:
|
||||||
|
gi = Graph()
|
||||||
|
gi.parse(path, format="turtle")
|
||||||
|
instances.append(gi)
|
||||||
|
check_version(g, spec, args.ontology)
|
||||||
|
|
||||||
|
before = [len(g)] + [len(x) for x in instances]
|
||||||
|
|
||||||
|
report = Report()
|
||||||
|
migrate(g, instances, spec, report)
|
||||||
|
after = [len(g)] + [len(x) for x in instances]
|
||||||
|
|
||||||
|
print("MIGRATION %s -> %s %s"
|
||||||
|
% (spec["meta"]["from_version"], spec["meta"]["to_version"],
|
||||||
|
"APPLY" if args.apply else "DRY RUN"))
|
||||||
|
print()
|
||||||
|
for step, n in report.steps.items():
|
||||||
|
print(" %-28s %6d" % (step, n))
|
||||||
|
print(" %-28s %6d" % ("total changes", report.total))
|
||||||
|
print()
|
||||||
|
for path, b, a in zip([args.ontology] + args.instances, before, after):
|
||||||
|
print(" %-46s %6d -> %6d triples" % (os.path.basename(path), b, a))
|
||||||
|
if report.notes:
|
||||||
|
print()
|
||||||
|
for n in report.notes:
|
||||||
|
print(" note: " + n)
|
||||||
|
|
||||||
|
os.makedirs(args.log_dir, exist_ok=True)
|
||||||
|
stamp = datetime.datetime.now().strftime("%Y%m%dT%H%M%S")
|
||||||
|
log_path = os.path.join(args.log_dir, "migration_v2_1_%s.json" % stamp)
|
||||||
|
json.dump({"attempt_timestamp": datetime.datetime.now().isoformat(timespec="seconds"),
|
||||||
|
"mode": "apply" if args.apply else "dry-run",
|
||||||
|
"from_version": spec["meta"]["from_version"],
|
||||||
|
"to_version": spec["meta"]["to_version"],
|
||||||
|
"input_checksums": checksums,
|
||||||
|
"steps": report.steps, "total_changes": report.total,
|
||||||
|
"triples_before": dict(zip([args.ontology] + args.instances, before)),
|
||||||
|
"triples_after": dict(zip([args.ontology] + args.instances, after)),
|
||||||
|
"notes": report.notes},
|
||||||
|
open(log_path, "w", encoding="utf-8"), indent=2)
|
||||||
|
print("\n log: %s" % log_path)
|
||||||
|
|
||||||
|
if not args.apply:
|
||||||
|
print("\n DRY RUN — nothing written. Re-run with --apply when the counts "
|
||||||
|
"above are what you expect.")
|
||||||
|
return
|
||||||
|
|
||||||
|
g.serialize(destination=args.ontology, format="turtle")
|
||||||
|
for path, gi in zip(args.instances, instances):
|
||||||
|
gi.serialize(destination=path, format="turtle")
|
||||||
|
print("\n written. Replay this script now: a second run must report zero "
|
||||||
|
"changes (EV-006).")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,367 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Migrate the Pernod Ricard Data MetaModel and the SODH instances to v2.2.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
python3 migrate_tbox_v2_2.py --ontology ... --instances ...
|
||||||
|
python3 migrate_tbox_v2_2.py --ontology ... --instances ... --apply
|
||||||
|
|
||||||
|
v2.2 turns the actor nodes into governance ASSIGNMENTS: a stable post carrying
|
||||||
|
an identifier, a domain and a name, plus the person holding it as a value. It
|
||||||
|
completes the twelve that existed, creates the ten sub-domain owners, the
|
||||||
|
product owner, and one steward assignment per business object.
|
||||||
|
|
||||||
|
DESIGN
|
||||||
|
EV-004 dry run is the default
|
||||||
|
EV-005 every edit goes through rdflib
|
||||||
|
EV-006 guards test the target state, so a replay reports zero change
|
||||||
|
EV-015 an execution log is written for every attempt
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from rdflib import Graph, Literal, Namespace, RDF, RDFS, OWL, URIRef
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
SPEC = os.path.join(HERE, "assignments_v2_2.yaml")
|
||||||
|
|
||||||
|
|
||||||
|
class Report(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.steps = OrderedDict()
|
||||||
|
self.notes = []
|
||||||
|
|
||||||
|
def add(self, step, n=1, detail=None):
|
||||||
|
self.steps[step] = self.steps.get(step, 0) + n
|
||||||
|
if detail:
|
||||||
|
self.notes.append("%s: %s" % (step, detail))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def total(self):
|
||||||
|
return sum(self.steps.values())
|
||||||
|
|
||||||
|
|
||||||
|
def derive_label(name):
|
||||||
|
"""TN-018 and TN-023: a label is derived from the local name, never typed."""
|
||||||
|
spaced = re.sub(r"(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])", " ", name)
|
||||||
|
return spaced[0].lower() + spaced[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def md5(path):
|
||||||
|
h = hashlib.md5()
|
||||||
|
with open(path, "rb") as fh:
|
||||||
|
for chunk in iter(lambda: fh.read(65536), b""):
|
||||||
|
h.update(chunk)
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def rename_node(g, old, new, report, step):
|
||||||
|
"""EV-001 applied to an instance: rewrite every triple naming the node."""
|
||||||
|
if (new, None, None) in g and (old, None, None) not in g:
|
||||||
|
return 0
|
||||||
|
n = 0
|
||||||
|
for s, p, o in list(g):
|
||||||
|
ns, no = (new if s == old else s), (new if o == old else o)
|
||||||
|
if (ns, no) != (s, o):
|
||||||
|
g.remove((s, p, o))
|
||||||
|
g.add((ns, p, no))
|
||||||
|
n += 1
|
||||||
|
report.add(step, n)
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def assign(g, pr, node, role, identifier, name, held_by, domain, spec, report, step):
|
||||||
|
"""Write one assignment. The guard tests the identifier, so a replay is a no-op."""
|
||||||
|
if (node, pr.identifier, Literal(identifier)) in g:
|
||||||
|
return False
|
||||||
|
g.add((node, RDF.type, role))
|
||||||
|
g.remove((node, pr.identifier, None))
|
||||||
|
g.add((node, pr.identifier, Literal(identifier)))
|
||||||
|
g.remove((node, pr.canonicalName, None))
|
||||||
|
g.add((node, pr.canonicalName, Literal(name)))
|
||||||
|
g.remove((node, pr.personName, None))
|
||||||
|
if held_by:
|
||||||
|
g.add((node, pr.personName, Literal(held_by)))
|
||||||
|
g.remove((node, pr.status, None))
|
||||||
|
g.add((node, pr.status, Literal(spec["status"])))
|
||||||
|
g.remove((node, pr.version, None))
|
||||||
|
g.add((node, pr.version, Literal(spec["version"])))
|
||||||
|
if domain is not None:
|
||||||
|
g.remove((node, pr.ownedByDomain, None))
|
||||||
|
g.add((node, pr.ownedByDomain, domain))
|
||||||
|
report.add(step, 1)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def check_version(g, spec, path):
|
||||||
|
"""EV-010 applied to a migration script: refuse to run against the wrong input.
|
||||||
|
|
||||||
|
A script announcing 2.1 -> 2.2 will happily work on a file still in 2.0 and
|
||||||
|
report success, leaving a state that is neither version. The same silent
|
||||||
|
failure the shapes are guarded against, one layer down.
|
||||||
|
"""
|
||||||
|
ns = spec["meta"]["namespace"]
|
||||||
|
expected = URIRef(ns.rstrip("/") + "/" + spec["meta"]["from_version"])
|
||||||
|
target = URIRef(ns.rstrip("/") + "/" + spec["meta"]["to_version"])
|
||||||
|
found = set(g.objects(None, OWL.versionIRI))
|
||||||
|
if target in found:
|
||||||
|
print("ALREADY AT %s — nothing to do." % spec["meta"]["to_version"])
|
||||||
|
sys.exit(0)
|
||||||
|
if expected not in found:
|
||||||
|
print("ABORT — %s declares %s; this script migrates from %s."
|
||||||
|
% (os.path.basename(path),
|
||||||
|
", ".join(sorted(str(f) for f in found)) or "no version",
|
||||||
|
expected))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(onto, data, spec, report):
|
||||||
|
ns, ins = spec["meta"]["namespace"], spec["meta"]["instance_namespace"]
|
||||||
|
pr, ex = Namespace(ns), Namespace(ins)
|
||||||
|
|
||||||
|
# 1 — declare heldBy on Actor (procedure C1)
|
||||||
|
for d in spec.get("declare_properties") or []:
|
||||||
|
term = pr[d["term"]]
|
||||||
|
if (term, RDF.type, getattr(OWL, d["kind"])) not in onto:
|
||||||
|
onto.add((term, RDF.type, getattr(OWL, d["kind"])))
|
||||||
|
onto.add((term, RDFS.label, Literal(derive_label(d["term"]))))
|
||||||
|
onto.add((term, RDFS.domain, pr[d["domain"]]))
|
||||||
|
onto.add((term, RDFS.range, URIRef(d["range"])))
|
||||||
|
onto.add((term, RDFS.comment, Literal(" ".join(d["comment"].split()))))
|
||||||
|
report.add("properties_declared", 1, d["term"])
|
||||||
|
|
||||||
|
# 2 — the short form of Data Steward is DST, and the rulebook now says so
|
||||||
|
for u in spec.get("display_updates") or []:
|
||||||
|
term = pr[u["term"]]
|
||||||
|
if (term, pr.acronym, Literal(u["acronym"])) not in onto:
|
||||||
|
onto.remove((term, pr.acronym, None))
|
||||||
|
onto.add((term, pr.acronym, Literal(u["acronym"])))
|
||||||
|
report.add("display_updated", 1, "%s -> %s" % (u["term"], u["acronym"]))
|
||||||
|
|
||||||
|
# 3 — rename the instance nodes that carried the wrong short form
|
||||||
|
for r in spec.get("instance_renames") or []:
|
||||||
|
rename_node(data, ex[r["from"]], ex[r["to"]], report, "instances_renamed")
|
||||||
|
|
||||||
|
# 4 — complete the assignments that already existed
|
||||||
|
for c in spec.get("complete") or []:
|
||||||
|
node = ex[c["node"]]
|
||||||
|
if (node, None, None) not in data:
|
||||||
|
report.add("complete_missing", 1, c["node"])
|
||||||
|
continue
|
||||||
|
role = next(iter(data.objects(node, RDF.type)), None)
|
||||||
|
assign(data, pr, node, role, c["identifier"], c["name"],
|
||||||
|
c["held_by"], ex[c["domain"]], spec, report, "assignments_completed")
|
||||||
|
|
||||||
|
# 5 — sub-domain owners
|
||||||
|
sdo_of = {}
|
||||||
|
for s in spec.get("create_sub_domain_owners") or []:
|
||||||
|
node = ex[s["node"]]
|
||||||
|
sdo_of[ex[s["sub_domain"]]] = node
|
||||||
|
if assign(data, pr, node, pr.DataSubDomainOwner, s["identifier"],
|
||||||
|
spec["sub_domain_owner_name"], s["held_by"], ex[s["domain"]],
|
||||||
|
spec, report, "sub_domain_owners_created"):
|
||||||
|
data.add((ex[s["sub_domain"]], pr.hasSubDomainOwner, node))
|
||||||
|
|
||||||
|
# 6 — product owners
|
||||||
|
for p in spec.get("create_product_owners") or []:
|
||||||
|
node = ex[p["node"]]
|
||||||
|
product = ex[p["product"]]
|
||||||
|
if (product, None, None) not in data:
|
||||||
|
report.add("product_missing", 1, p["product"])
|
||||||
|
continue
|
||||||
|
if assign(data, pr, node, pr.DataProductOwner, p["identifier"],
|
||||||
|
spec["product_owner_name"], p["held_by"], ex[p["domain"]],
|
||||||
|
spec, report, "product_owners_created"):
|
||||||
|
data.add((product, pr.hasProductOwner, node))
|
||||||
|
|
||||||
|
# 7 — create the business objects that close a governance chain
|
||||||
|
for b in spec.get("create_business_objects") or []:
|
||||||
|
node = ex[b["node"]]
|
||||||
|
if (node, pr.identifier, Literal(b["identifier"])) in data:
|
||||||
|
continue
|
||||||
|
data.add((node, RDF.type, pr.BusinessObject))
|
||||||
|
data.add((node, pr.identifier, Literal(b["identifier"])))
|
||||||
|
data.add((node, pr.canonicalName, Literal(b["name"])))
|
||||||
|
data.add((node, pr.isAbout, ex[b["is_about"]]))
|
||||||
|
data.add((node, pr.belongsTo, ex[b["sub_domain"]]))
|
||||||
|
data.add((node, pr.ownedByDomain, ex[b["domain"]]))
|
||||||
|
data.add((node, pr.status, Literal(spec["status"])))
|
||||||
|
data.add((node, pr.version, Literal(spec["version"])))
|
||||||
|
report.add("business_objects_created", 1, b["node"])
|
||||||
|
|
||||||
|
# 8 — one steward assignment per business object, derived from the graph
|
||||||
|
holders = spec.get("steward_holders") or {}
|
||||||
|
steward_of = {}
|
||||||
|
for bo in sorted(data.subjects(RDF.type, pr.BusinessObject), key=str):
|
||||||
|
ident = next((str(v) for v in data.objects(bo, pr.identifier)), None)
|
||||||
|
if not ident:
|
||||||
|
report.add("steward_no_identifier", 1, str(bo).replace(ins, ""))
|
||||||
|
continue
|
||||||
|
suffix = ident.split("-", 1)[1] # 06.01-001
|
||||||
|
domain_key = "DD_" + suffix.split(".", 1)[0] # DD_06
|
||||||
|
holder = holders.get(domain_key)
|
||||||
|
if holder is None:
|
||||||
|
report.add("steward_no_holder", 1, domain_key)
|
||||||
|
continue
|
||||||
|
node = ex["DST_" + suffix.replace(".", "_").replace("-", "_")]
|
||||||
|
steward_of[bo] = node
|
||||||
|
if assign(data, pr, node, pr.DataSteward, "DST-" + suffix,
|
||||||
|
spec["steward_name"], holder, ex[domain_key],
|
||||||
|
spec, report, "stewards_created"):
|
||||||
|
for _, _, old in list(data.triples((bo, pr.monitoredBy, None))):
|
||||||
|
data.remove((bo, pr.monitoredBy, old))
|
||||||
|
data.add((bo, pr.monitoredBy, node))
|
||||||
|
|
||||||
|
# 9 — resolve every ownedBy pointing at a per-domain steward node
|
||||||
|
res = spec.get("owned_by_resolution") or {}
|
||||||
|
old_stewards = {ex[r["from"]] for r in (spec.get("instance_renames") or [])} | \
|
||||||
|
{ex[r["to"]] for r in (spec.get("instance_renames") or [])}
|
||||||
|
|
||||||
|
def business_object_of(subject):
|
||||||
|
"""Follow the declared chains until a business object is reached."""
|
||||||
|
for chain in res.get("chains") or []:
|
||||||
|
prop = pr[chain["via"]]
|
||||||
|
if chain["direction"] == "inbound":
|
||||||
|
for candidate in data.subjects(prop, subject):
|
||||||
|
if (candidate, RDF.type, pr.BusinessObject) in data:
|
||||||
|
return candidate
|
||||||
|
else:
|
||||||
|
for target in data.objects(subject, prop):
|
||||||
|
if (target, RDF.type, pr.BusinessObject) in data:
|
||||||
|
return target
|
||||||
|
for candidate in data.subjects(pr.isAbout, target):
|
||||||
|
if (candidate, RDF.type, pr.BusinessObject) in data:
|
||||||
|
return candidate
|
||||||
|
return None
|
||||||
|
|
||||||
|
remove_on = {pr[c] for c in (res.get("remove_on") or [])}
|
||||||
|
for subject, _, target in list(data.triples((None, pr.ownedBy, None))):
|
||||||
|
if target not in old_stewards:
|
||||||
|
continue
|
||||||
|
types = set(data.objects(subject, RDF.type))
|
||||||
|
if types & remove_on:
|
||||||
|
data.remove((subject, pr.ownedBy, target))
|
||||||
|
report.add("ownedby_removed", 1)
|
||||||
|
continue
|
||||||
|
if pr.DataSubDomain in types and res.get("repoint_sub_domains"):
|
||||||
|
owner = sdo_of.get(subject)
|
||||||
|
if owner is None:
|
||||||
|
report.add("ownedby_unresolved", 1,
|
||||||
|
"%s has no sub-domain owner" % str(subject).replace(ins, ""))
|
||||||
|
continue
|
||||||
|
data.remove((subject, pr.ownedBy, target))
|
||||||
|
data.add((subject, pr.ownedBy, owner))
|
||||||
|
report.add("ownedby_repointed_to_sdo", 1)
|
||||||
|
continue
|
||||||
|
bo = business_object_of(subject)
|
||||||
|
owner = steward_of.get(bo) if bo is not None else None
|
||||||
|
if owner is None:
|
||||||
|
report.add("ownedby_unresolved", 1,
|
||||||
|
"%s reaches no business object" % str(subject).replace(ins, ""))
|
||||||
|
continue
|
||||||
|
data.remove((subject, pr.ownedBy, target))
|
||||||
|
data.add((subject, pr.ownedBy, owner))
|
||||||
|
report.add("ownedby_repointed_to_steward", 1)
|
||||||
|
|
||||||
|
# 10 — withdraw the per-domain steward nodes, once nothing names them (EV-003)
|
||||||
|
if spec.get("retire_domain_stewards"):
|
||||||
|
for r in spec.get("instance_renames") or []:
|
||||||
|
node = ex[r["to"]]
|
||||||
|
referrers = {s for s in data.subjects(None, node)}
|
||||||
|
if referrers:
|
||||||
|
report.add("steward_still_referenced", 1,
|
||||||
|
"%s cited by %d subject(s)" % (r["to"], len(referrers)))
|
||||||
|
continue
|
||||||
|
n = 0
|
||||||
|
for t in list(data.triples((node, None, None))):
|
||||||
|
data.remove(t)
|
||||||
|
n += 1
|
||||||
|
if n:
|
||||||
|
report.add("domain_stewards_retired", 1, r["to"])
|
||||||
|
|
||||||
|
# 11 — bump the version
|
||||||
|
target = URIRef(ns.rstrip("/") + "/" + spec["meta"]["to_version"])
|
||||||
|
for o in set(onto.subjects(RDF.type, OWL.Ontology)):
|
||||||
|
if (o, OWL.versionIRI, target) not in onto:
|
||||||
|
onto.remove((o, OWL.versionIRI, None))
|
||||||
|
onto.add((o, OWL.versionIRI, target))
|
||||||
|
report.add("version_bumped", 1, str(target))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--apply", action="store_true")
|
||||||
|
ap.add_argument("--ontology", required=True)
|
||||||
|
ap.add_argument("--instances", required=True)
|
||||||
|
ap.add_argument("--log-dir", default=os.path.join(HERE, "logs"))
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
for path in (args.ontology, args.instances):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
print("MISSING INPUT: %s" % path)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
spec = yaml.safe_load(open(SPEC, encoding="utf-8"))
|
||||||
|
checksums = OrderedDict((p, md5(p)) for p in (args.ontology, args.instances))
|
||||||
|
|
||||||
|
onto, data = Graph(), Graph()
|
||||||
|
onto.parse(args.ontology, format="turtle")
|
||||||
|
data.parse(args.instances, format="turtle")
|
||||||
|
check_version(onto, spec, args.ontology)
|
||||||
|
|
||||||
|
before = (len(onto), len(data))
|
||||||
|
|
||||||
|
report = Report()
|
||||||
|
migrate(onto, data, spec, report)
|
||||||
|
after = (len(onto), len(data))
|
||||||
|
|
||||||
|
print("MIGRATION %s -> %s %s"
|
||||||
|
% (spec["meta"]["from_version"], spec["meta"]["to_version"],
|
||||||
|
"APPLY" if args.apply else "DRY RUN"))
|
||||||
|
print()
|
||||||
|
for step, n in report.steps.items():
|
||||||
|
print(" %-28s %6d" % (step, n))
|
||||||
|
print(" %-28s %6d" % ("total changes", report.total))
|
||||||
|
print()
|
||||||
|
for path, b, a in zip((args.ontology, args.instances), before, after):
|
||||||
|
print(" %-46s %6d -> %6d triples" % (os.path.basename(path), b, a))
|
||||||
|
if report.notes:
|
||||||
|
print()
|
||||||
|
for n in report.notes:
|
||||||
|
print(" note: " + n)
|
||||||
|
|
||||||
|
os.makedirs(args.log_dir, exist_ok=True)
|
||||||
|
stamp = datetime.datetime.now().strftime("%Y%m%dT%H%M%S")
|
||||||
|
log_path = os.path.join(args.log_dir, "migration_v2_2_%s.json" % stamp)
|
||||||
|
json.dump({"attempt_timestamp": datetime.datetime.now().isoformat(timespec="seconds"),
|
||||||
|
"mode": "apply" if args.apply else "dry-run",
|
||||||
|
"from_version": spec["meta"]["from_version"],
|
||||||
|
"to_version": spec["meta"]["to_version"],
|
||||||
|
"input_checksums": checksums,
|
||||||
|
"steps": report.steps, "total_changes": report.total,
|
||||||
|
"triples_before": dict(zip((args.ontology, args.instances), before)),
|
||||||
|
"triples_after": dict(zip((args.ontology, args.instances), after)),
|
||||||
|
"notes": report.notes},
|
||||||
|
open(log_path, "w", encoding="utf-8"), indent=2)
|
||||||
|
print("\n log: %s" % log_path)
|
||||||
|
|
||||||
|
if not args.apply:
|
||||||
|
print("\n DRY RUN — nothing written. Re-run with --apply when the counts "
|
||||||
|
"above are what you expect.")
|
||||||
|
return
|
||||||
|
|
||||||
|
onto.serialize(destination=args.ontology, format="turtle")
|
||||||
|
data.serialize(destination=args.instances, format="turtle")
|
||||||
|
print("\n written. Replay this script now: a second run must report zero "
|
||||||
|
"changes (EV-006).")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# NAMING LEXICON
|
||||||
|
# =============================================================================
|
||||||
|
# The word knowledge the naming checks need and cannot derive. No script can
|
||||||
|
# decide on its own that "qualified" is a participle and "ordinal" an adjective.
|
||||||
|
#
|
||||||
|
# Anything absent from this file is not a violation: it is reported as REVIEW.
|
||||||
|
# A checker that guessed would either block on false positives or, worse, stay
|
||||||
|
# silent on real ones. Adding a word here is a deliberate act, reviewed like any
|
||||||
|
# other change to the vocabulary.
|
||||||
|
#
|
||||||
|
# Consumed by check_tbox_naming.py.
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# VERBS — TN-009. A relation begins with one of these.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
active_verbs:
|
||||||
|
- belongs
|
||||||
|
- computes
|
||||||
|
- consumes
|
||||||
|
- constrains
|
||||||
|
- contains
|
||||||
|
- depends
|
||||||
|
- exposes
|
||||||
|
- has
|
||||||
|
- lives
|
||||||
|
- measures
|
||||||
|
- packages
|
||||||
|
- produces
|
||||||
|
- references
|
||||||
|
- represents
|
||||||
|
- serves
|
||||||
|
- sources
|
||||||
|
- stores
|
||||||
|
- uses
|
||||||
|
|
||||||
|
# A past participle is a verb in first position (TN-009). Listed separately
|
||||||
|
# because the two forms read differently on an edge: the active says what the
|
||||||
|
# subject does, the participial what was done to it.
|
||||||
|
participles:
|
||||||
|
- computed
|
||||||
|
- derived
|
||||||
|
- governed
|
||||||
|
- hosted
|
||||||
|
- monitored
|
||||||
|
- operated
|
||||||
|
- owned
|
||||||
|
- packaged
|
||||||
|
- qualified
|
||||||
|
- served
|
||||||
|
- sourced
|
||||||
|
- stored
|
||||||
|
|
||||||
|
# The copula, admitted by TN-010 on object properties only.
|
||||||
|
copula:
|
||||||
|
- is
|
||||||
|
|
||||||
|
# TN-009: an adverb qualifying the verb is transparent. The checker skips it and
|
||||||
|
# tests the token that follows.
|
||||||
|
adverbs:
|
||||||
|
- primarily
|
||||||
|
- fully
|
||||||
|
- partially
|
||||||
|
- directly
|
||||||
|
- initially
|
||||||
|
- currently
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# ACRONYMS — TN-002 and TN-019.
|
||||||
|
# Forbidden in an IRI, with the single exception of TN-011: a relation
|
||||||
|
# reproducing the short label of the class it targets carries it as it stands.
|
||||||
|
# Listed so that the checker names what it found rather than reporting an
|
||||||
|
# anonymous run of capitals.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
known_acronyms:
|
||||||
|
BI: Business Intelligence
|
||||||
|
KPI: Key Performance Indicator
|
||||||
|
DD: Data Domain
|
||||||
|
SD: Data Sub Domain
|
||||||
|
BO: Business Object
|
||||||
|
DDO: Data Domain Owner
|
||||||
|
SDO: Data Sub Domain Owner
|
||||||
|
PO: Data Product Owner
|
||||||
|
DGL: Data Governance Lead
|
||||||
|
URI: Uniform Resource Identifier
|
||||||
|
URL: Uniform Resource Locator
|
||||||
|
API: Application Programming Interface
|
||||||
|
SQL: Structured Query Language
|
||||||
|
ETL: Extract Transform Load
|
||||||
|
UAT: User Acceptance Testing
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# TYPE WORDS — TN-005. A class name carries none of these unless the word names
|
||||||
|
# a genuine abstraction of the model, which the exceptions below record.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type_words:
|
||||||
|
- Entity
|
||||||
|
- Item
|
||||||
|
- Element
|
||||||
|
- Thing
|
||||||
|
- Record
|
||||||
|
|
||||||
|
type_word_exceptions:
|
||||||
|
- MetaModelObject
|
||||||
|
- DefinedObject
|
||||||
|
- CapturedObject
|
||||||
|
- OwnershipLayerObject
|
||||||
|
- BusinessLayerObject
|
||||||
|
- LogicalLayerObject
|
||||||
|
- PhysicalLayerObject
|
||||||
|
- DeliveryLayerObject
|
||||||
|
- ConsumptionLayerObject
|
||||||
|
- GovernanceLayerObject
|
||||||
|
- BusinessObject
|
||||||
|
- DataElement
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# PLURALS — TN-005. A class name is singular. English plural detection is
|
||||||
|
# unreliable, so the checker reports a suspected plural as REVIEW and this list
|
||||||
|
# silences the words that only look plural.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
not_plural:
|
||||||
|
- Status
|
||||||
|
- Address
|
||||||
|
- Business
|
||||||
|
- Analysis
|
||||||
|
- Schema
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# RELATIONAL SUFFIXES — TN-014. Reserved for object properties. An attribute
|
||||||
|
# ending in one of these reads as an edge and will be mistaken for one.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
relational_suffixes:
|
||||||
|
- By
|
||||||
|
- In
|
||||||
|
- From
|
||||||
|
- On
|
||||||
|
- To
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# DATE RANGES — TN-013. A property of one of these ranges is a date attribute
|
||||||
|
# and takes the nominal form.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
date_ranges:
|
||||||
|
- "http://www.w3.org/2001/XMLSchema#date"
|
||||||
|
- "http://www.w3.org/2001/XMLSchema#dateTime"
|
||||||
+48
-24
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
meta:
|
meta:
|
||||||
title: Pernod Ricard Data MetaModel — T-Box Rulebook
|
title: Pernod Ricard Data MetaModel — T-Box Rulebook
|
||||||
version: "1.2"
|
version: "1.4"
|
||||||
status: Draft for review
|
status: Draft for review
|
||||||
date: "2026-08-03"
|
date: "2026-08-03"
|
||||||
scope: >
|
scope: >
|
||||||
@@ -249,8 +249,9 @@ rules:
|
|||||||
statement: >
|
statement: >
|
||||||
The local name of an object property begins with a verb, in lower case. Both
|
The local name of an object property begins with a verb, in lower case. Both
|
||||||
the active form and the passive or participial form are admitted: a past
|
the active form and the passive or participial form are admitted: a past
|
||||||
participle is a verb in first position. No relation begins with a
|
participle is a verb in first position. An adverb qualifying that verb is
|
||||||
preposition or with a noun.
|
transparent and may precede it. No relation begins with a preposition or
|
||||||
|
with a noun.
|
||||||
scope: [object_property]
|
scope: [object_property]
|
||||||
severity: BLOCKING
|
severity: BLOCKING
|
||||||
control: {tier: script, executor: check_tbox_naming.py, procedure: null}
|
control: {tier: script, executor: check_tbox_naming.py, procedure: null}
|
||||||
@@ -258,12 +259,14 @@ rules:
|
|||||||
rationale: >
|
rationale: >
|
||||||
A relation reads as a verb and an attribute reads as a noun. The distinction
|
A relation reads as a verb and an attribute reads as a noun. The distinction
|
||||||
is what lets a reader tell an edge from a field without opening the
|
is what lets a reader tell an edge from a field without opening the
|
||||||
declaration. The explicit clause on participial forms is required: read
|
declaration. Two clauses are stated rather than left implicit, because a
|
||||||
literally, a verb-first rule would condemn a whole family of sound relations
|
literal reading would condemn sound relations: the participial one, which
|
||||||
such as computedBy, storedIn and derivedFrom.
|
covers a whole family such as computedBy, storedIn and derivedFrom, and the
|
||||||
|
adverbial one, which covers a relation whose verb is qualified rather than
|
||||||
|
replaced.
|
||||||
examples:
|
examples:
|
||||||
- {from: "pr:inDatabase", to: "pr:isInDatabase", note: a preposition is not a verb}
|
- {from: "pr:inDatabase", to: "pr:isInDatabase", note: a preposition is not a verb}
|
||||||
- {from: "pr:primaryLocation", to: "pr:primarilyStoredIn", note: a noun is not a verb}
|
- {from: "pr:primaryLocation", to: "pr:primarilyStoredIn", note: "a noun is not a verb; the adverb is transparent and the verb follows"}
|
||||||
- {from: "pr:computedBy", to: "pr:computedBy", note: participial form is conforming}
|
- {from: "pr:computedBy", to: "pr:computedBy", note: participial form is conforming}
|
||||||
|
|
||||||
- id: TN-010
|
- id: TN-010
|
||||||
@@ -596,9 +599,9 @@ rules:
|
|||||||
title: Every property declares its domain and range
|
title: Every property declares its domain and range
|
||||||
statement: >
|
statement: >
|
||||||
rdfs:domain and rdfs:range are mandatory on every property, EXCEPT where the
|
rdfs:domain and rdfs:range are mandatory on every property, EXCEPT where the
|
||||||
property is deliberately polymorphic. A polymorphic property states so in
|
property is deliberately polymorphic. A polymorphic property declares
|
||||||
its comment and has its scope declared in SHACL. Silent absence is a
|
pr:polymorphic true and has its scope declared in SHACL. Silent absence is a
|
||||||
violation; documented absence is not.
|
violation; declared absence is not.
|
||||||
scope: [object_property, datatype_property]
|
scope: [object_property, datatype_property]
|
||||||
severity: BLOCKING
|
severity: BLOCKING
|
||||||
control: {tier: [script, human], executor: check_tbox_naming.py, procedure: null}
|
control: {tier: [script, human], executor: check_tbox_naming.py, procedure: null}
|
||||||
@@ -608,30 +611,51 @@ rules:
|
|||||||
properties are polymorphic by design, their scope controlled class by class
|
properties are polymorphic by design, their scope controlled class by class
|
||||||
in SHACL rather than by twin properties; giving those an rdfs:domain would
|
in SHACL rather than by twin properties; giving those an rdfs:domain would
|
||||||
trigger the RDFS retyping described in TN-028. The rule therefore separates
|
trigger the RDFS retyping described in TN-028. The rule therefore separates
|
||||||
the two cases rather than demanding a domain everywhere.
|
the two cases rather than demanding a domain everywhere. The distinction is
|
||||||
|
carried by an axiom and not by a sentence in the comment: a checker looking
|
||||||
|
for a phrase in prose fails the moment someone words it differently, and
|
||||||
|
what matters is declared rather than read, exactly as for abstractness.
|
||||||
examples:
|
examples:
|
||||||
- {from: a polymorphic property with no domain and no comment, to: violation, note: silence is indistinguishable from omission}
|
- {from: a property with no domain and no declaration, to: violation, note: silence is indistinguishable from omission}
|
||||||
- {from: a polymorphic property with no domain, documented, to: conforming, note: scope declared in SHACL}
|
- {from: "a property with no domain, declaring pr:polymorphic true", to: conforming, note: scope declared in SHACL}
|
||||||
|
|
||||||
- id: TN-026
|
- id: TN-026
|
||||||
category: Declaration
|
category: Declaration
|
||||||
title: Every term declares how it was authored
|
title: Every concrete class declares how its instances are produced
|
||||||
statement: >
|
statement: >
|
||||||
pr:authoringMode is mandatory on every term. pr:harvestSource is mandatory
|
Every CONCRETE CLASS declares pr:authoringMode, stating whether its
|
||||||
if and only if the mode is HARVESTED.
|
instances are ASSERTED by governance or HARVESTED from a system.
|
||||||
scope: [class, object_property, datatype_property, annotation_property]
|
pr:harvestSource is mandatory if and only if the mode is HARVESTED. The
|
||||||
|
declared mode agrees with the provenance axis the class descends from: a
|
||||||
|
class under CapturedObject declares HARVESTED, one under DefinedObject
|
||||||
|
declares ASSERTED. Nothing else carries the property: not an abstract class,
|
||||||
|
not a property, not a term of the vocabulary in its own right.
|
||||||
|
scope: [class]
|
||||||
severity: BLOCKING
|
severity: BLOCKING
|
||||||
control: {tier: script, executor: check_tbox_naming.py, procedure: null}
|
control: {tier: script, executor: check_tbox_naming.py, procedure: null}
|
||||||
filiation: null
|
filiation: null
|
||||||
rationale: >
|
rationale: >
|
||||||
Provenance decides who may edit a term and what a divergence means. A term
|
A term of the vocabulary is declared, always and by definition; recording
|
||||||
stating where its data comes from without stating that it is harvested, or
|
that on every term would repeat one fact a hundred times and say nothing. It
|
||||||
declaring itself harvested without naming a source, is half-declared in a
|
is the INSTANCES that have a provenance, and the class is where the model
|
||||||
way no control can catch. Declared symmetrically, provenance also makes a
|
states which one: a governance object is asserted by a domain, a physical
|
||||||
harvester specifiable from the model itself rather than from a side
|
column is harvested from a system. The distinction matters because a class
|
||||||
document.
|
can be declared and describe captured things at once — CapturedObject itself
|
||||||
|
is a declared term naming harvested instances, and conflating the two is
|
||||||
|
what once put a harvesting mode on an abstraction.
|
||||||
|
|
||||||
|
Nothing is inherited. A subclass declares its own mode rather than taking
|
||||||
|
its parent's, so that the declaration is read where the instances are typed
|
||||||
|
and not chased up a chain. The agreement clause is what keeps that free of
|
||||||
|
contradiction: the axis says what kind of thing the class describes, the
|
||||||
|
mode says how those things arrive, and a class under CapturedObject
|
||||||
|
declaring ASSERTED is stating both at once.
|
||||||
|
|
||||||
|
Declared this way, provenance also makes a harvester specifiable from the
|
||||||
|
model itself rather than from a side document.
|
||||||
examples:
|
examples:
|
||||||
- {from: harvestSource present, authoringMode absent, to: "authoringMode HARVESTED", note: null}
|
- {from: "authoringMode on an abstract class", to: nothing, note: an abstraction has no instances to produce}
|
||||||
|
- {from: "a class under CapturedObject declaring ASSERTED", to: violation, note: the mode contradicts the axis}
|
||||||
- {from: "authoringMode HARVESTED, harvestSource absent", to: harvestSource declared, note: null}
|
- {from: "authoringMode HARVESTED, harvestSource absent", to: harvestSource declared, note: null}
|
||||||
|
|
||||||
- id: TN-027
|
- id: TN-027
|
||||||
|
|||||||
+394
-134
@@ -7,8 +7,7 @@ ex:BO_04_02_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Outlet" ;
|
pr:canonicalName "Outlet" ;
|
||||||
pr:identifier "BO-04.02-001" ;
|
pr:identifier "BO-04.02-001" ;
|
||||||
pr:isAbout ex:BC_04_01_002 ;
|
pr:isAbout ex:BC_04_01_002 ;
|
||||||
pr:monitoredBy ex:ST_DD_04 ;
|
pr:monitoredBy ex:DST_04_02_001 ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -19,8 +18,7 @@ ex:BO_04_03_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Country" ;
|
pr:canonicalName "Country" ;
|
||||||
pr:identifier "BO-04.03-001" ;
|
pr:identifier "BO-04.03-001" ;
|
||||||
pr:isAbout ex:BC_04_03_001 ;
|
pr:isAbout ex:BC_04_03_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_04 ;
|
pr:monitoredBy ex:DST_04_03_001 ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -30,8 +28,7 @@ ex:BO_05_01_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Promotion Type" ;
|
pr:canonicalName "Promotion Type" ;
|
||||||
pr:identifier "BO-05.01-001" ;
|
pr:identifier "BO-05.01-001" ;
|
||||||
pr:isAbout ex:BC_05_01_001 ;
|
pr:isAbout ex:BC_05_01_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_05 ;
|
pr:monitoredBy ex:DST_05_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_05 ;
|
|
||||||
pr:ownedByDomain ex:DD_05 ;
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -42,8 +39,7 @@ ex:BO_06_01_002 a pr:BusinessObject ;
|
|||||||
pr:hasMetric ex:M_06_01_002 ;
|
pr:hasMetric ex:M_06_01_002 ;
|
||||||
pr:identifier "BO-06.01-002" ;
|
pr:identifier "BO-06.01-002" ;
|
||||||
pr:isAbout ex:BC_06_01_001 ;
|
pr:isAbout ex:BC_06_01_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_06 ;
|
pr:monitoredBy ex:DST_06_01_002 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:usesConcept ex:BC_05_01_001,
|
pr:usesConcept ex:BC_05_01_001,
|
||||||
@@ -59,8 +55,7 @@ ex:BO_06_01_004 a pr:BusinessObject ;
|
|||||||
ex:M_06_01_008 ;
|
ex:M_06_01_008 ;
|
||||||
pr:identifier "BO-06.01-004" ;
|
pr:identifier "BO-06.01-004" ;
|
||||||
pr:isAbout ex:BC_06_01_002 ;
|
pr:isAbout ex:BC_06_01_002 ;
|
||||||
pr:monitoredBy ex:ST_DD_06 ;
|
pr:monitoredBy ex:DST_06_01_004 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:usesConcept ex:BC_06_01_001,
|
pr:usesConcept ex:BC_06_01_001,
|
||||||
@@ -74,22 +69,30 @@ ex:BO_06_01_005 a pr:BusinessObject ;
|
|||||||
ex:M_06_01_010 ;
|
ex:M_06_01_010 ;
|
||||||
pr:identifier "BO-06.01-005" ;
|
pr:identifier "BO-06.01-005" ;
|
||||||
pr:isAbout ex:BC_06_01_005 ;
|
pr:isAbout ex:BC_06_01_005 ;
|
||||||
pr:monitoredBy ex:ST_DD_06 ;
|
pr:monitoredBy ex:DST_06_01_005 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:usesConcept ex:BC_05_01_001,
|
pr:usesConcept ex:BC_05_01_001,
|
||||||
ex:BC_06_01_001 ;
|
ex:BC_06_01_001 ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
|
ex:BO_06_01_006 a pr:BusinessObject ;
|
||||||
|
pr:belongsTo ex:SD_06_01 ;
|
||||||
|
pr:canonicalName "Panel Coverage" ;
|
||||||
|
pr:identifier "BO-06.01-006" ;
|
||||||
|
pr:isAbout ex:BC_06_01_003 ;
|
||||||
|
pr:monitoredBy ex:DST_06_01_006 ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:BO_16_01_002 a pr:BusinessObject ;
|
ex:BO_16_01_002 a pr:BusinessObject ;
|
||||||
pr:arbitrationStatus "TO_ARBITRATE" ;
|
pr:arbitrationStatus "TO_ARBITRATE" ;
|
||||||
pr:belongsTo ex:SD_16_01 ;
|
pr:belongsTo ex:SD_16_01 ;
|
||||||
pr:canonicalName "Exchange Rate" ;
|
pr:canonicalName "Exchange Rate" ;
|
||||||
pr:identifier "BO-16.01-002" ;
|
pr:identifier "BO-16.01-002" ;
|
||||||
pr:isAbout ex:BC_16_01_002 ;
|
pr:isAbout ex:BC_16_01_002 ;
|
||||||
pr:monitoredBy ex:ST_DD_16 ;
|
pr:monitoredBy ex:DST_16_01_002 ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -100,8 +103,7 @@ ex:BO_16_02_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Fiscal Period" ;
|
pr:canonicalName "Fiscal Period" ;
|
||||||
pr:identifier "BO-16.02-001" ;
|
pr:identifier "BO-16.02-001" ;
|
||||||
pr:isAbout ex:BC_16_02_001 ;
|
pr:isAbout ex:BC_16_02_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_16 ;
|
pr:monitoredBy ex:DST_16_02_001 ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -112,8 +114,7 @@ ex:BO_16_03_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Marketing Entity" ;
|
pr:canonicalName "Marketing Entity" ;
|
||||||
pr:identifier "BO-16.03-001" ;
|
pr:identifier "BO-16.03-001" ;
|
||||||
pr:isAbout ex:BC_16_03_001 ;
|
pr:isAbout ex:BC_16_03_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_16 ;
|
pr:monitoredBy ex:DST_16_03_001 ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -123,6 +124,7 @@ ex:DP_06_01_001 a pr:DataProduct ;
|
|||||||
pr:exposes ex:DI_06_01_001,
|
pr:exposes ex:DI_06_01_001,
|
||||||
ex:DI_06_01_002 ;
|
ex:DI_06_01_002 ;
|
||||||
pr:governedBy ex:DC_06_01_001 ;
|
pr:governedBy ex:DC_06_01_001 ;
|
||||||
|
pr:hasProductOwner ex:PO_SODH ;
|
||||||
pr:identifier "DP-06.01-001" ;
|
pr:identifier "DP-06.01-001" ;
|
||||||
pr:operatedBy ex:SD_06_01 ;
|
pr:operatedBy ex:SD_06_01 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
@@ -142,8 +144,7 @@ ex:BO_04_01_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Customer Hierarchy" ;
|
pr:canonicalName "Customer Hierarchy" ;
|
||||||
pr:identifier "BO-04.01-001" ;
|
pr:identifier "BO-04.01-001" ;
|
||||||
pr:isAbout ex:BC_04_01_001 ;
|
pr:isAbout ex:BC_04_01_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_04 ;
|
pr:monitoredBy ex:DST_04_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -154,8 +155,7 @@ ex:BO_06_01_001 a pr:BusinessObject ;
|
|||||||
pr:hasMetric ex:M_06_01_001 ;
|
pr:hasMetric ex:M_06_01_001 ;
|
||||||
pr:identifier "BO-06.01-001" ;
|
pr:identifier "BO-06.01-001" ;
|
||||||
pr:isAbout ex:BC_06_01_001 ;
|
pr:isAbout ex:BC_06_01_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_06 ;
|
pr:monitoredBy ex:DST_06_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:usesConcept ex:BC_05_01_001,
|
pr:usesConcept ex:BC_05_01_001,
|
||||||
@@ -170,8 +170,7 @@ ex:BO_06_01_003 a pr:BusinessObject ;
|
|||||||
ex:M_06_01_005 ;
|
ex:M_06_01_005 ;
|
||||||
pr:identifier "BO-06.01-003" ;
|
pr:identifier "BO-06.01-003" ;
|
||||||
pr:isAbout ex:BC_06_01_004 ;
|
pr:isAbout ex:BC_06_01_004 ;
|
||||||
pr:monitoredBy ex:ST_DD_06 ;
|
pr:monitoredBy ex:DST_06_01_003 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:usesConcept ex:BC_06_01_003 ;
|
pr:usesConcept ex:BC_06_01_003 ;
|
||||||
@@ -182,8 +181,7 @@ ex:BO_10_01_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Product Hierarchy" ;
|
pr:canonicalName "Product Hierarchy" ;
|
||||||
pr:identifier "BO-10.01-001" ;
|
pr:identifier "BO-10.01-001" ;
|
||||||
pr:isAbout ex:BC_10_01_001 ;
|
pr:isAbout ex:BC_10_01_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_10 ;
|
pr:monitoredBy ex:DST_10_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_10 ;
|
|
||||||
pr:ownedByDomain ex:DD_10 ;
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -193,8 +191,7 @@ ex:BO_16_01_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Currency" ;
|
pr:canonicalName "Currency" ;
|
||||||
pr:identifier "BO-16.01-001" ;
|
pr:identifier "BO-16.01-001" ;
|
||||||
pr:isAbout ex:BC_16_01_001 ;
|
pr:isAbout ex:BC_16_01_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_16 ;
|
pr:monitoredBy ex:DST_16_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -204,38 +201,67 @@ ex:BO_21_01_001 a pr:BusinessObject ;
|
|||||||
pr:canonicalName "Analysis Period" ;
|
pr:canonicalName "Analysis Period" ;
|
||||||
pr:identifier "BO-21.01-001" ;
|
pr:identifier "BO-21.01-001" ;
|
||||||
pr:isAbout ex:BC_21_01_001 ;
|
pr:isAbout ex:BC_21_01_001 ;
|
||||||
pr:monitoredBy ex:ST_DD_21 ;
|
pr:monitoredBy ex:DST_21_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_21 ;
|
|
||||||
pr:ownedByDomain ex:DD_21 ;
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:DC_06_01_001 a pr:DataContract ;
|
ex:DC_06_01_001 a pr:DataContract ;
|
||||||
pr:businessRule "Refreshed weekly, available Tuesday 06:00 CET. Panel coverage published with every load. Schema stable within a minor version; 60 days notice on any breaking change. Support through the Sales Performance data team." ;
|
|
||||||
pr:canonicalName "Sell Out Data Hub - Gold service commitment" ;
|
pr:canonicalName "Sell Out Data Hub - Gold service commitment" ;
|
||||||
pr:identifier "DC-06.01-001" ;
|
pr:identifier "DC-06.01-001" ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:serviceCommitment "Refreshed weekly, available Tuesday 06:00 CET. Panel coverage published with every load. Schema stable within a minor version; 60 days notice on any breaking change. Support through the Sales Performance data team." ;
|
||||||
pr:shortLabel "SODH Gold contract" ;
|
pr:shortLabel "SODH Gold contract" ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:DDO_DD_04 a pr:DataDomainOwner ;
|
ex:DDO_DD_04 a pr:DataDomainOwner ;
|
||||||
pr:canonicalName "David Carpio" .
|
pr:canonicalName "Data Domain Owner" ;
|
||||||
|
pr:identifier "DDO-04" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "David Carpio" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DDO_DD_05 a pr:DataDomainOwner ;
|
ex:DDO_DD_05 a pr:DataDomainOwner ;
|
||||||
pr:canonicalName "David Carpio" .
|
pr:canonicalName "Data Domain Owner" ;
|
||||||
|
pr:identifier "DDO-05" ;
|
||||||
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
|
pr:personName "David Carpio" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DDO_DD_06 a pr:DataDomainOwner ;
|
ex:DDO_DD_06 a pr:DataDomainOwner ;
|
||||||
pr:canonicalName "Ludovic Cottier" .
|
pr:canonicalName "Data Domain Owner" ;
|
||||||
|
pr:identifier "DDO-06" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Ludovic Cottier" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DDO_DD_10 a pr:DataDomainOwner ;
|
ex:DDO_DD_10 a pr:DataDomainOwner ;
|
||||||
pr:canonicalName "Alberto Lupano" .
|
pr:canonicalName "Data Domain Owner" ;
|
||||||
|
pr:identifier "DDO-10" ;
|
||||||
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
|
pr:personName "Alberto Lupano" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DDO_DD_16 a pr:DataDomainOwner ;
|
ex:DDO_DD_16 a pr:DataDomainOwner ;
|
||||||
pr:canonicalName "Julien Soisson" .
|
pr:canonicalName "Data Domain Owner" ;
|
||||||
|
pr:identifier "DDO-16" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Julien Soisson" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DDO_DD_21 a pr:DataDomainOwner ;
|
ex:DDO_DD_21 a pr:DataDomainOwner ;
|
||||||
pr:canonicalName "Vincent Meunier" .
|
pr:canonicalName "Data Domain Owner" ;
|
||||||
|
pr:identifier "DDO-21" ;
|
||||||
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
|
pr:personName "Vincent Meunier" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DE_04_01_0001 a pr:DataElement ;
|
ex:DE_04_01_0001 a pr:DataElement ;
|
||||||
pr:canonicalName "Distributor Code" ;
|
pr:canonicalName "Distributor Code" ;
|
||||||
@@ -608,22 +634,18 @@ ex:DE_21_01_0005 a pr:DataElement ;
|
|||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:DI_06_01_001 a pr:DataInterface ;
|
ex:DI_06_01_001 a pr:DataInterface ;
|
||||||
pr:businessDefinition "Read access to the Gold layer through Snowflake grants. The channel BI tools and analysts consume."@en ;
|
|
||||||
pr:canonicalName "SODH Gold consumption schema" ;
|
pr:canonicalName "SODH Gold consumption schema" ;
|
||||||
pr:identifier "DI-06.01-001" ;
|
pr:identifier "DI-06.01-001" ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:shortLabel "Consumption schema" ;
|
pr:shortLabel "Consumption schema" ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:DI_06_01_002 a pr:DataInterface ;
|
ex:DI_06_01_002 a pr:DataInterface ;
|
||||||
pr:businessDefinition "File extract delivered to the panel provider each week. Outside Snowflake, and the boundary where lineage leaves the warehouse."@en ;
|
|
||||||
pr:canonicalName "Panel partner weekly extract" ;
|
pr:canonicalName "Panel partner weekly extract" ;
|
||||||
pr:identifier "DI-06.01-002" ;
|
pr:identifier "DI-06.01-002" ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:shortLabel "Panel extract" ;
|
pr:shortLabel "Panel extract" ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
@@ -714,7 +736,7 @@ ex:DO_06_01_001 a pr:DataObject ;
|
|||||||
ex:DE_06_01_0094,
|
ex:DE_06_01_0094,
|
||||||
ex:DE_06_01_0095 ;
|
ex:DE_06_01_0095 ;
|
||||||
pr:identifier "DO-06.01-001" ;
|
pr:identifier "DO-06.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:physicalName "F_SELL_OUT" ;
|
pr:physicalName "F_SELL_OUT" ;
|
||||||
pr:qualifiedBy ex:DO_04_01_001,
|
pr:qualifiedBy ex:DO_04_01_001,
|
||||||
@@ -745,7 +767,7 @@ ex:DO_06_01_003 a pr:DataObject ;
|
|||||||
ex:DE_06_01_0074,
|
ex:DE_06_01_0074,
|
||||||
ex:DE_06_01_0075 ;
|
ex:DE_06_01_0075 ;
|
||||||
pr:identifier "DO-06.01-003" ;
|
pr:identifier "DO-06.01-003" ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_003 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:physicalName "F_SELL_OUT_DISTRIBUTION" ;
|
pr:physicalName "F_SELL_OUT_DISTRIBUTION" ;
|
||||||
pr:qualifiedBy ex:DO_04_01_001,
|
pr:qualifiedBy ex:DO_04_01_001,
|
||||||
@@ -786,7 +808,7 @@ ex:M_06_01_001 a pr:Metric ;
|
|||||||
pr:formula "SUM(validated sell-out volumes over the period)" ;
|
pr:formula "SUM(validated sell-out volumes over the period)" ;
|
||||||
pr:identifier "M-06.01-001" ;
|
pr:identifier "M-06.01-001" ;
|
||||||
pr:measures ex:BC_06_01_001 ;
|
pr:measures ex:BC_06_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_002 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -835,7 +857,7 @@ ex:M_06_01_002 a pr:Metric ;
|
|||||||
pr:formula "SUM(validated sell-out value over the period)" ;
|
pr:formula "SUM(validated sell-out value over the period)" ;
|
||||||
pr:identifier "M-06.01-002" ;
|
pr:identifier "M-06.01-002" ;
|
||||||
pr:measures ex:BC_06_01_001 ;
|
pr:measures ex:BC_06_01_001 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_002 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -855,7 +877,7 @@ ex:M_06_01_003 a pr:Metric ;
|
|||||||
pr:formula "COUNT(outlets carrying the SKU) / COUNT(universe outlets) * 100" ;
|
pr:formula "COUNT(outlets carrying the SKU) / COUNT(universe outlets) * 100" ;
|
||||||
pr:identifier "M-06.01-003" ;
|
pr:identifier "M-06.01-003" ;
|
||||||
pr:measures ex:BC_06_01_004 ;
|
pr:measures ex:BC_06_01_004 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_003 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -873,7 +895,7 @@ ex:M_06_01_004 a pr:Metric ;
|
|||||||
pr:formula "SUM(category turnover of outlets carrying SKU) / SUM(total category turnover) * 100" ;
|
pr:formula "SUM(category turnover of outlets carrying SKU) / SUM(total category turnover) * 100" ;
|
||||||
pr:identifier "M-06.01-004" ;
|
pr:identifier "M-06.01-004" ;
|
||||||
pr:measures ex:BC_06_01_004 ;
|
pr:measures ex:BC_06_01_004 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_003 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -888,7 +910,7 @@ ex:M_06_01_005 a pr:Metric ;
|
|||||||
pr:formula "SUM(Weighted Distribution across SKUs of the brand)" ;
|
pr:formula "SUM(Weighted Distribution across SKUs of the brand)" ;
|
||||||
pr:identifier "M-06.01-005" ;
|
pr:identifier "M-06.01-005" ;
|
||||||
pr:measures ex:BC_06_01_004 ;
|
pr:measures ex:BC_06_01_004 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_003 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -906,7 +928,7 @@ ex:M_06_01_006 a pr:Metric ;
|
|||||||
pr:formula "SUM(retailer forward stock volume at period end)" ;
|
pr:formula "SUM(retailer forward stock volume at period end)" ;
|
||||||
pr:identifier "M-06.01-006" ;
|
pr:identifier "M-06.01-006" ;
|
||||||
pr:measures ex:BC_06_01_002 ;
|
pr:measures ex:BC_06_01_002 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_004 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -922,7 +944,7 @@ ex:M_06_01_007 a pr:Metric ;
|
|||||||
pr:formula "Retailer Forward Stock Volume / average daily Sell Out Volume" ;
|
pr:formula "Retailer Forward Stock Volume / average daily Sell Out Volume" ;
|
||||||
pr:identifier "M-06.01-007" ;
|
pr:identifier "M-06.01-007" ;
|
||||||
pr:measures ex:BC_06_01_002 ;
|
pr:measures ex:BC_06_01_002 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_004 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -937,7 +959,7 @@ ex:M_06_01_008 a pr:Metric ;
|
|||||||
pr:formula "SKU retailer stock / category retailer stock * 100" ;
|
pr:formula "SKU retailer stock / category retailer stock * 100" ;
|
||||||
pr:identifier "M-06.01-008" ;
|
pr:identifier "M-06.01-008" ;
|
||||||
pr:measures ex:BC_06_01_002 ;
|
pr:measures ex:BC_06_01_002 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_004 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -953,7 +975,7 @@ ex:M_06_01_009 a pr:Metric ;
|
|||||||
pr:formula "MODEL(expected volume under a no-promotion scenario)" ;
|
pr:formula "MODEL(expected volume under a no-promotion scenario)" ;
|
||||||
pr:identifier "M-06.01-009" ;
|
pr:identifier "M-06.01-009" ;
|
||||||
pr:measures ex:BC_06_01_005 ;
|
pr:measures ex:BC_06_01_005 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_005 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -970,7 +992,7 @@ ex:M_06_01_010 a pr:Metric ;
|
|||||||
pr:formula "Observed Sell Out Volume - Baseline Volume" ;
|
pr:formula "Observed Sell Out Volume - Baseline Volume" ;
|
||||||
pr:identifier "M-06.01-010" ;
|
pr:identifier "M-06.01-010" ;
|
||||||
pr:measures ex:BC_06_01_005 ;
|
pr:measures ex:BC_06_01_005 ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_005 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
pr:sourcedFrom ex:SYS_SODH_GOLD ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -978,11 +1000,44 @@ ex:M_06_01_010 a pr:Metric ;
|
|||||||
"L" ;
|
"L" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
|
ex:PO_SODH a pr:DataProductOwner ;
|
||||||
|
pr:canonicalName "Data Product Owner" ;
|
||||||
|
pr:identifier "PO-06.01-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Guilherme Muller" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_04_02 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-04.02" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "Etienne Coulon" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_04_03 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-04.03" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "Etienne Coulon" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_16_03 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-16.03" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Julien Soisson" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:SD_04_01 a pr:DataSubDomain ;
|
ex:SD_04_01 a pr:DataSubDomain ;
|
||||||
pr:belongsTo ex:DD_04 ;
|
pr:belongsTo ex:DD_04 ;
|
||||||
pr:canonicalName "Customer Structure" ;
|
pr:canonicalName "Customer Structure" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_04_01 ;
|
||||||
pr:identifier "SD-04.01" ;
|
pr:identifier "SD-04.01" ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
pr:ownedBy ex:SDO_SD_04_01 ;
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -991,6 +1046,7 @@ ex:SD_04_02 a pr:DataSubDomain ;
|
|||||||
pr:arbitrationStatus "TO_ARBITRATE" ;
|
pr:arbitrationStatus "TO_ARBITRATE" ;
|
||||||
pr:belongsTo ex:DD_04 ;
|
pr:belongsTo ex:DD_04 ;
|
||||||
pr:canonicalName "Outlet & Points of Sale" ;
|
pr:canonicalName "Outlet & Points of Sale" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_04_02 ;
|
||||||
pr:identifier "SD-04.02" ;
|
pr:identifier "SD-04.02" ;
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -1000,6 +1056,7 @@ ex:SD_04_03 a pr:DataSubDomain ;
|
|||||||
pr:arbitrationStatus "TO_ARBITRATE" ;
|
pr:arbitrationStatus "TO_ARBITRATE" ;
|
||||||
pr:belongsTo ex:DD_04 ;
|
pr:belongsTo ex:DD_04 ;
|
||||||
pr:canonicalName "Geography" ;
|
pr:canonicalName "Geography" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_04_03 ;
|
||||||
pr:identifier "SD-04.03" ;
|
pr:identifier "SD-04.03" ;
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -1008,8 +1065,9 @@ ex:SD_04_03 a pr:DataSubDomain ;
|
|||||||
ex:SD_05_01 a pr:DataSubDomain ;
|
ex:SD_05_01 a pr:DataSubDomain ;
|
||||||
pr:belongsTo ex:DD_05 ;
|
pr:belongsTo ex:DD_05 ;
|
||||||
pr:canonicalName "Promotion" ;
|
pr:canonicalName "Promotion" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_05_01 ;
|
||||||
pr:identifier "SD-05.01" ;
|
pr:identifier "SD-05.01" ;
|
||||||
pr:ownedBy ex:ST_DD_05 ;
|
pr:ownedBy ex:SDO_SD_05_01 ;
|
||||||
pr:ownedByDomain ex:DD_05 ;
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -1017,8 +1075,9 @@ ex:SD_05_01 a pr:DataSubDomain ;
|
|||||||
ex:SD_10_01 a pr:DataSubDomain ;
|
ex:SD_10_01 a pr:DataSubDomain ;
|
||||||
pr:belongsTo ex:DD_10 ;
|
pr:belongsTo ex:DD_10 ;
|
||||||
pr:canonicalName "Product Structure" ;
|
pr:canonicalName "Product Structure" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_10_01 ;
|
||||||
pr:identifier "SD-10.01" ;
|
pr:identifier "SD-10.01" ;
|
||||||
pr:ownedBy ex:ST_DD_10 ;
|
pr:ownedBy ex:SDO_SD_10_01 ;
|
||||||
pr:ownedByDomain ex:DD_10 ;
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -1026,8 +1085,9 @@ ex:SD_10_01 a pr:DataSubDomain ;
|
|||||||
ex:SD_16_02 a pr:DataSubDomain ;
|
ex:SD_16_02 a pr:DataSubDomain ;
|
||||||
pr:belongsTo ex:DD_16 ;
|
pr:belongsTo ex:DD_16 ;
|
||||||
pr:canonicalName "Fiscal Calendar" ;
|
pr:canonicalName "Fiscal Calendar" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_16_02 ;
|
||||||
pr:identifier "SD-16.02" ;
|
pr:identifier "SD-16.02" ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
pr:ownedBy ex:SDO_SD_16_02 ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -1036,6 +1096,7 @@ ex:SD_16_03 a pr:DataSubDomain ;
|
|||||||
pr:arbitrationStatus "TO_ARBITRATE" ;
|
pr:arbitrationStatus "TO_ARBITRATE" ;
|
||||||
pr:belongsTo ex:DD_16 ;
|
pr:belongsTo ex:DD_16 ;
|
||||||
pr:canonicalName "Organisation Structure" ;
|
pr:canonicalName "Organisation Structure" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_16_03 ;
|
||||||
pr:identifier "SD-16.03" ;
|
pr:identifier "SD-16.03" ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
@@ -1044,8 +1105,9 @@ ex:SD_16_03 a pr:DataSubDomain ;
|
|||||||
ex:SD_21_01 a pr:DataSubDomain ;
|
ex:SD_21_01 a pr:DataSubDomain ;
|
||||||
pr:belongsTo ex:DD_21 ;
|
pr:belongsTo ex:DD_21 ;
|
||||||
pr:canonicalName "Standard Calendar" ;
|
pr:canonicalName "Standard Calendar" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_21_01 ;
|
||||||
pr:identifier "SD-21.01" ;
|
pr:identifier "SD-21.01" ;
|
||||||
pr:ownedBy ex:ST_DD_21 ;
|
pr:ownedBy ex:SDO_SD_21_01 ;
|
||||||
pr:ownedByDomain ex:DD_21 ;
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -1056,7 +1118,7 @@ ex:BC_16_03_001 a pr:BusinessConcept ;
|
|||||||
pr:businessRule "Named Marketing Entity, not Management Entity: the element is Marketing Entity Code and the column is MARKETING_ENTITY_CD. To be confirmed by DD-16." ;
|
pr:businessRule "Named Marketing Entity, not Management Entity: the element is Marketing Entity Code and the column is MARKETING_ENTITY_CD. To be confirmed by DD-16." ;
|
||||||
pr:canonicalName "Marketing Entity" ;
|
pr:canonicalName "Marketing Entity" ;
|
||||||
pr:identifier "BC-16.03-001" ;
|
pr:identifier "BC-16.03-001" ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
pr:ownedBy ex:DST_16_03_001 ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2152,22 +2214,52 @@ ex:DE_21_01_0001 a pr:DataElement ;
|
|||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:DGL_DD_04 a pr:DataGovernanceLead ;
|
ex:DGL_DD_04 a pr:DataGovernanceLead ;
|
||||||
pr:canonicalName "TBD (vacant)" .
|
pr:canonicalName "Data Governance Lead" ;
|
||||||
|
pr:identifier "DGL-04" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "Marie Carabin" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DGL_DD_05 a pr:DataGovernanceLead ;
|
ex:DGL_DD_05 a pr:DataGovernanceLead ;
|
||||||
pr:canonicalName "Alberic Piot" .
|
pr:canonicalName "Data Governance Lead" ;
|
||||||
|
pr:identifier "DGL-05" ;
|
||||||
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
|
pr:personName "Alberic Piot" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DGL_DD_06 a pr:DataGovernanceLead ;
|
ex:DGL_DD_06 a pr:DataGovernanceLead ;
|
||||||
pr:canonicalName "Bastien Gourdon" .
|
pr:canonicalName "Data Governance Lead" ;
|
||||||
|
pr:identifier "DGL-06" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Bastien Gourdon" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DGL_DD_10 a pr:DataGovernanceLead ;
|
ex:DGL_DD_10 a pr:DataGovernanceLead ;
|
||||||
pr:canonicalName "Anas El Kesri" .
|
pr:canonicalName "Data Governance Lead" ;
|
||||||
|
pr:identifier "DGL-10" ;
|
||||||
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
|
pr:personName "Anas El Kesri" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DGL_DD_16 a pr:DataGovernanceLead ;
|
ex:DGL_DD_16 a pr:DataGovernanceLead ;
|
||||||
pr:canonicalName "Gaelle Seret" .
|
pr:canonicalName "Data Governance Lead" ;
|
||||||
|
pr:identifier "DGL-16" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Gaelle Seret" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DGL_DD_21 a pr:DataGovernanceLead ;
|
ex:DGL_DD_21 a pr:DataGovernanceLead ;
|
||||||
pr:canonicalName "Bastien Gourdon" .
|
pr:canonicalName "Data Governance Lead" ;
|
||||||
|
pr:identifier "DGL-21" ;
|
||||||
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
|
pr:personName "Bastien Gourdon" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:DO_16_01_001 a pr:DataObject ;
|
ex:DO_16_01_001 a pr:DataObject ;
|
||||||
pr:canonicalName "Currency Dimension" ;
|
pr:canonicalName "Currency Dimension" ;
|
||||||
@@ -2179,7 +2271,7 @@ ex:DO_16_01_001 a pr:DataObject ;
|
|||||||
ex:DE_16_02_0003 ;
|
ex:DE_16_02_0003 ;
|
||||||
pr:hasGrainElement ex:DE_16_01_0001 ;
|
pr:hasGrainElement ex:DE_16_01_0001 ;
|
||||||
pr:identifier "DO-16.01-001" ;
|
pr:identifier "DO-16.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
pr:ownedBy ex:DST_16_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:physicalName "D_CURRENCY" ;
|
pr:physicalName "D_CURRENCY" ;
|
||||||
pr:represents ex:BO_16_01_001 ;
|
pr:represents ex:BO_16_01_001 ;
|
||||||
@@ -2188,11 +2280,132 @@ ex:DO_16_01_001 a pr:DataObject ;
|
|||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
|
ex:DST_04_02_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-04.02-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "Marc Chambouleyron" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_04_03_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-04.03-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "Marc Chambouleyron" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_05_01_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-05.01-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
|
pr:personName "Francois Vadrouille" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_06_01_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-06.01-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Michael Flores" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_06_01_006 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-06.01-006" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Michael Flores" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_16_01_002 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-16.01-002" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Clementine Sauries" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_16_02_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-16.02-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Clementine Sauries" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_16_03_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-16.03-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Clementine Sauries" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_04_01 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-04.01" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "Etienne Coulon" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_05_01 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-05.01" ;
|
||||||
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
|
pr:personName "David Carpio" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_06_01 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-06.01" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Loic Berger" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_10_01 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-10.01" ;
|
||||||
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
|
pr:personName "Alberto Lupano" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_16_01 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-16.01" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Julien Soisson" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_16_02 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-16.02" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Julien Soisson" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:SDO_SD_21_01 a pr:DataSubDomainOwner ;
|
||||||
|
pr:canonicalName "Data Sub Domain Owner" ;
|
||||||
|
pr:identifier "SDO-21.01" ;
|
||||||
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
|
pr:personName "Vincent Meunier" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:SD_16_01 a pr:DataSubDomain ;
|
ex:SD_16_01 a pr:DataSubDomain ;
|
||||||
pr:belongsTo ex:DD_16 ;
|
pr:belongsTo ex:DD_16 ;
|
||||||
pr:canonicalName "Currency & Exchange Rate" ;
|
pr:canonicalName "Currency & Exchange Rate" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_16_01 ;
|
||||||
pr:identifier "SD-16.01" ;
|
pr:identifier "SD-16.01" ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
pr:ownedBy ex:SDO_SD_16_01 ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2202,7 +2415,7 @@ ex:BC_04_03_001 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "A sovereign territory used as the geographic frame for retail measurement, identified by its ISO 3166 code. Distinct from Customer and Outlet: it is where they operate, not what they are." ;
|
pr:businessDefinition "A sovereign territory used as the geographic frame for retail measurement, identified by its ISO 3166 code. Distinct from Customer and Outlet: it is where they operate, not what they are." ;
|
||||||
pr:canonicalName "Country" ;
|
pr:canonicalName "Country" ;
|
||||||
pr:identifier "BC-04.03-001" ;
|
pr:identifier "BC-04.03-001" ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
pr:ownedBy ex:DST_04_03_001 ;
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2211,7 +2424,7 @@ ex:BC_06_01_005 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "The modelled level of retail sales that would have occurred without promotional activity, used as the reference against which promotional uplift is measured. Distinct from Sell Out, which is observed rather than modelled." ;
|
pr:businessDefinition "The modelled level of retail sales that would have occurred without promotional activity, used as the reference against which promotional uplift is measured. Distinct from Sell Out, which is observed rather than modelled." ;
|
||||||
pr:canonicalName "Sell Out Baseline" ;
|
pr:canonicalName "Sell Out Baseline" ;
|
||||||
pr:identifier "BC-06.01-005" ;
|
pr:identifier "BC-06.01-005" ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_005 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2221,7 +2434,7 @@ ex:BC_16_01_001 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "A monetary unit in which a transaction or a reported amount is denominated, identified by its ISO 4217 code." ;
|
pr:businessDefinition "A monetary unit in which a transaction or a reported amount is denominated, identified by its ISO 4217 code." ;
|
||||||
pr:canonicalName "Currency" ;
|
pr:canonicalName "Currency" ;
|
||||||
pr:identifier "BC-16.01-001" ;
|
pr:identifier "BC-16.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
pr:ownedBy ex:DST_16_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2231,7 +2444,7 @@ ex:BC_16_01_002 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "The conversion factor applied between two currencies at a given date, used to restate local amounts into group reporting currency. Distinct from Currency: a rate is a dated relationship between two of them." ;
|
pr:businessDefinition "The conversion factor applied between two currencies at a given date, used to restate local amounts into group reporting currency. Distinct from Currency: a rate is a dated relationship between two of them." ;
|
||||||
pr:canonicalName "Exchange Rate" ;
|
pr:canonicalName "Exchange Rate" ;
|
||||||
pr:identifier "BC-16.01-002" ;
|
pr:identifier "BC-16.01-002" ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
pr:ownedBy ex:DST_16_01_002 ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2241,7 +2454,7 @@ ex:BC_16_02_001 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "A reporting interval of the Pernod Ricard fiscal year, which runs July to June. Distinct from Calendar Date: the fiscal frame does not align with the Gregorian one." ;
|
pr:businessDefinition "A reporting interval of the Pernod Ricard fiscal year, which runs July to June. Distinct from Calendar Date: the fiscal frame does not align with the Gregorian one." ;
|
||||||
pr:canonicalName "Fiscal Period" ;
|
pr:canonicalName "Fiscal Period" ;
|
||||||
pr:identifier "BC-16.02-001" ;
|
pr:identifier "BC-16.02-001" ;
|
||||||
pr:ownedBy ex:ST_DD_16 ;
|
pr:ownedBy ex:DST_16_02_001 ;
|
||||||
pr:ownedByDomain ex:DD_16 ;
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2260,7 +2473,7 @@ ex:DO_04_01_001 a pr:DataObject ;
|
|||||||
ex:DE_04_01_0010 ;
|
ex:DE_04_01_0010 ;
|
||||||
pr:hasGrainElement ex:DE_04_01_0005 ;
|
pr:hasGrainElement ex:DE_04_01_0005 ;
|
||||||
pr:identifier "DO-04.01-001" ;
|
pr:identifier "DO-04.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
pr:ownedBy ex:DST_04_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:physicalName "D_CUSTOMER" ;
|
pr:physicalName "D_CUSTOMER" ;
|
||||||
pr:represents ex:BO_04_01_001 ;
|
pr:represents ex:BO_04_01_001 ;
|
||||||
@@ -2285,7 +2498,7 @@ ex:DO_10_01_001 a pr:DataObject ;
|
|||||||
ex:DE_10_01_0012 ;
|
ex:DE_10_01_0012 ;
|
||||||
pr:hasGrainElement ex:DE_10_01_0005 ;
|
pr:hasGrainElement ex:DE_10_01_0005 ;
|
||||||
pr:identifier "DO-10.01-001" ;
|
pr:identifier "DO-10.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_10 ;
|
pr:ownedBy ex:DST_10_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_10 ;
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
pr:physicalName "D_FINISHED_PRODUCT" ;
|
pr:physicalName "D_FINISHED_PRODUCT" ;
|
||||||
pr:represents ex:BO_10_01_001 ;
|
pr:represents ex:BO_10_01_001 ;
|
||||||
@@ -2303,7 +2516,7 @@ ex:DO_21_01_001 a pr:DataObject ;
|
|||||||
ex:DE_21_01_0005 ;
|
ex:DE_21_01_0005 ;
|
||||||
pr:hasGrainElement ex:DE_21_01_0001 ;
|
pr:hasGrainElement ex:DE_21_01_0001 ;
|
||||||
pr:identifier "DO-21.01-001" ;
|
pr:identifier "DO-21.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_21 ;
|
pr:ownedBy ex:DST_21_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_21 ;
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
pr:physicalName "D_DATE" ;
|
pr:physicalName "D_DATE" ;
|
||||||
pr:represents ex:BO_21_01_001 ;
|
pr:represents ex:BO_21_01_001 ;
|
||||||
@@ -2312,12 +2525,44 @@ ex:DO_21_01_001 a pr:DataObject ;
|
|||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
|
ex:DST_04_01_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-04.01-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
|
pr:personName "Marc Chambouleyron" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_10_01_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-10.01-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
|
pr:personName "Magda Kugli" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_16_01_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-16.01-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_16 ;
|
||||||
|
pr:personName "Clementine Sauries" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
|
ex:DST_21_01_001 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-21.01-001" ;
|
||||||
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
|
pr:personName "Alberic Piot" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:BC_04_01_002 a pr:BusinessConcept ;
|
ex:BC_04_01_002 a pr:BusinessConcept ;
|
||||||
pr:arbitrationStatus "TO_ARBITRATE" ;
|
pr:arbitrationStatus "TO_ARBITRATE" ;
|
||||||
pr:businessDefinition "A physical point of sale where products are made available to end consumers, characterised by its Channel. An Outlet belongs to a Customer but is not the same notion: one Customer operates many Outlets." ;
|
pr:businessDefinition "A physical point of sale where products are made available to end consumers, characterised by its Channel. An Outlet belongs to a Customer but is not the same notion: one Customer operates many Outlets." ;
|
||||||
pr:canonicalName "Outlet" ;
|
pr:canonicalName "Outlet" ;
|
||||||
pr:identifier "BC-04.01-002" ;
|
pr:identifier "BC-04.01-002" ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
pr:ownedBy ex:DST_04_02_001 ;
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2326,16 +2571,7 @@ ex:BC_06_01_002 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "Inventory of Pernod Ricard products held at the retailer level, downstream of Pernod Ricard shipments." ;
|
pr:businessDefinition "Inventory of Pernod Ricard products held at the retailer level, downstream of Pernod Ricard shipments." ;
|
||||||
pr:canonicalName "Retailer Stock" ;
|
pr:canonicalName "Retailer Stock" ;
|
||||||
pr:identifier "BC-06.01-002" ;
|
pr:identifier "BC-06.01-002" ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_004 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
|
||||||
pr:status "PUBLISHED" ;
|
|
||||||
pr:version "1.3" .
|
|
||||||
|
|
||||||
ex:BC_06_01_003 a pr:BusinessConcept ;
|
|
||||||
pr:businessDefinition "The scope of retail outlets observed by a panel provider, defining the universe over which distribution is computed." ;
|
|
||||||
pr:canonicalName "Panel Coverage" ;
|
|
||||||
pr:identifier "BC-06.01-003" ;
|
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2344,19 +2580,43 @@ ex:BC_06_01_004 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "The presence of a product across retail outlets: the number of points of sale carrying at least one Pernod Ricard product, related to the total number of points of sale in the Pernod Ricard universe." ;
|
pr:businessDefinition "The presence of a product across retail outlets: the number of points of sale carrying at least one Pernod Ricard product, related to the total number of points of sale in the Pernod Ricard universe." ;
|
||||||
pr:canonicalName "Retail Distribution" ;
|
pr:canonicalName "Retail Distribution" ;
|
||||||
pr:identifier "BC-06.01-004" ;
|
pr:identifier "BC-06.01-004" ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_003 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:ST_DD_05 a pr:DataSteward ;
|
ex:DST_06_01_002 a pr:DataSteward ;
|
||||||
pr:canonicalName "Sales Activation Steward" .
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-06.01-002" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Michael Flores" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:ST_DD_10 a pr:DataSteward ;
|
ex:DST_06_01_005 a pr:DataSteward ;
|
||||||
pr:canonicalName "Product & Material Steward" .
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-06.01-005" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Michael Flores" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:ST_DD_21 a pr:DataSteward ;
|
ex:BC_06_01_003 a pr:BusinessConcept ;
|
||||||
pr:canonicalName "Data Governance Steward" .
|
pr:businessDefinition "The scope of retail outlets observed by a panel provider, defining the universe over which distribution is computed." ;
|
||||||
|
pr:canonicalName "Panel Coverage" ;
|
||||||
|
pr:identifier "BC-06.01-003" ;
|
||||||
|
pr:ownedBy ex:DST_06_01_006 ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.3" .
|
||||||
|
|
||||||
|
ex:DST_06_01_004 a pr:DataSteward ;
|
||||||
|
pr:canonicalName "Data Steward" ;
|
||||||
|
pr:identifier "DST-06.01-004" ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Michael Flores" ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:SYS_CORPORATE_CALENDAR a pr:System ;
|
ex:SYS_CORPORATE_CALENDAR a pr:System ;
|
||||||
pr:canonicalName "Corporate standard calendar" ;
|
pr:canonicalName "Corporate standard calendar" ;
|
||||||
@@ -2371,7 +2631,7 @@ ex:BC_04_01_001 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "A commercial counterparty that buys Pernod Ricard products for resale, positioned in the trade hierarchy from Distributor through Tier-1 and Tier-2 Customer. Distinct from Outlet, which is a physical point of sale." ;
|
pr:businessDefinition "A commercial counterparty that buys Pernod Ricard products for resale, positioned in the trade hierarchy from Distributor through Tier-1 and Tier-2 Customer. Distinct from Outlet, which is a physical point of sale." ;
|
||||||
pr:canonicalName "Customer" ;
|
pr:canonicalName "Customer" ;
|
||||||
pr:identifier "BC-04.01-001" ;
|
pr:identifier "BC-04.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_04 ;
|
pr:ownedBy ex:DST_04_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_04 ;
|
pr:ownedByDomain ex:DD_04 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2380,7 +2640,7 @@ ex:BC_06_01_001 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "The sale of Pernod Ricard products by retail customers to end consumers, observed via panel or direct feed, as opposed to shipments from Pernod Ricard to its customers (Sell In)." ;
|
pr:businessDefinition "The sale of Pernod Ricard products by retail customers to end consumers, observed via panel or direct feed, as opposed to shipments from Pernod Ricard to its customers (Sell In)." ;
|
||||||
pr:canonicalName "Sell Out" ;
|
pr:canonicalName "Sell Out" ;
|
||||||
pr:identifier "BC-06.01-001" ;
|
pr:identifier "BC-06.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
pr:ownedBy ex:DST_06_01_002 ;
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2390,19 +2650,18 @@ ex:BC_21_01_001 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "A single day in the Gregorian calendar, the atomic unit from which Week, Month, Quarter and Year are derived and against which all time-based measures are positioned." ;
|
pr:businessDefinition "A single day in the Gregorian calendar, the atomic unit from which Week, Month, Quarter and Year are derived and against which all time-based measures are positioned." ;
|
||||||
pr:canonicalName "Calendar Date" ;
|
pr:canonicalName "Calendar Date" ;
|
||||||
pr:identifier "BC-21.01-001" ;
|
pr:identifier "BC-21.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_21 ;
|
pr:ownedBy ex:DST_21_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_21 ;
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:SD_06_01 a pr:DataSubDomain ;
|
ex:DST_06_01_003 a pr:DataSteward ;
|
||||||
pr:belongsTo ex:DD_06 ;
|
pr:canonicalName "Data Steward" ;
|
||||||
pr:canonicalName "Sell Out" ;
|
pr:identifier "DST-06.01-003" ;
|
||||||
pr:identifier "SD-06.01" ;
|
|
||||||
pr:ownedBy ex:ST_DD_06 ;
|
|
||||||
pr:ownedByDomain ex:DD_06 ;
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:personName "Michael Flores" ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.0" .
|
||||||
|
|
||||||
ex:SYS_GROUP_FINANCE a pr:System ;
|
ex:SYS_GROUP_FINANCE a pr:System ;
|
||||||
pr:canonicalName "Group Finance consolidation" ;
|
pr:canonicalName "Group Finance consolidation" ;
|
||||||
@@ -2412,22 +2671,21 @@ ex:SYS_GROUP_FINANCE a pr:System ;
|
|||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.4" .
|
pr:version "1.4" .
|
||||||
|
|
||||||
|
ex:SD_06_01 a pr:DataSubDomain ;
|
||||||
|
pr:belongsTo ex:DD_06 ;
|
||||||
|
pr:canonicalName "Sell Out" ;
|
||||||
|
pr:hasSubDomainOwner ex:SDO_SD_06_01 ;
|
||||||
|
pr:identifier "SD-06.01" ;
|
||||||
|
pr:ownedBy ex:SDO_SD_06_01 ;
|
||||||
|
pr:ownedByDomain ex:DD_06 ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:BC_05_01_001 a pr:BusinessConcept ;
|
ex:BC_05_01_001 a pr:BusinessConcept ;
|
||||||
pr:businessDefinition "A commercial mechanism applied at retail to stimulate sales (for example Temporary Price Reduction, Multibuy, Feature Display)." ;
|
pr:businessDefinition "A commercial mechanism applied at retail to stimulate sales (for example Temporary Price Reduction, Multibuy, Feature Display)." ;
|
||||||
pr:canonicalName "Promotion" ;
|
pr:canonicalName "Promotion" ;
|
||||||
pr:identifier "BC-05.01-001" ;
|
pr:identifier "BC-05.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_05 ;
|
pr:ownedBy ex:DST_05_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_05 ;
|
|
||||||
pr:status "PUBLISHED" ;
|
|
||||||
pr:version "1.3" .
|
|
||||||
|
|
||||||
ex:DD_05 a pr:DataDomain ;
|
|
||||||
pr:activationStatus "FULL_ACTIVATION" ;
|
|
||||||
pr:canonicalName "Sales Activation" ;
|
|
||||||
pr:hasDomainOwner ex:DDO_DD_05 ;
|
|
||||||
pr:hasGovernanceLead ex:DGL_DD_05 ;
|
|
||||||
pr:identifier "DD-05" ;
|
|
||||||
pr:ownedBy ex:DGL_DD_05 ;
|
|
||||||
pr:ownedByDomain ex:DD_05 ;
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
@@ -2440,20 +2698,6 @@ ex:SYS_MDM_CUSTOMER a pr:System ;
|
|||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.4" .
|
pr:version "1.4" .
|
||||||
|
|
||||||
ex:DD_21 a pr:DataDomain ;
|
|
||||||
pr:activationStatus "FULL_ACTIVATION" ;
|
|
||||||
pr:canonicalName "Data Governance" ;
|
|
||||||
pr:hasDomainOwner ex:DDO_DD_21 ;
|
|
||||||
pr:hasGovernanceLead ex:DGL_DD_21 ;
|
|
||||||
pr:identifier "DD-21" ;
|
|
||||||
pr:ownedBy ex:DGL_DD_21 ;
|
|
||||||
pr:ownedByDomain ex:DD_21 ;
|
|
||||||
pr:status "PUBLISHED" ;
|
|
||||||
pr:version "1.3" .
|
|
||||||
|
|
||||||
ex:ST_DD_04 a pr:DataSteward ;
|
|
||||||
pr:canonicalName "Customer & Channels Steward" .
|
|
||||||
|
|
||||||
ex:SYS_MDM_PRODUCT a pr:System ;
|
ex:SYS_MDM_PRODUCT a pr:System ;
|
||||||
pr:canonicalName "Master Data Management - Product" ;
|
pr:canonicalName "Master Data Management - Product" ;
|
||||||
pr:hasSystemType pr:MasterDataSystem ;
|
pr:hasSystemType pr:MasterDataSystem ;
|
||||||
@@ -2467,13 +2711,32 @@ ex:BC_10_01_001 a pr:BusinessConcept ;
|
|||||||
pr:businessDefinition "A commercial item sold by Pernod Ricard, identified at Stock Keeping Unit level and positioned in the corporate hierarchy from Brand through Category, Sub-Category and Flavor." ;
|
pr:businessDefinition "A commercial item sold by Pernod Ricard, identified at Stock Keeping Unit level and positioned in the corporate hierarchy from Brand through Category, Sub-Category and Flavor." ;
|
||||||
pr:canonicalName "Product" ;
|
pr:canonicalName "Product" ;
|
||||||
pr:identifier "BC-10.01-001" ;
|
pr:identifier "BC-10.01-001" ;
|
||||||
pr:ownedBy ex:ST_DD_10 ;
|
pr:ownedBy ex:DST_10_01_001 ;
|
||||||
pr:ownedByDomain ex:DD_10 ;
|
pr:ownedByDomain ex:DD_10 ;
|
||||||
pr:status "DRAFT" ;
|
pr:status "DRAFT" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:ST_DD_16 a pr:DataSteward ;
|
ex:DD_05 a pr:DataDomain ;
|
||||||
pr:canonicalName "Financial Performance Steward" .
|
pr:activationStatus "FULL_ACTIVATION" ;
|
||||||
|
pr:canonicalName "Sales Activation" ;
|
||||||
|
pr:hasDomainOwner ex:DDO_DD_05 ;
|
||||||
|
pr:hasGovernanceLead ex:DGL_DD_05 ;
|
||||||
|
pr:identifier "DD-05" ;
|
||||||
|
pr:ownedBy ex:DGL_DD_05 ;
|
||||||
|
pr:ownedByDomain ex:DD_05 ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.3" .
|
||||||
|
|
||||||
|
ex:DD_21 a pr:DataDomain ;
|
||||||
|
pr:activationStatus "FULL_ACTIVATION" ;
|
||||||
|
pr:canonicalName "Data Governance" ;
|
||||||
|
pr:hasDomainOwner ex:DDO_DD_21 ;
|
||||||
|
pr:hasGovernanceLead ex:DGL_DD_21 ;
|
||||||
|
pr:identifier "DD-21" ;
|
||||||
|
pr:ownedBy ex:DGL_DD_21 ;
|
||||||
|
pr:ownedByDomain ex:DD_21 ;
|
||||||
|
pr:status "PUBLISHED" ;
|
||||||
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:DD_10 a pr:DataDomain ;
|
ex:DD_10 a pr:DataDomain ;
|
||||||
pr:activationStatus "FULL_ACTIVATION" ;
|
pr:activationStatus "FULL_ACTIVATION" ;
|
||||||
@@ -2508,9 +2771,6 @@ ex:DD_04 a pr:DataDomain ;
|
|||||||
pr:status "PUBLISHED" ;
|
pr:status "PUBLISHED" ;
|
||||||
pr:version "1.3" .
|
pr:version "1.3" .
|
||||||
|
|
||||||
ex:ST_DD_06 a pr:DataSteward ;
|
|
||||||
pr:canonicalName "Sales Performance Steward" .
|
|
||||||
|
|
||||||
ex:SYS_SODH_GOLD a pr:System ;
|
ex:SYS_SODH_GOLD a pr:System ;
|
||||||
pr:canonicalName "Sell Out Data Hub - Gold layer" ;
|
pr:canonicalName "Sell Out Data Hub - Gold layer" ;
|
||||||
pr:hasSystemType pr:DataPlatform ;
|
pr:hasSystemType pr:DataPlatform ;
|
||||||
|
|||||||
+178
-101
@@ -12,7 +12,7 @@ pr: a owl:Ontology ;
|
|||||||
dcterms:title "Pernod Ricard Data Meta Model" ;
|
dcterms:title "Pernod Ricard Data Meta Model" ;
|
||||||
rdfs:comment "OWL T-Box. Six layers (Ownership, Business, Logical, Delivery, Physical, Consumption) crossed with a provenance axis (Defined vs Captured). Defined objects are written by governance and reviewed; Captured objects are read from Snowflake, Tableau and Looker and are rebuilt, never edited. Bridge annotation properties are declared for future alignment with domain ontologies." ;
|
rdfs:comment "OWL T-Box. Six layers (Ownership, Business, Logical, Delivery, Physical, Consumption) crossed with a provenance axis (Defined vs Captured). Defined objects are written by governance and reviewed; Captured objects are read from Snowflake, Tableau and Looker and are rebuilt, never edited. Bridge annotation properties are declared for future alignment with domain ontologies." ;
|
||||||
owl:priorVersion pr:1.5 ;
|
owl:priorVersion pr:1.5 ;
|
||||||
owl:versionIRI pr:2.0 ;
|
owl:versionIRI pr:2.2 ;
|
||||||
owl:versionInfo "1.6.0-draft-2026-07-30" .
|
owl:versionInfo "1.6.0-draft-2026-07-30" .
|
||||||
|
|
||||||
pr:BusinessIntelligenceTool a pr:SystemType ;
|
pr:BusinessIntelligenceTool a pr:SystemType ;
|
||||||
@@ -29,6 +29,7 @@ pr:KeyPerformanceIndicator a owl:Class ;
|
|||||||
rdfs:comment "An elevated Metric. Use this class rather than the deprecated isKPI flag." ;
|
rdfs:comment "An elevated Metric. Use this class rather than the deprecated isKPI flag." ;
|
||||||
rdfs:subClassOf pr:Metric ;
|
rdfs:subClassOf pr:Metric ;
|
||||||
pr:acronym "KPI" ;
|
pr:acronym "KPI" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "KPI" .
|
pr:shortLabel "KPI" .
|
||||||
|
|
||||||
@@ -51,7 +52,8 @@ pr:activationStatus a owl:DatatypeProperty,
|
|||||||
pr:arbitrationStatus a owl:DatatypeProperty ;
|
pr:arbitrationStatus a owl:DatatypeProperty ;
|
||||||
rdfs:label "arbitration Status" ;
|
rdfs:label "arbitration Status" ;
|
||||||
rdfs:comment "TO_ARBITRATE blocks publication (OW-007). Use this on the object rather than tracking arbitration in a side spreadsheet." ;
|
rdfs:comment "TO_ARBITRATE blocks publication (OW-007). Use this on the object rather than tracking arbitration in a side spreadsheet." ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:authoringMode a owl:AnnotationProperty ;
|
pr:authoringMode a owl:AnnotationProperty ;
|
||||||
rdfs:label "authoring Mode" ;
|
rdfs:label "authoring Mode" ;
|
||||||
@@ -60,7 +62,8 @@ pr:authoringMode a owl:AnnotationProperty ;
|
|||||||
pr:belongsTo a owl:FunctionalProperty,
|
pr:belongsTo a owl:FunctionalProperty,
|
||||||
owl:ObjectProperty ;
|
owl:ObjectProperty ;
|
||||||
rdfs:label "belongs To" ;
|
rdfs:label "belongs To" ;
|
||||||
rdfs:comment "Position in the categorization tree. One property for the whole tree: SubDomain belongs to DataDomain, BusinessObject belongs to SubDomain. Scope is enforced per class by SHACL rather than by two near-identical properties, and the same relation will serve if a domain family ever appears. Deliberately NOT transitive: entailment would produce two values and break the maxCount 1 shape. To walk the ancestry, use the property path belongsTo+ in the query." .
|
rdfs:comment "Position in the categorization tree. One property for the whole tree: SubDomain belongs to DataDomain, BusinessObject belongs to SubDomain. Scope is enforced per class by SHACL rather than by two near-identical properties, and the same relation will serve if a domain family ever appears. Deliberately NOT transitive: entailment would produce two values and break the maxCount 1 shape. To walk the ancestry, use the property path belongsTo+ in the query." ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:businessDefinition a owl:DatatypeProperty ;
|
pr:businessDefinition a owl:DatatypeProperty ;
|
||||||
rdfs:label "business Definition" ;
|
rdfs:label "business Definition" ;
|
||||||
@@ -69,16 +72,19 @@ pr:businessDefinition a owl:DatatypeProperty ;
|
|||||||
MUST NOT be carried by a BusinessObject: an object gets its meaning through aboutConcept. Two definitions for one notion are two definitions free to diverge. Enforced by SHACL rather than by rdfs:domain, because Data Elements legitimately carry one too and a declared domain would make them entail BusinessConcept -- which the disjointness axioms would then reject.
|
MUST NOT be carried by a BusinessObject: an object gets its meaning through aboutConcept. Two definitions for one notion are two definitions free to diverge. Enforced by SHACL rather than by rdfs:domain, because Data Elements legitimately carry one too and a declared domain would make them entail BusinessConcept -- which the disjointness axioms would then reject.
|
||||||
|
|
||||||
MIGRATION: text currently on a Business Object moves to the Concept named by its aboutConcept.""" ;
|
MIGRATION: text currently on a Business Object moves to the Concept named by its aboutConcept.""" ;
|
||||||
|
rdfs:domain pr:BusinessConcept ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:businessRule a owl:DatatypeProperty ;
|
pr:businessRule a owl:DatatypeProperty ;
|
||||||
rdfs:label "business Rule" ;
|
rdfs:label "business Rule" ;
|
||||||
|
rdfs:domain pr:BusinessConcept ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:canonicalName a owl:DatatypeProperty,
|
pr:canonicalName a owl:DatatypeProperty,
|
||||||
owl:FunctionalProperty ;
|
owl:FunctionalProperty ;
|
||||||
rdfs:label "canonical Name" ;
|
rdfs:label "canonical Name" ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:computedBy a owl:ObjectProperty ;
|
pr:computedBy a owl:ObjectProperty ;
|
||||||
rdfs:label "computed By" ;
|
rdfs:label "computed By" ;
|
||||||
@@ -107,11 +113,13 @@ pr:containsBIField a owl:ObjectProperty ;
|
|||||||
|
|
||||||
pr:creationDate a owl:DatatypeProperty ;
|
pr:creationDate a owl:DatatypeProperty ;
|
||||||
rdfs:label "creation Date" ;
|
rdfs:label "creation Date" ;
|
||||||
rdfs:range xsd:date .
|
rdfs:range xsd:date ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:definitionAddress a owl:DatatypeProperty ;
|
pr:definitionAddress a owl:DatatypeProperty ;
|
||||||
rdfs:label "definition Address" ;
|
rdfs:label "definition Address" ;
|
||||||
rdfs:comment "Git URI of the code behind a Transformation. Connects the graph to the repository." ;
|
rdfs:comment "Git URI of the code behind a Transformation. Connects the graph to the repository." ;
|
||||||
|
rdfs:domain pr:Transformation ;
|
||||||
rdfs:range xsd:anyURI .
|
rdfs:range xsd:anyURI .
|
||||||
|
|
||||||
pr:denotes a owl:AnnotationProperty ;
|
pr:denotes a owl:AnnotationProperty ;
|
||||||
@@ -158,7 +166,8 @@ pr:environment a owl:DatatypeProperty,
|
|||||||
|
|
||||||
pr:exampleValue a owl:DatatypeProperty ;
|
pr:exampleValue a owl:DatatypeProperty ;
|
||||||
rdfs:label "example Value" ;
|
rdfs:label "example Value" ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:exposes a owl:ObjectProperty ;
|
pr:exposes a owl:ObjectProperty ;
|
||||||
rdfs:label "exposes" ;
|
rdfs:label "exposes" ;
|
||||||
@@ -177,23 +186,25 @@ So: TRANSITIONAL CONTROL. Asserted directly, and that is all. No rule is written
|
|||||||
|
|
||||||
Retirement criterion, to be measured rather than debated: when the lineage chain reproduces the asserted link for more than 95% of certified BI fields across three consecutive reconciliation cycles, the direct link is derivable and can go.""" ;
|
Retirement criterion, to be measured rather than debated: when the lineage chain reproduces the asserted link for more than 95% of certified BI fields across three consecutive reconciliation cycles, the direct link is derivable and can go.""" ;
|
||||||
rdfs:domain pr:BusinessIntelligenceField ;
|
rdfs:domain pr:BusinessIntelligenceField ;
|
||||||
rdfs:range pr:Metric ;
|
rdfs:range pr:Metric .
|
||||||
pr:authoringMode "PROPOSED_CONFIRMED" .
|
|
||||||
|
|
||||||
pr:expression a owl:DatatypeProperty ;
|
pr:expression a owl:DatatypeProperty ;
|
||||||
rdfs:label "expression" ;
|
rdfs:label "expression" ;
|
||||||
rdfs:comment "The calculated-field formula, Tableau or LookML. Compared against the Metric formula to detect definition drift." ;
|
rdfs:comment "The calculated-field formula, Tableau or LookML. Compared against the Metric formula to detect definition drift." ;
|
||||||
|
rdfs:domain pr:BusinessIntelligenceField ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:formula a owl:DatatypeProperty ;
|
pr:formula a owl:DatatypeProperty ;
|
||||||
rdfs:label "formula" ;
|
rdfs:label "formula" ;
|
||||||
|
rdfs:domain pr:Metric ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:fullyQualifiedName a owl:DatatypeProperty,
|
pr:fullyQualifiedName a owl:DatatypeProperty,
|
||||||
owl:FunctionalProperty ;
|
owl:FunctionalProperty ;
|
||||||
rdfs:label "fully Qualified Name" ;
|
rdfs:label "fully Qualified Name" ;
|
||||||
rdfs:comment "DATABASE.SCHEMA.RELATION[.COLUMN]. The actual join key against INFORMATION_SCHEMA: a bare physical name is ambiguous as soon as the same column name exists in two tables." ;
|
rdfs:comment "DATABASE.SCHEMA.RELATION[.COLUMN]. The actual join key against INFORMATION_SCHEMA: a bare physical name is ambiguous as soon as the same column name exists in two tables." ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:governedBy a owl:FunctionalProperty,
|
pr:governedBy a owl:FunctionalProperty,
|
||||||
owl:ObjectProperty ;
|
owl:ObjectProperty ;
|
||||||
@@ -204,7 +215,8 @@ pr:governedBy a owl:FunctionalProperty,
|
|||||||
pr:harvestDate a owl:DatatypeProperty ;
|
pr:harvestDate a owl:DatatypeProperty ;
|
||||||
rdfs:label "harvest Date" ;
|
rdfs:label "harvest Date" ;
|
||||||
rdfs:comment "Staleness marker. A Captured Object not refreshed by the last run is a deletion candidate, not a fact." ;
|
rdfs:comment "Staleness marker. A Captured Object not refreshed by the last run is a deletion candidate, not a fact." ;
|
||||||
rdfs:range xsd:dateTime .
|
rdfs:range xsd:dateTime ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:harvestSource a owl:AnnotationProperty ;
|
pr:harvestSource a owl:AnnotationProperty ;
|
||||||
rdfs:label "harvest Source" ;
|
rdfs:label "harvest Source" ;
|
||||||
@@ -279,7 +291,8 @@ pr:hostedOn a owl:FunctionalProperty,
|
|||||||
pr:identifier a owl:DatatypeProperty,
|
pr:identifier a owl:DatatypeProperty,
|
||||||
owl:FunctionalProperty ;
|
owl:FunctionalProperty ;
|
||||||
rdfs:label "identifier" ;
|
rdfs:label "identifier" ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:isAbout a owl:FunctionalProperty,
|
pr:isAbout a owl:FunctionalProperty,
|
||||||
owl:ObjectProperty ;
|
owl:ObjectProperty ;
|
||||||
@@ -308,24 +321,29 @@ pr:isInSchema a owl:FunctionalProperty,
|
|||||||
owl:ObjectProperty ;
|
owl:ObjectProperty ;
|
||||||
rdfs:label "is In Schema" ;
|
rdfs:label "is In Schema" ;
|
||||||
rdfs:comment "Domain deliberately not declared, and the shapes must keep this OPTIONAL: Neo4j has no schema and BigQuery calls the level a dataset. A minCount 1 would break on the first harvest outside Snowflake. It is also the only property that would distinguish a tabular Dataset from a Graph, which is why that intermediate level is not declared yet -- one client does not pay for a level of hierarchy." ;
|
rdfs:comment "Domain deliberately not declared, and the shapes must keep this OPTIONAL: Neo4j has no schema and BigQuery calls the level a dataset. A minCount 1 would break on the first harvest outside Snowflake. It is also the only property that would distinguish a tabular Dataset from a Graph, which is why that intermediate level is not declared yet -- one client does not pay for a level of hierarchy." ;
|
||||||
|
rdfs:domain pr:DataStructure ;
|
||||||
rdfs:range pr:Schema .
|
rdfs:range pr:Schema .
|
||||||
|
|
||||||
pr:isNullable a owl:DatatypeProperty ;
|
pr:isNullable a owl:DatatypeProperty ;
|
||||||
rdfs:label "is Nullable" ;
|
rdfs:label "is Nullable" ;
|
||||||
|
rdfs:domain pr:Field ;
|
||||||
rdfs:range xsd:boolean .
|
rdfs:range xsd:boolean .
|
||||||
|
|
||||||
pr:lastQueryDate a owl:DatatypeProperty ;
|
pr:lastQueryDate a owl:DatatypeProperty ;
|
||||||
rdfs:label "last Query Date" ;
|
rdfs:label "last Query Date" ;
|
||||||
rdfs:comment "Lets the model say 'documented but unused for eight months'. A rationalisation argument that costs nothing and is denominated in warehouse spend." ;
|
rdfs:comment "Lets the model say 'documented but unused for eight months'. A rationalisation argument that costs nothing and is denominated in warehouse spend." ;
|
||||||
|
rdfs:domain pr:DataStructure ;
|
||||||
rdfs:range xsd:dateTime ;
|
rdfs:range xsd:dateTime ;
|
||||||
pr:harvestSource "SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY" .
|
pr:harvestSource "SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY" .
|
||||||
|
|
||||||
pr:lastReviewDate a owl:DatatypeProperty ;
|
pr:lastReviewDate a owl:DatatypeProperty ;
|
||||||
rdfs:label "last Review Date" ;
|
rdfs:label "last Review Date" ;
|
||||||
rdfs:range xsd:date .
|
rdfs:range xsd:date ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:logicalFormat a owl:DatatypeProperty ;
|
pr:logicalFormat a owl:DatatypeProperty ;
|
||||||
rdfs:label "logical Format" ;
|
rdfs:label "logical Format" ;
|
||||||
|
rdfs:domain pr:DataElement ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:measures a owl:ObjectProperty ;
|
pr:measures a owl:ObjectProperty ;
|
||||||
@@ -340,6 +358,7 @@ pr:monitoredBy a owl:ObjectProperty ;
|
|||||||
Says what the steward DOES, where the deprecated hasSteward only stated a membership.
|
Says what the steward DOES, where the deprecated hasSteward only stated a membership.
|
||||||
|
|
||||||
DataProduct is the one exception to inheritance: it packages Data Objects belonging to several Business Objects, so it cannot inherit a single steward and declares its own.""" ;
|
DataProduct is the one exception to inheritance: it packages Data Objects belonging to several Business Objects, so it cannot inherit a single steward and declares its own.""" ;
|
||||||
|
rdfs:domain pr:BusinessObject ;
|
||||||
rdfs:range pr:DataSteward .
|
rdfs:range pr:DataSteward .
|
||||||
|
|
||||||
pr:operatedBy a owl:FunctionalProperty,
|
pr:operatedBy a owl:FunctionalProperty,
|
||||||
@@ -351,11 +370,13 @@ pr:operatedBy a owl:FunctionalProperty,
|
|||||||
|
|
||||||
pr:ordinalPosition a owl:DatatypeProperty ;
|
pr:ordinalPosition a owl:DatatypeProperty ;
|
||||||
rdfs:label "ordinal Position" ;
|
rdfs:label "ordinal Position" ;
|
||||||
|
rdfs:domain pr:Field ;
|
||||||
rdfs:range xsd:integer .
|
rdfs:range xsd:integer .
|
||||||
|
|
||||||
pr:ownedBy a owl:ObjectProperty ;
|
pr:ownedBy a owl:ObjectProperty ;
|
||||||
rdfs:label "owned By" ;
|
rdfs:label "owned By" ;
|
||||||
rdfs:range pr:Actor .
|
rdfs:range pr:Actor ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:ownedByDomain a owl:FunctionalProperty,
|
pr:ownedByDomain a owl:FunctionalProperty,
|
||||||
owl:ObjectProperty ;
|
owl:ObjectProperty ;
|
||||||
@@ -363,7 +384,8 @@ pr:ownedByDomain a owl:FunctionalProperty,
|
|||||||
rdfs:comment """The single Data Domain with authority to define the object's rule. Absent on Captured Objects: a Snowflake column is attributed through the Data Element mapped to it, never asserted directly.
|
rdfs:comment """The single Data Domain with authority to define the object's rule. Absent on Captured Objects: a Snowflake column is attributed through the Data Element mapped to it, never asserted directly.
|
||||||
|
|
||||||
Unlike the steward, this one is RE-DECLARABLE on a Data Element. It is inherited by default from the parent chain, but OW-002 attributes an element to the domain that defines its RULE, and a borrowed element -- Brand Code sitting in a Sell Out fact object -- has its rule elsewhere. A Data Element whose owningDomain differs from its parent's is a borrowed element: a signal to check against its represents, not an anomaly to fix.""" ;
|
Unlike the steward, this one is RE-DECLARABLE on a Data Element. It is inherited by default from the parent chain, but OW-002 attributes an element to the domain that defines its RULE, and a borrowed element -- Brand Code sitting in a Sell Out fact object -- has its rule elsewhere. A Data Element whose owningDomain differs from its parent's is a borrowed element: a signal to check against its represents, not an anomaly to fix.""" ;
|
||||||
rdfs:range pr:DataDomain .
|
rdfs:range pr:DataDomain ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:packages a owl:ObjectProperty ;
|
pr:packages a owl:ObjectProperty ;
|
||||||
rdfs:label "packages" ;
|
rdfs:label "packages" ;
|
||||||
@@ -371,15 +393,27 @@ pr:packages a owl:ObjectProperty ;
|
|||||||
rdfs:domain pr:DataProduct ;
|
rdfs:domain pr:DataProduct ;
|
||||||
rdfs:range pr:DataObject .
|
rdfs:range pr:DataObject .
|
||||||
|
|
||||||
|
pr:personName a owl:DatatypeProperty ;
|
||||||
|
rdfs:label "person Name" ;
|
||||||
|
rdfs:comment "The name of the person currently holding the post. Declared on Actor rather than on each role, since every role shares it and Actor is their smallest common ancestor below the layer root. Held as a value rather than a node because what changes is the person, not the assignment: the post keeps its identifier, its domain and every edge that points at it, and a change of holder rewrites one literal. Used on actors and nowhere else." ;
|
||||||
|
rdfs:domain pr:Actor ;
|
||||||
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:physicalDataType a owl:DatatypeProperty ;
|
pr:physicalDataType a owl:DatatypeProperty ;
|
||||||
rdfs:label "physical Data Type" ;
|
rdfs:label "physical Data Type" ;
|
||||||
|
rdfs:domain pr:Field ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:physicalName a owl:DatatypeProperty,
|
pr:physicalName a owl:DatatypeProperty,
|
||||||
owl:FunctionalProperty ;
|
owl:FunctionalProperty ;
|
||||||
rdfs:label "physical Name" ;
|
rdfs:label "physical Name" ;
|
||||||
rdfs:comment "Recorded verbatim, never altered (NR-009). Must NOT be carried by a Metric - that is a layer leak; under BR-013 the physical name belongs on the Data Element." ;
|
rdfs:comment "Recorded verbatim, never altered (NR-009). Must NOT be carried by a Metric - that is a layer leak; under BR-013 the physical name belongs on the Data Element." ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
|
pr:polymorphic a owl:AnnotationProperty ;
|
||||||
|
rdfs:label "polymorphic" ;
|
||||||
|
rdfs:comment "Declares that a property deliberately carries no rdfs:domain because it serves several classes with no useful common ancestor. Its scope is controlled class by class in SHACL instead. Without this axiom a deliberate omission and a forgotten one look identical, and no check can tell them apart." .
|
||||||
|
|
||||||
pr:primarilyStoredIn a owl:FunctionalProperty,
|
pr:primarilyStoredIn a owl:FunctionalProperty,
|
||||||
owl:ObjectProperty ;
|
owl:ObjectProperty ;
|
||||||
@@ -391,8 +425,7 @@ A SUB-PROPERTY of storedIn on purpose, not a parallel relation. Entailment then
|
|||||||
Primary and replica rather than master and slave: the industry has moved on, and it reads better to the business.""" ;
|
Primary and replica rather than master and slave: the industry has moved on, and it reads better to the business.""" ;
|
||||||
rdfs:domain pr:DataElement ;
|
rdfs:domain pr:DataElement ;
|
||||||
rdfs:range pr:Field ;
|
rdfs:range pr:Field ;
|
||||||
rdfs:subPropertyOf pr:storedIn ;
|
rdfs:subPropertyOf pr:storedIn .
|
||||||
pr:authoringMode "PROPOSED_CONFIRMED" .
|
|
||||||
|
|
||||||
pr:produces a owl:ObjectProperty ;
|
pr:produces a owl:ObjectProperty ;
|
||||||
rdfs:label "produces" ;
|
rdfs:label "produces" ;
|
||||||
@@ -407,6 +440,7 @@ pr:qualifiedBy a owl:ObjectProperty ;
|
|||||||
|
|
||||||
pr:queryCount a owl:DatatypeProperty ;
|
pr:queryCount a owl:DatatypeProperty ;
|
||||||
rdfs:label "query Count" ;
|
rdfs:label "query Count" ;
|
||||||
|
rdfs:domain pr:DataStructure ;
|
||||||
rdfs:range xsd:integer .
|
rdfs:range xsd:integer .
|
||||||
|
|
||||||
pr:referencesSourceField a owl:ObjectProperty ;
|
pr:referencesSourceField a owl:ObjectProperty ;
|
||||||
@@ -430,7 +464,8 @@ Range is deliberately left open and scoped by SHACL per class, because the two u
|
|||||||
|
|
||||||
A Data Element uses this ONLY when it is dimensional. A measure element reaches meaning through Metric -> computedBy, which says more: it names WHICH metric the meaning travels through. Never both routes on one element.
|
A Data Element uses this ONLY when it is dimensional. A measure element reaches meaning through Metric -> computedBy, which says more: it names WHICH metric the meaning travels through. Never both routes on one element.
|
||||||
|
|
||||||
Functional at DataObject level only -- not declared owl:FunctionalProperty here, since a single global cardinality would be wrong for one of the two uses.""" .
|
Functional at DataObject level only -- not declared owl:FunctionalProperty here, since a single global cardinality would be wrong for one of the two uses.""" ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:servedBy a owl:ObjectProperty ;
|
pr:servedBy a owl:ObjectProperty ;
|
||||||
rdfs:label "served By" ;
|
rdfs:label "served By" ;
|
||||||
@@ -438,6 +473,12 @@ pr:servedBy a owl:ObjectProperty ;
|
|||||||
rdfs:domain pr:DataInterface ;
|
rdfs:domain pr:DataInterface ;
|
||||||
rdfs:range pr:DataStructure .
|
rdfs:range pr:DataStructure .
|
||||||
|
|
||||||
|
pr:serviceCommitment a owl:DatatypeProperty ;
|
||||||
|
rdfs:label "service Commitment" ;
|
||||||
|
rdfs:comment "What the contract guarantees about the product it covers: refresh schedule, freshness, quality thresholds, support channel, notice period on a breaking change. Held as prose for now, which no shape can verify; the structured form, aligned on ODCS, is the next step. Recorded here because the commitment had been written into businessRule for want of anywhere else, where it sat next to genuine business rules and could not be told apart." ;
|
||||||
|
rdfs:domain pr:DataContract ;
|
||||||
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:shortLabel a owl:AnnotationProperty,
|
pr:shortLabel a owl:AnnotationProperty,
|
||||||
owl:FunctionalProperty ;
|
owl:FunctionalProperty ;
|
||||||
rdfs:label "short Label" ;
|
rdfs:label "short Label" ;
|
||||||
@@ -450,7 +491,8 @@ pr:sourceIdentifier a owl:DatatypeProperty,
|
|||||||
owl:FunctionalProperty ;
|
owl:FunctionalProperty ;
|
||||||
rdfs:label "source Identifier" ;
|
rdfs:label "source Identifier" ;
|
||||||
rdfs:comment "Native immutable id in the source system (Snowflake object id, Tableau LUID, Looker id). Makes reconciliation idempotent and survives renames, which names do not." ;
|
rdfs:comment "Native immutable id in the source system (Snowflake object id, Tableau LUID, Looker id). Makes reconciliation idempotent and survives renames, which names do not." ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:sourcedFrom a owl:ObjectProperty ;
|
pr:sourcedFrom a owl:ObjectProperty ;
|
||||||
rdfs:label "sourced From" ;
|
rdfs:label "sourced From" ;
|
||||||
@@ -459,28 +501,34 @@ pr:sourcedFrom a owl:ObjectProperty ;
|
|||||||
Domain deliberately omitted -- it applies to Data Elements, Data Structures, Metrics and BI Data Sources alike, and the scope lives in the shapes.
|
Domain deliberately omitted -- it applies to Data Elements, Data Structures, Metrics and BI Data Sources alike, and the scope lives in the shapes.
|
||||||
|
|
||||||
It also settles a naming collision. As a string, "source" meant a system here and a BI surface in BIDataSource, two readings of one word. As a relation towards System, the two are different kinds of thing and the ambiguity disappears.""" ;
|
It also settles a naming collision. As a string, "source" meant a system here and a BI surface in BIDataSource, two readings of one word. As a relation towards System, the two are different kinds of thing and the ambiguity disappears.""" ;
|
||||||
rdfs:range pr:System .
|
rdfs:range pr:System ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:status a owl:DatatypeProperty ;
|
pr:status a owl:DatatypeProperty ;
|
||||||
rdfs:label "status" ;
|
rdfs:label "status" ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:synonym a owl:DatatypeProperty ;
|
pr:synonym a owl:DatatypeProperty ;
|
||||||
rdfs:label "synonym" ;
|
rdfs:label "synonym" ;
|
||||||
|
rdfs:domain pr:BusinessConcept ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:technicalDefinition a owl:DatatypeProperty ;
|
pr:technicalDefinition a owl:DatatypeProperty ;
|
||||||
rdfs:label "technical Definition" ;
|
rdfs:label "technical Definition" ;
|
||||||
|
rdfs:domain pr:BusinessConcept ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:timeAggregation a owl:DatatypeProperty ;
|
pr:timeAggregation a owl:DatatypeProperty ;
|
||||||
rdfs:label "time Aggregation" ;
|
rdfs:label "time Aggregation" ;
|
||||||
|
rdfs:domain pr:Metric ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:unit a owl:DatatypeProperty ;
|
pr:unit a owl:DatatypeProperty ;
|
||||||
rdfs:label "unit" ;
|
rdfs:label "unit" ;
|
||||||
rdfs:comment "From the Units LoV. Note the LoV currently mixes physical units, currencies (ISO 4217) and value types (Index, Rate, Count) - three different concepts that should eventually be split." ;
|
rdfs:comment "From the Units LoV. Note the LoV currently mixes physical units, currencies (ISO 4217) and value types (Index, Rate, Count) - three different concepts that should eventually be split." ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:usesBIDataSource a owl:ObjectProperty ;
|
pr:usesBIDataSource a owl:ObjectProperty ;
|
||||||
rdfs:label "uses BI Data Source" ;
|
rdfs:label "uses BI Data Source" ;
|
||||||
@@ -495,23 +543,19 @@ pr:usesConcept a owl:ObjectProperty ;
|
|||||||
|
|
||||||
pr:version a owl:DatatypeProperty ;
|
pr:version a owl:DatatypeProperty ;
|
||||||
rdfs:label "version" ;
|
rdfs:label "version" ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string ;
|
||||||
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:viewDefinition a owl:DatatypeProperty ;
|
pr:viewDefinition a owl:DatatypeProperty ;
|
||||||
rdfs:label "view Definition" ;
|
rdfs:label "view Definition" ;
|
||||||
|
rdfs:domain pr:View ;
|
||||||
rdfs:range xsd:string .
|
rdfs:range xsd:string .
|
||||||
|
|
||||||
pr:BaseTable a owl:Class ;
|
pr:BaseTable a owl:Class ;
|
||||||
rdfs:label "Base Table" ;
|
rdfs:label "Base Table" ;
|
||||||
rdfs:comment "A structure that physically holds its rows, as opposed to one that derives them. The common case in a warehouse Gold layer, and where impact analysis usually bottoms out." ;
|
rdfs:comment "A structure that physically holds its rows, as opposed to one that derives them. The common case in a warehouse Gold layer, and where impact analysis usually bottoms out." ;
|
||||||
rdfs:subClassOf pr:DataStructure ;
|
rdfs:subClassOf pr:DataStructure ;
|
||||||
pr:isAbstract false .
|
pr:authoringMode "HARVESTED" ;
|
||||||
|
|
||||||
pr:DataContract a owl:Class ;
|
|
||||||
rdfs:label "Data Contract" ;
|
|
||||||
rdfs:comment "The service commitment attached to a Data Product: schema, freshness, quality thresholds, support, breaking-change notice period. Candidate alignment: ODCS (Bitol)." ;
|
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
|
||||||
pr:DeliveryLayerObject ;
|
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:DataDomainOwner a owl:Class ;
|
pr:DataDomainOwner a owl:Class ;
|
||||||
@@ -519,6 +563,7 @@ pr:DataDomainOwner a owl:Class ;
|
|||||||
rdfs:comment "Accountable for a Data Domain. Exactly one per domain." ;
|
rdfs:comment "Accountable for a Data Domain. Exactly one per domain." ;
|
||||||
rdfs:subClassOf pr:Actor ;
|
rdfs:subClassOf pr:Actor ;
|
||||||
pr:acronym "DDO" ;
|
pr:acronym "DDO" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "Domain Owner" .
|
pr:shortLabel "Domain Owner" .
|
||||||
|
|
||||||
@@ -527,6 +572,7 @@ pr:DataGovernanceLead a owl:Class ;
|
|||||||
rdfs:comment "Runs governance for a Data Domain. The acronym belongs in hasAcronym, not in the label (rule NR-005)." ;
|
rdfs:comment "Runs governance for a Data Domain. The acronym belongs in hasAcronym, not in the label (rule NR-005)." ;
|
||||||
rdfs:subClassOf pr:Actor ;
|
rdfs:subClassOf pr:Actor ;
|
||||||
pr:acronym "DGL" ;
|
pr:acronym "DGL" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "Governance Lead" .
|
pr:shortLabel "Governance Lead" .
|
||||||
|
|
||||||
@@ -535,6 +581,7 @@ pr:DataProductOwner a owl:Class ;
|
|||||||
rdfs:comment "Runs a Data Product day to day: releases, changelog, support, consumer communication. Belongs to the Sub-Domain that operates the product." ;
|
rdfs:comment "Runs a Data Product day to day: releases, changelog, support, consumer communication. Belongs to the Sub-Domain that operates the product." ;
|
||||||
rdfs:subClassOf pr:Actor ;
|
rdfs:subClassOf pr:Actor ;
|
||||||
pr:acronym "PO" ;
|
pr:acronym "PO" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "Product Owner" .
|
pr:shortLabel "Product Owner" .
|
||||||
|
|
||||||
@@ -542,6 +589,8 @@ pr:DataSteward a owl:Class ;
|
|||||||
rdfs:label "Data Steward" ;
|
rdfs:label "Data Steward" ;
|
||||||
rdfs:comment "Monitors a Business Object. Declared there and nowhere else: Data Objects and Data Elements inherit their steward, so that no column can ever have two answers to 'who watches this'." ;
|
rdfs:comment "Monitors a Business Object. Declared there and nowhere else: Data Objects and Data Elements inherit their steward, so that no column can ever have two answers to 'who watches this'." ;
|
||||||
rdfs:subClassOf pr:Actor ;
|
rdfs:subClassOf pr:Actor ;
|
||||||
|
pr:acronym "DST" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "Steward" .
|
pr:shortLabel "Steward" .
|
||||||
|
|
||||||
@@ -550,6 +599,7 @@ pr:DataSubDomainOwner a owl:Class ;
|
|||||||
rdfs:comment "Accountable for a Sub-Domain, exactly one per sub-domain. Named to match Data Domain Owner: the two roles are the same responsibility at two levels of the tree, and calling one 'Data Domain Owner' and the other 'Sub-Domain Owner' suggested a difference that does not exist." ;
|
rdfs:comment "Accountable for a Sub-Domain, exactly one per sub-domain. Named to match Data Domain Owner: the two roles are the same responsibility at two levels of the tree, and calling one 'Data Domain Owner' and the other 'Sub-Domain Owner' suggested a difference that does not exist." ;
|
||||||
rdfs:subClassOf pr:Actor ;
|
rdfs:subClassOf pr:Actor ;
|
||||||
pr:acronym "SDO" ;
|
pr:acronym "SDO" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "Sub Domain Owner" .
|
pr:shortLabel "Sub Domain Owner" .
|
||||||
|
|
||||||
@@ -557,12 +607,14 @@ pr:ExternalTable a owl:Class ;
|
|||||||
rdfs:label "External Table" ;
|
rdfs:label "External Table" ;
|
||||||
rdfs:comment "Backed by files outside the warehouse. Marks the boundary where lineage leaves Snowflake." ;
|
rdfs:comment "Backed by files outside the warehouse. Marks the boundary where lineage leaves Snowflake." ;
|
||||||
rdfs:subClassOf pr:DataStructure ;
|
rdfs:subClassOf pr:DataStructure ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:GovernanceLayerObject a owl:Class ;
|
pr:GovernanceLayerObject a owl:Class ;
|
||||||
rdfs:label "Governance Layer Object" ;
|
rdfs:label "Governance Layer Object" ;
|
||||||
rdfs:comment "Root of the governance layer. Holds the actors and governance objects that are not data objects and therefore sit outside the data layers. Created by insertion above Actor rather than by renaming it, so that every range pointing at Actor keeps stating the nature of its target rather than its position in the model." ;
|
rdfs:comment "Root of the governance layer. Holds the actors and governance objects that are not data objects and therefore sit outside the data layers. Created by insertion above Actor rather than by renaming it, so that every range pointing at Actor keeps stating the nature of its target rather than its position in the model." ;
|
||||||
rdfs:subClassOf pr:MetaModelObject ;
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
|
pr:MetaModelObject ;
|
||||||
pr:isAbstract true ;
|
pr:isAbstract true ;
|
||||||
pr:shortLabel "Governance Layer" .
|
pr:shortLabel "Governance Layer" .
|
||||||
|
|
||||||
@@ -570,12 +622,7 @@ pr:PrimaryKey a owl:Class ;
|
|||||||
rdfs:label "Primary Key" ;
|
rdfs:label "Primary Key" ;
|
||||||
rdfs:comment "The column or set of columns whose values make a row unique. Once harvested it makes a declared grain checkable: a hasGrainElement that does not match the real key is an automatic finding." ;
|
rdfs:comment "The column or set of columns whose values make a row unique. Once harvested it makes a declared grain checkable: a hasGrainElement that does not match the real key is an automatic finding." ;
|
||||||
rdfs:subClassOf pr:KeyConstraint ;
|
rdfs:subClassOf pr:KeyConstraint ;
|
||||||
pr:isAbstract false .
|
pr:authoringMode "HARVESTED" ;
|
||||||
|
|
||||||
pr:View a owl:Class ;
|
|
||||||
rdfs:label "View" ;
|
|
||||||
rdfs:comment "Depends on other relations through dependsOn, so impact is transitive." ;
|
|
||||||
rdfs:subClassOf pr:DataStructure ;
|
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:isInBIDataSource a owl:FunctionalProperty,
|
pr:isInBIDataSource a owl:FunctionalProperty,
|
||||||
@@ -601,13 +648,14 @@ Range left open and scoped by SHACL per class, because the two uses have differe
|
|||||||
THE SEAM. This is the one relation crossing from what governance defines to what a harvester captures, and the one thing a steward confirms: the reconciliation job proposes the mapping on fully qualified name and type, a person accepts it. It is also what makes end-to-end impact analysis possible.
|
THE SEAM. This is the one relation crossing from what governance defines to what a harvester captures, and the one thing a steward confirms: the reconciliation job proposes the mapping on fully qualified name and type, a person accepts it. It is also what makes end-to-end impact analysis possible.
|
||||||
|
|
||||||
Not functional: the same object exists in DEV, UAT and PROD, and a Data Element may sit in several Fields -- see primaryLocation for the authoritative one. Renamed from physicalizedIn, which was jargon.""" ;
|
Not functional: the same object exists in DEV, UAT and PROD, and a Data Element may sit in several Fields -- see primaryLocation for the authoritative one. Renamed from physicalizedIn, which was jargon.""" ;
|
||||||
pr:authoringMode "PROPOSED_CONFIRMED" .
|
pr:polymorphic true .
|
||||||
|
|
||||||
pr:BusinessIntelligenceReport a owl:Class ;
|
pr:BusinessIntelligenceReport a owl:Class ;
|
||||||
rdfs:label "Business Intelligence Report" ;
|
rdfs:label "Business Intelligence Report" ;
|
||||||
rdfs:comment "What a Data Governance Lead actually cares about in an impact analysis: not that a column changed, but which reports break and who to warn." ;
|
rdfs:comment "What a Data Governance Lead actually cares about in an impact analysis: not that a column changed, but which reports break and who to warn." ;
|
||||||
rdfs:subClassOf pr:CapturedObject,
|
rdfs:subClassOf pr:CapturedObject,
|
||||||
pr:ConsumptionLayerObject ;
|
pr:ConsumptionLayerObject ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:harvestSource "Tableau Workbook/Sheet ; Looker Look/Dashboard" ;
|
pr:harvestSource "Tableau Workbook/Sheet ; Looker Look/Dashboard" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "BI Report" .
|
pr:shortLabel "BI Report" .
|
||||||
@@ -617,16 +665,26 @@ pr:BusinessIntelligenceWorkspace a owl:Class ;
|
|||||||
rdfs:comment "The container a BI tool organises its content in: a Tableau project or site, a Looker model. Useful mainly for attribution -- it is where you look to find who publishes what." ;
|
rdfs:comment "The container a BI tool organises its content in: a Tableau project or site, a Looker model. Useful mainly for attribution -- it is where you look to find who publishes what." ;
|
||||||
rdfs:subClassOf pr:CapturedObject,
|
rdfs:subClassOf pr:CapturedObject,
|
||||||
pr:ConsumptionLayerObject ;
|
pr:ConsumptionLayerObject ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:harvestSource "Tableau Metadata API (Project/Site) ; Looker API (Model)" ;
|
pr:harvestSource "Tableau Metadata API (Project/Site) ; Looker API (Model)" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "BI Workspace" .
|
pr:shortLabel "BI Workspace" .
|
||||||
|
|
||||||
|
pr:DataContract a owl:Class ;
|
||||||
|
rdfs:label "Data Contract" ;
|
||||||
|
rdfs:comment "The service commitment attached to a Data Product: schema, freshness, quality thresholds, support, breaking-change notice period. Candidate alignment: ODCS (Bitol)." ;
|
||||||
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
|
pr:DeliveryLayerObject ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:DataSubDomain a owl:Class ;
|
pr:DataSubDomain a owl:Class ;
|
||||||
rdfs:label "Data Sub Domain" ;
|
rdfs:label "Data Sub Domain" ;
|
||||||
rdfs:comment "A coherent slice of a Data Domain, grouping the business objects that belong together: Sell Out inside Sales Performance, Fiscal Calendar inside Financial Performance. It exists so that governance can be delegated and reviewed at a size a person can hold in their head." ;
|
rdfs:comment "A coherent slice of a Data Domain, grouping the business objects that belong together: Sell Out inside Sales Performance, Fiscal Calendar inside Financial Performance. It exists so that governance can be delegated and reviewed at a size a person can hold in their head." ;
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
pr:OwnershipLayerObject ;
|
pr:OwnershipLayerObject ;
|
||||||
pr:acronym "SD" ;
|
pr:acronym "SD" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "Sub Domain" .
|
pr:shortLabel "Sub Domain" .
|
||||||
|
|
||||||
@@ -635,6 +693,7 @@ pr:Database a owl:Class ;
|
|||||||
rdfs:comment "The top container of a storage engine: a Snowflake database, a Databricks catalog, a BigQuery project. Named differently by every vendor, which is why the class carries the generic name and the vendor word lives in the harvest annotation." ;
|
rdfs:comment "The top container of a storage engine: a Snowflake database, a Databricks catalog, a BigQuery project. Named differently by every vendor, which is why the class carries the generic name and the vendor word lives in the harvest annotation." ;
|
||||||
rdfs:subClassOf pr:CapturedObject,
|
rdfs:subClassOf pr:CapturedObject,
|
||||||
pr:PhysicalLayerObject ;
|
pr:PhysicalLayerObject ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.DATABASES" ;
|
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.DATABASES" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
@@ -643,16 +702,15 @@ pr:Schema a owl:Class ;
|
|||||||
rdfs:comment "The namespace grouping structures inside a database. Optional on purpose: Neo4j has no schema and BigQuery calls the level a dataset, so nothing in the shapes may require it." ;
|
rdfs:comment "The namespace grouping structures inside a database. Optional on purpose: Neo4j has no schema and BigQuery calls the level a dataset, so nothing in the shapes may require it." ;
|
||||||
rdfs:subClassOf pr:CapturedObject,
|
rdfs:subClassOf pr:CapturedObject,
|
||||||
pr:PhysicalLayerObject ;
|
pr:PhysicalLayerObject ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.SCHEMATA" ;
|
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.SCHEMATA" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:Transformation a owl:Class ;
|
pr:View a owl:Class ;
|
||||||
rdfs:label "Transformation" ;
|
rdfs:label "View" ;
|
||||||
rdfs:comment "The code that turns inputs into outputs: a dbt model, a Snowflake task, an ADF pipeline. Subclass of prov:Activity so that lineage is expressed in a standard vocabulary rather than a private one." ;
|
rdfs:comment "Depends on other relations through dependsOn, so impact is transitive." ;
|
||||||
rdfs:subClassOf prov:Activity,
|
rdfs:subClassOf pr:DataStructure ;
|
||||||
pr:CapturedObject,
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:PhysicalLayerObject ;
|
|
||||||
pr:harvestSource "dbt manifest.json, Snowflake TASKS, Azure Data Factory pipelines" ;
|
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:BusinessLayerObject a owl:Class ;
|
pr:BusinessLayerObject a owl:Class ;
|
||||||
@@ -667,12 +725,14 @@ pr:DataInterface a owl:Class ;
|
|||||||
rdfs:comment "The channel through which a consumer actually gets the data: a Snowflake secure share or listing, a consumption schema with its grants, a file extract to a panel partner, an API or reverse-ETL feed. Not a Data Object (that is structure) and not a BI Data Source (that is one consumer among others). Verifiable: a grant, a share or an endpoint either exists or it does not. The output port in data-mesh terms." ;
|
rdfs:comment "The channel through which a consumer actually gets the data: a Snowflake secure share or listing, a consumption schema with its grants, a file extract to a panel partner, an API or reverse-ETL feed. Not a Data Object (that is structure) and not a BI Data Source (that is one consumer among others). Verifiable: a grant, a share or an endpoint either exists or it does not. The output port in data-mesh terms." ;
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
pr:DeliveryLayerObject ;
|
pr:DeliveryLayerObject ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:ForeignKey a owl:Class ;
|
pr:ForeignKey a owl:Class ;
|
||||||
rdfs:label "Foreign Key" ;
|
rdfs:label "Foreign Key" ;
|
||||||
rdfs:comment "A column, or set of columns, whose values must exist in another structure. Rule OW-006 keeps it out of the Data Elements -- it carries no business notion of its own, it is joining plumbing -- but it still needs somewhere to live, and here it makes the asserted qualifiedBy between Data Objects checkable against what the warehouse really enforces." ;
|
rdfs:comment "A column, or set of columns, whose values must exist in another structure. Rule OW-006 keeps it out of the Data Elements -- it carries no business notion of its own, it is joining plumbing -- but it still needs somewhere to live, and here it makes the asserted qualifiedBy between Data Objects checkable against what the warehouse really enforces." ;
|
||||||
rdfs:subClassOf pr:KeyConstraint ;
|
rdfs:subClassOf pr:KeyConstraint ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:KeyConstraint a owl:Class ;
|
pr:KeyConstraint a owl:Class ;
|
||||||
@@ -698,49 +758,36 @@ pr:System a owl:Class ;
|
|||||||
pr:authoringMode "ASSERTED" ;
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:BusinessConcept a owl:Class ;
|
pr:Transformation a owl:Class ;
|
||||||
rdfs:label "Business Concept" ;
|
rdfs:label "Transformation" ;
|
||||||
rdfs:comment "A shared business notion WITHOUT a formula. The presence of a formula is the sole discriminator against Metric (rule BR-003)." ;
|
rdfs:comment "The code that turns inputs into outputs: a dbt model, a Snowflake task, an ADF pipeline. Subclass of prov:Activity so that lineage is expressed in a standard vocabulary rather than a private one." ;
|
||||||
rdfs:subClassOf pr:BusinessLayerObject,
|
rdfs:subClassOf prov:Activity,
|
||||||
pr:DefinedObject ;
|
pr:CapturedObject,
|
||||||
pr:isAbstract false ;
|
pr:PhysicalLayerObject ;
|
||||||
pr:shortLabel "Concept" .
|
pr:authoringMode "HARVESTED" ;
|
||||||
|
pr:harvestSource "dbt manifest.json, Snowflake TASKS, Azure Data Factory pipelines" ;
|
||||||
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:BusinessIntelligenceDataSource a owl:Class ;
|
pr:BusinessIntelligenceDataSource a owl:Class ;
|
||||||
rdfs:label "Business Intelligence Data Source" ;
|
rdfs:label "Business Intelligence Data Source" ;
|
||||||
rdfs:comment "The semantic surface a report is built on. This is where BI meets the warehouse." ;
|
rdfs:comment "The semantic surface a report is built on. This is where BI meets the warehouse." ;
|
||||||
rdfs:subClassOf pr:CapturedObject,
|
rdfs:subClassOf pr:CapturedObject,
|
||||||
pr:ConsumptionLayerObject ;
|
pr:ConsumptionLayerObject ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:harvestSource "Tableau Published Datasource ; Looker Explore" ;
|
pr:harvestSource "Tableau Published Datasource ; Looker Explore" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "BI Data Source" .
|
pr:shortLabel "BI Data Source" .
|
||||||
|
|
||||||
pr:BusinessObject a owl:Class ;
|
|
||||||
rdfs:label "Business Object" ;
|
|
||||||
rdfs:comment "The single anchor of accountability, which is why it stays in the Ownership layer: it is the one object where a steward is declared, and Data Objects and Data Elements inherit from it. It carries NO business definition of its own -- its meaning comes from its aboutConcept. What it does carry is a perimeter: the subject it is about, and the notions needed to read it." ;
|
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
|
||||||
pr:OwnershipLayerObject ;
|
|
||||||
pr:acronym "BO" ;
|
|
||||||
pr:isAbstract false .
|
|
||||||
|
|
||||||
pr:DataDomain a owl:Class ;
|
pr:DataDomain a owl:Class ;
|
||||||
rdfs:label "Data Domain" ;
|
rdfs:label "Data Domain" ;
|
||||||
rdfs:comment "A durable area of the business that owns the meaning of its data: Sales Performance, Product & Material, Financial Performance. It is the unit of accountability -- one owner, one governance lead -- and the top of the categorisation tree. Domains are stable; the projects and systems inside them are not." ;
|
rdfs:comment "A durable area of the business that owns the meaning of its data: Sales Performance, Product & Material, Financial Performance. It is the unit of accountability -- one owner, one governance lead -- and the top of the categorisation tree. Domains are stable; the projects and systems inside them are not." ;
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
pr:OwnershipLayerObject ;
|
pr:OwnershipLayerObject ;
|
||||||
pr:acronym "DD" ;
|
pr:acronym "DD" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "Domain" .
|
pr:shortLabel "Domain" .
|
||||||
|
|
||||||
pr:DataElement a owl:Class ;
|
|
||||||
rdfs:label "Data Element" ;
|
|
||||||
rdfs:comment "The finest defined unit. Its mapping to a physical Field is the ONLY place where the defined graph and the captured graph meet: proposed automatically by name and type matching, confirmed by a Data Steward." ;
|
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
|
||||||
pr:LogicalLayerObject ;
|
|
||||||
pr:authoringMode "PROPOSED_CONFIRMED" ;
|
|
||||||
pr:isAbstract false ;
|
|
||||||
pr:shortLabel "Element" .
|
|
||||||
|
|
||||||
pr:DeliveryLayerObject a owl:Class ;
|
pr:DeliveryLayerObject a owl:Class ;
|
||||||
rdfs:label "Delivery Layer Object" ;
|
rdfs:label "Delivery Layer Object" ;
|
||||||
rdfs:comment "What we promise, to whom, until when. A different question from how the data is structured, hence a layer of its own: version, owner, changelog, support channel, consumers to warn." ;
|
rdfs:comment "What we promise, to whom, until when. A different question from how the data is structured, hence a layer of its own: version, owner, changelog, support channel, consumers to warn." ;
|
||||||
@@ -755,11 +802,30 @@ pr:OwnershipLayerObject a owl:Class ;
|
|||||||
pr:isAbstract true ;
|
pr:isAbstract true ;
|
||||||
pr:shortLabel "Ownership Layer" .
|
pr:shortLabel "Ownership Layer" .
|
||||||
|
|
||||||
|
pr:BusinessObject a owl:Class ;
|
||||||
|
rdfs:label "Business Object" ;
|
||||||
|
rdfs:comment "The single anchor of accountability, which is why it stays in the Ownership layer: it is the one object where a steward is declared, and Data Objects and Data Elements inherit from it. It carries NO business definition of its own -- its meaning comes from its aboutConcept. What it does carry is a perimeter: the subject it is about, and the notions needed to read it." ;
|
||||||
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
|
pr:OwnershipLayerObject ;
|
||||||
|
pr:acronym "BO" ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
|
pr:isAbstract false .
|
||||||
|
|
||||||
|
pr:DataElement a owl:Class ;
|
||||||
|
rdfs:label "Data Element" ;
|
||||||
|
rdfs:comment "The finest defined unit. Its mapping to a physical Field is the ONLY place where the defined graph and the captured graph meet: proposed automatically by name and type matching, confirmed by a Data Steward." ;
|
||||||
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
|
pr:LogicalLayerObject ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
|
pr:isAbstract false ;
|
||||||
|
pr:shortLabel "Element" .
|
||||||
|
|
||||||
pr:BusinessIntelligenceField a owl:Class ;
|
pr:BusinessIntelligenceField a owl:Class ;
|
||||||
rdfs:label "Business Intelligence Field" ;
|
rdfs:label "Business Intelligence Field" ;
|
||||||
rdfs:comment "A field as a business user sees it. exposesMetric is what turns a certified Metric from a definition on paper into something measurable against the real BI estate." ;
|
rdfs:comment "A field as a business user sees it. exposesMetric is what turns a certified Metric from a definition on paper into something measurable against the real BI estate." ;
|
||||||
rdfs:subClassOf pr:CapturedObject,
|
rdfs:subClassOf pr:CapturedObject,
|
||||||
pr:ConsumptionLayerObject ;
|
pr:ConsumptionLayerObject ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:harvestSource "Tableau calculated fields ; LookML measures and dimensions" ;
|
pr:harvestSource "Tableau calculated fields ; LookML measures and dimensions" ;
|
||||||
pr:isAbstract false ;
|
pr:isAbstract false ;
|
||||||
pr:shortLabel "BI Field" .
|
pr:shortLabel "BI Field" .
|
||||||
@@ -769,6 +835,7 @@ pr:DataObject a owl:Class ;
|
|||||||
rdfs:comment "The pivot of the whole model: it represents exactly one Business Object, groups the Data Elements that make it up, and is stored in one or more Data Structures. The single junction between what governance defines and what actually runs -- everything upstream is meaning, everything downstream is machinery." ;
|
rdfs:comment "The pivot of the whole model: it represents exactly one Business Object, groups the Data Elements that make it up, and is stored in one or more Data Structures. The single junction between what governance defines and what actually runs -- everything upstream is meaning, everything downstream is machinery." ;
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
pr:LogicalLayerObject ;
|
pr:LogicalLayerObject ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:DataProduct a owl:Class ;
|
pr:DataProduct a owl:Class ;
|
||||||
@@ -776,13 +843,7 @@ pr:DataProduct a owl:Class ;
|
|||||||
rdfs:comment "The unit of delivery: what carries a version number, an owner, a changelog, a support channel and a list of consumers to warn. It has no single technical counterpart, and should not: physically it often lands on a Snowflake schema or a dbt project, but that is not what defines it. Operated by exactly one Sub-Domain -- it is a consumable, and there must be no ambiguity about who runs it. Candidate alignment: dcat:Dataset / DPROD." ;
|
rdfs:comment "The unit of delivery: what carries a version number, an owner, a changelog, a support channel and a list of consumers to warn. It has no single technical counterpart, and should not: physically it often lands on a Snowflake schema or a dbt project, but that is not what defines it. Operated by exactly one Sub-Domain -- it is a consumable, and there must be no ambiguity about who runs it. Candidate alignment: dcat:Dataset / DPROD." ;
|
||||||
rdfs:subClassOf pr:DefinedObject,
|
rdfs:subClassOf pr:DefinedObject,
|
||||||
pr:DeliveryLayerObject ;
|
pr:DeliveryLayerObject ;
|
||||||
pr:isAbstract false .
|
pr:authoringMode "ASSERTED" ;
|
||||||
|
|
||||||
pr:Metric a owl:Class ;
|
|
||||||
rdfs:label "Metric" ;
|
|
||||||
rdfs:comment "A quantifiable measurement WITH a formula, a unit and a grain. Under BR-013 a Metric carries the harmonised CALCULATION RULE; variants that differ only by analysis context are Data Elements linked back via computedBy." ;
|
|
||||||
rdfs:subClassOf pr:BusinessLayerObject,
|
|
||||||
pr:DefinedObject ;
|
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:SystemType a owl:Class ;
|
pr:SystemType a owl:Class ;
|
||||||
@@ -790,12 +851,6 @@ pr:SystemType a owl:Class ;
|
|||||||
rdfs:comment "What kind of system this is -- source application, master data system, data platform, BI tool, external provider. Coarse on purpose: it exists to group systems in a view, not to model them." ;
|
rdfs:comment "What kind of system this is -- source application, master data system, data platform, BI tool, external provider. Coarse on purpose: it exists to group systems in a view, not to model them." ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:Actor a owl:Class ;
|
|
||||||
rdfs:label "Actor" ;
|
|
||||||
rdfs:comment "A person or a team. Deliberately NOT a MetaModelObject: people are not subject to ownership and lifecycle rules." ;
|
|
||||||
rdfs:subClassOf pr:GovernanceLayerObject ;
|
|
||||||
pr:isAbstract true .
|
|
||||||
|
|
||||||
pr:ConsumptionLayerObject a owl:Class ;
|
pr:ConsumptionLayerObject a owl:Class ;
|
||||||
rdfs:label "Consumption Layer Object" ;
|
rdfs:label "Consumption Layer Object" ;
|
||||||
rdfs:comment "The BI estate. Separate from Physical because BI assets have their own lifecycle and their own owner: a dashboard belongs to its author, not to the Data Domain that owns the underlying data." ;
|
rdfs:comment "The BI estate. Separate from Physical because BI assets have their own lifecycle and their own owner: a dashboard belongs to its author, not to the Data Domain that owns the underlying data." ;
|
||||||
@@ -803,6 +858,29 @@ pr:ConsumptionLayerObject a owl:Class ;
|
|||||||
pr:isAbstract true ;
|
pr:isAbstract true ;
|
||||||
pr:shortLabel "Consumption Layer" .
|
pr:shortLabel "Consumption Layer" .
|
||||||
|
|
||||||
|
pr:Actor a owl:Class ;
|
||||||
|
rdfs:label "Actor" ;
|
||||||
|
rdfs:comment "A person or a team. Deliberately NOT a MetaModelObject: people are not subject to ownership and lifecycle rules." ;
|
||||||
|
rdfs:subClassOf pr:GovernanceLayerObject ;
|
||||||
|
pr:isAbstract true .
|
||||||
|
|
||||||
|
pr:BusinessConcept a owl:Class ;
|
||||||
|
rdfs:label "Business Concept" ;
|
||||||
|
rdfs:comment "A shared business notion WITHOUT a formula. The presence of a formula is the sole discriminator against Metric (rule BR-003)." ;
|
||||||
|
rdfs:subClassOf pr:BusinessLayerObject,
|
||||||
|
pr:DefinedObject ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
|
pr:isAbstract false ;
|
||||||
|
pr:shortLabel "Concept" .
|
||||||
|
|
||||||
|
pr:Metric a owl:Class ;
|
||||||
|
rdfs:label "Metric" ;
|
||||||
|
rdfs:comment "A quantifiable measurement WITH a formula, a unit and a grain. Under BR-013 a Metric carries the harmonised CALCULATION RULE; variants that differ only by analysis context are Data Elements linked back via computedBy." ;
|
||||||
|
rdfs:subClassOf pr:BusinessLayerObject,
|
||||||
|
pr:DefinedObject ;
|
||||||
|
pr:authoringMode "ASSERTED" ;
|
||||||
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:PhysicalLayerObject a owl:Class ;
|
pr:PhysicalLayerObject a owl:Class ;
|
||||||
rdfs:label "Physical Layer Object" ;
|
rdfs:label "Physical Layer Object" ;
|
||||||
rdfs:comment """What actually runs: the systems, containers, structures, columns, keys and code that hold the data.
|
rdfs:comment """What actually runs: the systems, containers, structures, columns, keys and code that hold the data.
|
||||||
@@ -816,19 +894,26 @@ Until v1.4 this class was itself declared a kind of Captured Object, which made
|
|||||||
pr:isAbstract true ;
|
pr:isAbstract true ;
|
||||||
pr:shortLabel "Physical Layer" .
|
pr:shortLabel "Physical Layer" .
|
||||||
|
|
||||||
|
pr:MetaModelObject a owl:Class ;
|
||||||
|
rdfs:label "Meta Model Object" ;
|
||||||
|
rdfs:comment "Anything the meta model describes. Actors are deliberately NOT Meta Model Objects." ;
|
||||||
|
pr:isAbstract true .
|
||||||
|
|
||||||
|
pr:CapturedObject a owl:Class ;
|
||||||
|
rdfs:label "Captured Object" ;
|
||||||
|
rdfs:comment "Read from a running system and rebuilt on every run. Never hand-edited. Carries sourceIdentifier, fullyQualifiedName and harvestedOn instead of declared ownership: attribution is INFERRED through the Data Element it is mapped to, never asserted on the node." ;
|
||||||
|
rdfs:subClassOf pr:MetaModelObject ;
|
||||||
|
pr:isAbstract true .
|
||||||
|
|
||||||
pr:Field a owl:Class ;
|
pr:Field a owl:Class ;
|
||||||
rdfs:label "Field" ;
|
rdfs:label "Field" ;
|
||||||
rdfs:comment "A physical column. The anchor of impact analysis: everything upstream reaches a Field, everything downstream starts from one." ;
|
rdfs:comment "A physical column. The anchor of impact analysis: everything upstream reaches a Field, everything downstream starts from one." ;
|
||||||
rdfs:subClassOf pr:CapturedObject,
|
rdfs:subClassOf pr:CapturedObject,
|
||||||
pr:PhysicalLayerObject ;
|
pr:PhysicalLayerObject ;
|
||||||
|
pr:authoringMode "HARVESTED" ;
|
||||||
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.COLUMNS" ;
|
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.COLUMNS" ;
|
||||||
pr:isAbstract false .
|
pr:isAbstract false .
|
||||||
|
|
||||||
pr:MetaModelObject a owl:Class ;
|
|
||||||
rdfs:label "Meta Model Object" ;
|
|
||||||
rdfs:comment "Anything the meta model describes. Actors are deliberately NOT Meta Model Objects." ;
|
|
||||||
pr:isAbstract true .
|
|
||||||
|
|
||||||
pr:DataStructure a owl:Class ;
|
pr:DataStructure a owl:Class ;
|
||||||
rdfs:label "Data Structure" ;
|
rdfs:label "Data Structure" ;
|
||||||
rdfs:comment """Anything that carries Fields and reads as a set of rows. Never instantiated directly: a harvest types the concrete kind, and a viewer shows that kind rather than this parent.
|
rdfs:comment """Anything that carries Fields and reads as a set of rows. Never instantiated directly: a harvest types the concrete kind, and a viewer shows that kind rather than this parent.
|
||||||
@@ -850,23 +935,21 @@ Existing BaseTable assertions stay DataStructure by entailment, every shape targ
|
|||||||
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.TABLES" ;
|
pr:harvestSource "SNOWFLAKE.INFORMATION_SCHEMA.TABLES" ;
|
||||||
pr:isAbstract true .
|
pr:isAbstract true .
|
||||||
|
|
||||||
pr:CapturedObject a owl:Class ;
|
|
||||||
rdfs:label "Captured Object" ;
|
|
||||||
rdfs:comment "Read from a running system and rebuilt on every run. Never hand-edited. Carries sourceIdentifier, fullyQualifiedName and harvestedOn instead of declared ownership: attribution is INFERRED through the Data Element it is mapped to, never asserted on the node." ;
|
|
||||||
rdfs:subClassOf pr:MetaModelObject ;
|
|
||||||
pr:authoringMode "HARVESTED" ;
|
|
||||||
pr:isAbstract true .
|
|
||||||
|
|
||||||
pr:DefinedObject a owl:Class ;
|
pr:DefinedObject a owl:Class ;
|
||||||
rdfs:label "Defined Object" ;
|
rdfs:label "Defined Object" ;
|
||||||
rdfs:comment "Somebody decided it. Written by governance, edited through a pull request on the domain TTL file, and subject to the full identity and lifecycle shape: identifier, name, owner, owning Data Domain, status, version." ;
|
rdfs:comment "Somebody decided it. Written by governance, edited through a pull request on the domain TTL file, and subject to the full identity and lifecycle shape: identifier, name, owner, owning Data Domain, status, version." ;
|
||||||
rdfs:subClassOf pr:MetaModelObject ;
|
rdfs:subClassOf pr:MetaModelObject ;
|
||||||
pr:authoringMode "ASSERTED" ;
|
|
||||||
pr:isAbstract true .
|
pr:isAbstract true .
|
||||||
|
|
||||||
[] a owl:AllDisjointClasses ;
|
[] a owl:AllDisjointClasses ;
|
||||||
owl:members ( pr:DefinedObject pr:CapturedObject ) .
|
owl:members ( pr:DefinedObject pr:CapturedObject ) .
|
||||||
|
|
||||||
|
[] a owl:AllDisjointClasses ;
|
||||||
|
owl:members ( pr:BusinessIntelligenceWorkspace pr:BusinessIntelligenceDataSource pr:BusinessIntelligenceField pr:BusinessIntelligenceReport ) .
|
||||||
|
|
||||||
|
[] a owl:AllDisjointClasses ;
|
||||||
|
owl:members ( pr:OwnershipLayerObject pr:BusinessLayerObject pr:LogicalLayerObject pr:DeliveryLayerObject pr:PhysicalLayerObject pr:ConsumptionLayerObject ) .
|
||||||
|
|
||||||
[] a owl:AllDisjointClasses ;
|
[] a owl:AllDisjointClasses ;
|
||||||
owl:members ( pr:BusinessObject pr:DataObject pr:DataInterface pr:DataProduct ) .
|
owl:members ( pr:BusinessObject pr:DataObject pr:DataInterface pr:DataProduct ) .
|
||||||
|
|
||||||
@@ -879,9 +962,3 @@ pr:DefinedObject a owl:Class ;
|
|||||||
[] a owl:AllDisjointClasses ;
|
[] a owl:AllDisjointClasses ;
|
||||||
owl:members ( pr:PrimaryKey pr:ForeignKey ) .
|
owl:members ( pr:PrimaryKey pr:ForeignKey ) .
|
||||||
|
|
||||||
[] a owl:AllDisjointClasses ;
|
|
||||||
owl:members ( pr:BusinessIntelligenceWorkspace pr:BusinessIntelligenceDataSource pr:BusinessIntelligenceField pr:BusinessIntelligenceReport ) .
|
|
||||||
|
|
||||||
[] a owl:AllDisjointClasses ;
|
|
||||||
owl:members ( pr:OwnershipLayerObject pr:BusinessLayerObject pr:LogicalLayerObject pr:DeliveryLayerObject pr:PhysicalLayerObject pr:ConsumptionLayerObject ) .
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user