1 Commits

4 changed files with 419 additions and 88 deletions
+133 -79
View File
@@ -67,31 +67,60 @@ ex:SD_10_01 a pr:SubDomain ;
pr:belongsTo ex:DD_10 ; pr:belongsTo ex:DD_10 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" . pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" .
ex:SD_04_01 a pr:SubDomain ; ex:SD_04_01 a pr:SubDomain ;
pr:hasIdentifier "SD-04.01" ; pr:hasName "Customer & Outlet" ; pr:hasName "Customer Structure" ;
pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ; pr:hasIdentifier "SD-04.01" ;
pr:belongsTo ex:DD_04 ; pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" . pr:belongsTo ex:DD_04 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0"
.
ex:SD_16_01 a pr:SubDomain ; ex:SD_16_01 a pr:SubDomain ;
pr:hasIdentifier "SD-16.01" ; pr:hasName "Currency & Exchange Rates" ; pr:hasName "Currency & Exchange Rate" ;
pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:hasIdentifier "SD-16.01" ;
pr:belongsTo ex:DD_16 ; pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" . pr:belongsTo ex:DD_16 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0"
.
ex:SD_16_02 a pr:SubDomain ; ex:SD_16_02 a pr:SubDomain ;
pr:hasIdentifier "SD-16.02" ; pr:hasName "Marketing Entity & Fiscal Calendar" ; pr:hasName "Fiscal Calendar" ;
pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:hasIdentifier "SD-16.02" ;
pr:belongsTo ex:DD_16 ; pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" . pr:belongsTo ex:DD_16 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0"
.
ex:SD_21_01 a pr:SubDomain ; ex:SD_21_01 a pr:SubDomain ;
pr:hasIdentifier "SD-21.01" ; pr:hasName "Calendar" ; pr:hasName "Standard Calendar" ;
pr:owningDomain ex:DD_21 ; pr:ownedBy ex:ST_DD_21 ; pr:hasIdentifier "SD-21.01" ;
pr:belongsTo ex:DD_21 ; pr:owningDomain ex:DD_21 ; pr:ownedBy ex:ST_DD_21 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" . pr:belongsTo ex:DD_21 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0"
.
ex:SD_05_01 a pr:SubDomain ; ex:SD_05_01 a pr:SubDomain ;
pr:hasIdentifier "SD-05.01" ; pr:hasName "Promotion" ; pr:hasIdentifier "SD-05.01" ; pr:hasName "Promotion" ;
pr:owningDomain ex:DD_05 ; pr:ownedBy ex:ST_DD_05 ; pr:owningDomain ex:DD_05 ; pr:ownedBy ex:ST_DD_05 ;
pr:belongsTo ex:DD_05 ; pr:belongsTo ex:DD_05 ;
pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" . pr:hasStatus "PUBLISHED" ; pr:hasVersion "1.0" .
# --- sub-domains from the v1.1 structure proposal --------------------
# 'Customer & Outlet' and 'Marketing Entity & Fiscal Calendar' had the
# same defect as the compound Business Objects: a name joined by '&'
# has no single subject. Same rule, one level up.
ex:SD_04_02 a pr:SubDomain ;
pr:hasIdentifier "SD-04.02" ; pr:hasName "Outlet & Points of Sale" ;
pr:belongsTo ex:DD_04 ; pr:owningDomain ex:DD_04 ;
pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, split of a compound sub-domain" .
ex:SD_04_03 a pr:SubDomain ;
pr:hasIdentifier "SD-04.03" ; pr:hasName "Geography" ;
pr:belongsTo ex:DD_04 ; pr:owningDomain ex:DD_04 ;
pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, split of a compound sub-domain" .
ex:SD_16_03 a pr:SubDomain ;
pr:hasIdentifier "SD-16.03" ; pr:hasName "Organisation Structure" ;
pr:belongsTo ex:DD_16 ; pr:owningDomain ex:DD_16 ;
pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, split of a compound sub-domain" .
ex:BC_06_01_001 a pr:BusinessConcept ; ex:BC_06_01_001 a pr:BusinessConcept ;
pr:hasIdentifier "BC-06.01-001" ; pr:hasName "Sell Out" ; pr:hasIdentifier "BC-06.01-001" ; pr:hasName "Sell Out" ;
pr:owningDomain ex:DD_06 ; pr:ownedBy ex:ST_DD_06 ; pr:owningDomain ex:DD_06 ; pr:ownedBy ex:ST_DD_06 ;
@@ -168,20 +197,22 @@ ex:BC_21_01_001 a pr:BusinessConcept ;
# --- concepts required by the meaning XOR --------------------------- # --- concepts required by the meaning XOR ---------------------------
# Country, Marketing Entity and Fiscal Period had Data Elements but no # Country, Marketing Entity and Fiscal Period had Data Elements but no
# notion behind them. The rule forced them to be named. # notion behind them. The rule forced them to be named.
ex:BC_04_01_003 a pr:BusinessConcept ; ex:BC_04_03_001 a pr:BusinessConcept ;
pr:hasIdentifier "BC-04.01-003" ; pr:hasName "Country" ; pr:hasIdentifier "BC-04.03-001" ; pr:hasName "Country" ;
pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ; pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ;
pr:hasBusinessDefinition "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:hasBusinessDefinition "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:arbitrationStatus "TO_ARBITRATE" ; pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, pending ratification by the owning domain" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, pending ratification by the owning domain" .
ex:BC_16_01_003 a pr:BusinessConcept ; ex:BC_16_03_001 a pr:BusinessConcept ;
pr:hasIdentifier "BC-16.01-003" ; pr:hasName "Marketing Entity" ; pr:hasBusinessRule "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:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:hasIdentifier "BC-16.03-001" ; pr:hasName "Marketing Entity" ;
pr:hasBusinessDefinition "An organisational unit of the group holding commercial responsibility for a market, and the level at which financial results are consolidated." ; pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ;
pr:arbitrationStatus "TO_ARBITRATE" ; pr:hasBusinessDefinition "An organisational unit of the group holding commercial responsibility for a market, and the level at which financial results are consolidated." ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, pending ratification by the owning domain" . pr:arbitrationStatus "TO_ARBITRATE" ;
ex:BC_16_01_004 a pr:BusinessConcept ; pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, pending ratification by the owning domain"
pr:hasIdentifier "BC-16.01-004" ; pr:hasName "Fiscal Period" ; .
ex:BC_16_02_001 a pr:BusinessConcept ;
pr:hasIdentifier "BC-16.02-001" ; pr:hasName "Fiscal Period" ;
pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ;
pr:hasBusinessDefinition "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:hasBusinessDefinition "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:arbitrationStatus "TO_ARBITRATE" ; pr:arbitrationStatus "TO_ARBITRATE" ;
@@ -193,8 +224,6 @@ ex:BO_06_01_001 a pr:BusinessObject ;
pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ; pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ;
pr:aboutConcept ex:BC_06_01_001 ; pr:aboutConcept ex:BC_06_01_001 ;
pr:usesConcept ex:BC_06_01_003 , ex:BC_05_01_001 ; pr:usesConcept ex:BC_06_01_003 , ex:BC_05_01_001 ;
pr:aboutConcept ex:BC_06_01_001 ;
pr:usesConcept ex:BC_06_01_003 , ex:BC_05_01_001 ;
pr:hasMetric ex:M_06_01_001 ; pr:hasMetric ex:M_06_01_001 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT" .
ex:BO_06_01_002 a pr:BusinessObject ; ex:BO_06_01_002 a pr:BusinessObject ;
@@ -203,8 +232,6 @@ ex:BO_06_01_002 a pr:BusinessObject ;
pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ; pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ;
pr:aboutConcept ex:BC_06_01_001 ; pr:aboutConcept ex:BC_06_01_001 ;
pr:usesConcept ex:BC_06_01_003 , ex:BC_05_01_001 , ex:BC_16_01_001 ; pr:usesConcept ex:BC_06_01_003 , ex:BC_05_01_001 , ex:BC_16_01_001 ;
pr:aboutConcept ex:BC_06_01_001 ;
pr:usesConcept ex:BC_06_01_003 , ex:BC_05_01_001 , ex:BC_16_01_001 ;
pr:hasMetric ex:M_06_01_002 ; pr:hasMetric ex:M_06_01_002 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT" .
ex:BO_06_01_003 a pr:BusinessObject ; ex:BO_06_01_003 a pr:BusinessObject ;
@@ -213,8 +240,6 @@ ex:BO_06_01_003 a pr:BusinessObject ;
pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ; pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ;
pr:aboutConcept ex:BC_06_01_004 ; pr:aboutConcept ex:BC_06_01_004 ;
pr:usesConcept ex:BC_06_01_003 ; pr:usesConcept ex:BC_06_01_003 ;
pr:aboutConcept ex:BC_06_01_004 ;
pr:usesConcept ex:BC_06_01_003 ;
pr:hasMetric ex:M_06_01_003 , ex:M_06_01_004 , ex:M_06_01_005 ; pr:hasMetric ex:M_06_01_003 , ex:M_06_01_004 , ex:M_06_01_005 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT_DISTRIBUTION" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT_DISTRIBUTION" .
ex:BO_06_01_004 a pr:BusinessObject ; ex:BO_06_01_004 a pr:BusinessObject ;
@@ -223,8 +248,6 @@ ex:BO_06_01_004 a pr:BusinessObject ;
pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ; pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ;
pr:aboutConcept ex:BC_06_01_002 ; pr:aboutConcept ex:BC_06_01_002 ;
pr:usesConcept ex:BC_06_01_001 , ex:BC_06_01_003 ; pr:usesConcept ex:BC_06_01_001 , ex:BC_06_01_003 ;
pr:aboutConcept ex:BC_06_01_002 ;
pr:usesConcept ex:BC_06_01_001 , ex:BC_06_01_003 ;
pr:hasMetric ex:M_06_01_006 , ex:M_06_01_007 , ex:M_06_01_008 ; pr:hasMetric ex:M_06_01_006 , ex:M_06_01_007 , ex:M_06_01_008 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT" .
ex:BO_06_01_005 a pr:BusinessObject ; ex:BO_06_01_005 a pr:BusinessObject ;
@@ -233,64 +256,95 @@ ex:BO_06_01_005 a pr:BusinessObject ;
pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ; pr:belongsTo ex:SD_06_01 ; pr:monitoredBy ex:ST_DD_06 ;
pr:aboutConcept ex:BC_06_01_005 ; pr:aboutConcept ex:BC_06_01_005 ;
pr:usesConcept ex:BC_06_01_001 , ex:BC_05_01_001 ; pr:usesConcept ex:BC_06_01_001 , ex:BC_05_01_001 ;
pr:aboutConcept ex:BC_06_01_005 ;
pr:usesConcept ex:BC_06_01_001 , ex:BC_05_01_001 ;
pr:hasMetric ex:M_06_01_009 , ex:M_06_01_010 ; pr:hasMetric ex:M_06_01_009 , ex:M_06_01_010 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT_BASELINE" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "SODH Gold / F_SELL_OUT_BASELINE" .
ex:BO_10_01_001 a pr:BusinessObject ; ex:BO_10_01_001 a pr:BusinessObject ;
pr:hasIdentifier "BO-10.01-001" ; pr:hasName "Product Structure" ; pr:aboutConcept ex:BC_10_01_001 ;
pr:owningDomain ex:DD_10 ; pr:ownedBy ex:ST_DD_10 ; pr:belongsTo ex:SD_10_01 ;
pr:belongsTo ex:SD_10_01 ; pr:monitoredBy ex:ST_DD_10 ; pr:hasName "Product Hierarchy" ;
pr:aboutConcept ex:BC_10_01_001 ; pr:hasIdentifier "BO-10.01-001" ;
pr:aboutConcept ex:BC_10_01_001 ; pr:owningDomain ex:DD_10 ; pr:ownedBy ex:ST_DD_10 ; pr:monitoredBy ex:ST_DD_10 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "MDM Product -> SODH [concepts TBC by DD-10]" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "MDM Product -> SODH [concepts TBC by DD-10]"
.
ex:BO_04_01_001 a pr:BusinessObject ; ex:BO_04_01_001 a pr:BusinessObject ;
pr:hasIdentifier "BO-04.01-001" ; pr:hasName "Customer" ; pr:aboutConcept ex:BC_04_01_001 ;
pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ; pr:belongsTo ex:SD_04_01 ;
pr:belongsTo ex:SD_04_01 ; pr:monitoredBy ex:ST_DD_04 ; pr:hasName "Customer Hierarchy" ;
pr:aboutConcept ex:BC_04_01_001 ; pr:hasIdentifier "BO-04.01-001" ;
pr:aboutConcept ex:BC_04_01_001 ; pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ; pr:monitoredBy ex:ST_DD_04 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "MDM -> SODH [split proposed, subject arbitrated 2026-07-27]" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "MDM -> SODH [split proposed, subject arbitrated 2026-07-27]"
.
ex:BO_16_01_001 a pr:BusinessObject ; ex:BO_16_01_001 a pr:BusinessObject ;
pr:hasIdentifier "BO-16.01-001" ; pr:hasName "Currency" ; pr:aboutConcept ex:BC_16_01_001 ;
pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:belongsTo ex:SD_16_01 ;
pr:belongsTo ex:SD_16_01 ; pr:monitoredBy ex:ST_DD_16 ; pr:hasIdentifier "BO-16.01-001" ; pr:hasName "Currency" ;
pr:aboutConcept ex:BC_16_01_001 ; pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:monitoredBy ex:ST_DD_16 ;
pr:aboutConcept ex:BC_16_01_001 ; pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "MDM -> SODH [split proposed, subject arbitrated 2026-07-27]"
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "MDM -> SODH [split proposed, subject arbitrated 2026-07-27]" . .
ex:BO_21_01_001 a pr:BusinessObject ; ex:BO_21_01_001 a pr:BusinessObject ;
pr:hasIdentifier "BO-21.01-001" ; pr:hasName "Calendar" ; pr:aboutConcept ex:BC_21_01_001 ;
pr:owningDomain ex:DD_21 ; pr:ownedBy ex:ST_DD_21 ; pr:belongsTo ex:SD_21_01 ;
pr:belongsTo ex:SD_21_01 ; pr:monitoredBy ex:ST_DD_21 ; pr:hasName "Analysis Period" ;
pr:aboutConcept ex:BC_21_01_001 ; pr:hasIdentifier "BO-21.01-001" ;
pr:aboutConcept ex:BC_21_01_001 ; pr:owningDomain ex:DD_21 ; pr:ownedBy ex:ST_DD_21 ; pr:monitoredBy ex:ST_DD_21 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "Standard Calendar -> SODH [concepts TBC by DD-21]" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "Standard Calendar -> SODH [concepts TBC by DD-21]"
.
ex:BO_05_01_001 a pr:BusinessObject ; ex:BO_05_01_001 a pr:BusinessObject ;
pr:hasIdentifier "BO-05.01-001" ; pr:hasName "Promotion Type" ; pr:hasIdentifier "BO-05.01-001" ; pr:hasName "Promotion Type" ;
pr:owningDomain ex:DD_05 ; pr:ownedBy ex:ST_DD_05 ; pr:owningDomain ex:DD_05 ; pr:ownedBy ex:ST_DD_05 ;
pr:belongsTo ex:SD_05_01 ; pr:monitoredBy ex:ST_DD_05 ; pr:belongsTo ex:SD_05_01 ; pr:monitoredBy ex:ST_DD_05 ;
pr:aboutConcept ex:BC_05_01_001 ; pr:aboutConcept ex:BC_05_01_001 ;
pr:aboutConcept ex:BC_05_01_001 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "Sales Activation -> SODH [concepts TBC by DD-05]" . pr:hasStatus "DRAFT" ; pr:hasVersion "0.6" ; pr:hasSource "Sales Activation -> SODH [concepts TBC by DD-05]" .
# --- Business Objects from the v1.1 splits --------------------------- # --- Business Objects from the v1.1 splits ---------------------------
# 'Customer & Outlet' and 'Currency & Exchange Rates' had no single # 'Customer & Outlet' and 'Currency & Exchange Rates' had no single
# subject. Split proposed to DD-04 and DD-16, hence TO_ARBITRATE. # subject. Split proposed to DD-04 and DD-16, hence TO_ARBITRATE.
ex:BO_04_01_002 a pr:BusinessObject ; ex:BO_04_02_001 a pr:BusinessObject ;
pr:hasIdentifier "BO-04.01-002" ; pr:hasName "Outlet" ; pr:aboutConcept ex:BC_04_01_002 ;
pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ; pr:belongsTo ex:SD_04_02 ;
pr:belongsTo ex:SD_04_01 ; pr:monitoredBy ex:ST_DD_04 ; pr:hasIdentifier "BO-04.02-001" ; pr:hasName "Outlet" ;
pr:aboutConcept ex:BC_04_01_002 ; pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ; pr:monitoredBy ex:ST_DD_04 ;
pr:arbitrationStatus "TO_ARBITRATE" ; pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "MDM Customer -> SODH [split from BO-04.01-001, proposed to DD-04]" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "MDM Customer -> SODH [split from BO-04.01-001, proposed to DD-04]"
.
ex:BO_16_01_002 a pr:BusinessObject ; ex:BO_16_01_002 a pr:BusinessObject ;
pr:hasIdentifier "BO-16.01-002" ; pr:hasName "Exchange Rate" ; pr:aboutConcept ex:BC_16_01_002 ;
pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:belongsTo ex:SD_16_01 ;
pr:belongsTo ex:SD_16_01 ; pr:monitoredBy ex:ST_DD_16 ; pr:hasIdentifier "BO-16.01-002" ; pr:hasName "Exchange Rate" ;
pr:aboutConcept ex:BC_16_01_002 ; pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:monitoredBy ex:ST_DD_16 ;
pr:arbitrationStatus "TO_ARBITRATE" ; pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance -> SODH [split from BO-16.01-001, proposed to DD-16]" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance -> SODH [split from BO-16.01-001, proposed to DD-16]"
.
# --- business objects for the borrowed concepts -----------------------
# Country, Fiscal Period and Marketing Entity had a concept but nothing
# to hold it. Proposed to DD-04 and DD-16, hence TO_ARBITRATE.
ex:BO_04_03_001 a pr:BusinessObject ;
pr:aboutConcept ex:BC_04_03_001 ;
pr:belongsTo ex:SD_04_03 ;
pr:hasIdentifier "BO-04.03-001" ; pr:hasName "Country" ;
pr:owningDomain ex:DD_04 ; pr:ownedBy ex:ST_DD_04 ; pr:monitoredBy ex:ST_DD_04 ;
pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, pending ratification by the owning domain"
.
ex:BO_16_02_001 a pr:BusinessObject ;
pr:aboutConcept ex:BC_16_02_001 ;
pr:belongsTo ex:SD_16_02 ;
pr:hasIdentifier "BO-16.02-001" ; pr:hasName "Fiscal Period" ;
pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:monitoredBy ex:ST_DD_16 ;
pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, pending ratification by the owning domain"
.
ex:BO_16_03_001 a pr:BusinessObject ;
pr:aboutConcept ex:BC_16_03_001 ;
pr:belongsTo ex:SD_16_03 ;
pr:hasIdentifier "BO-16.03-001" ; pr:hasName "Marketing Entity" ;
pr:owningDomain ex:DD_16 ; pr:ownedBy ex:ST_DD_16 ; pr:monitoredBy ex:ST_DD_16 ;
pr:arbitrationStatus "TO_ARBITRATE" ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "DGO proposal, pending ratification by the owning domain"
.
ex:DO_06_01_001 a pr:DataObject ; ex:DO_06_01_001 a pr:DataObject ;
pr:hasIdentifier "DO-06.01-001" ; pr:hasName "Sell Out Volume Dataset" ; pr:hasIdentifier "DO-06.01-001" ; pr:hasName "Sell Out Volume Dataset" ;
@@ -585,14 +639,14 @@ ex:DE_04_01_0008 a pr:DataElement ;
pr:owningDomain ex:DD_04 ; pr:owningDomain ex:DD_04 ;
pr:hasFormat "STRING(2)" ; pr:hasFormat "STRING(2)" ;
pr:physicalName "COUNTRY_CD" ; pr:physicalName "COUNTRY_CD" ;
pr:represents ex:BC_04_01_003 ; pr:represents ex:BC_04_03_001 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "MDM Customer" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "MDM Customer" .
ex:DE_04_01_0009 a pr:DataElement ; ex:DE_04_01_0009 a pr:DataElement ;
pr:hasIdentifier "DE-04.01-0009" ; pr:hasName "Country Name" ; pr:hasIdentifier "DE-04.01-0009" ; pr:hasName "Country Name" ;
pr:owningDomain ex:DD_04 ; pr:owningDomain ex:DD_04 ;
pr:hasFormat "STRING(100)" ; pr:hasFormat "STRING(100)" ;
pr:physicalName "COUNTRY_NM" ; pr:physicalName "COUNTRY_NM" ;
pr:represents ex:BC_04_01_003 ; pr:represents ex:BC_04_03_001 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "MDM Customer" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "MDM Customer" .
ex:DE_04_01_0010 a pr:DataElement ; ex:DE_04_01_0010 a pr:DataElement ;
pr:hasIdentifier "DE-04.01-0010" ; pr:hasName "Point of Sale Count (Count)" ; pr:hasIdentifier "DE-04.01-0010" ; pr:hasName "Point of Sale Count (Count)" ;
@@ -630,21 +684,21 @@ ex:DE_16_02_0001 a pr:DataElement ;
pr:owningDomain ex:DD_16 ; pr:owningDomain ex:DD_16 ;
pr:hasFormat "STRING(10)" ; pr:hasFormat "STRING(10)" ;
pr:physicalName "MARKETING_ENTITY_CD" ; pr:physicalName "MARKETING_ENTITY_CD" ;
pr:represents ex:BC_16_01_003 ; pr:represents ex:BC_16_03_001 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance" .
ex:DE_16_02_0002 a pr:DataElement ; ex:DE_16_02_0002 a pr:DataElement ;
pr:hasIdentifier "DE-16.02-0002" ; pr:hasName "Fiscal Period Code" ; pr:hasIdentifier "DE-16.02-0002" ; pr:hasName "Fiscal Period Code" ;
pr:owningDomain ex:DD_16 ; pr:owningDomain ex:DD_16 ;
pr:hasFormat "STRING(10)" ; pr:hasFormat "STRING(10)" ;
pr:physicalName "PR_FISCAL_PERIOD_CD" ; pr:physicalName "PR_FISCAL_PERIOD_CD" ;
pr:represents ex:BC_16_01_004 ; pr:represents ex:BC_16_02_001 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance" .
ex:DE_16_02_0003 a pr:DataElement ; ex:DE_16_02_0003 a pr:DataElement ;
pr:hasIdentifier "DE-16.02-0003" ; pr:hasName "Fiscal Quarter Code" ; pr:hasIdentifier "DE-16.02-0003" ; pr:hasName "Fiscal Quarter Code" ;
pr:owningDomain ex:DD_16 ; pr:owningDomain ex:DD_16 ;
pr:hasFormat "STRING(10)" ; pr:hasFormat "STRING(10)" ;
pr:physicalName "PR_FISCAL_QUARTER_CD" ; pr:physicalName "PR_FISCAL_QUARTER_CD" ;
pr:represents ex:BC_16_01_004 ; pr:represents ex:BC_16_02_001 ;
pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance" . pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; pr:hasSource "Group Finance" .
ex:DE_21_01_0001 a pr:DataElement ; ex:DE_21_01_0001 a pr:DataElement ;
pr:hasIdentifier "DE-21.01-0001" ; pr:hasName "Calendar Date" ; pr:hasIdentifier "DE-21.01-0001" ; pr:hasName "Calendar Date" ;
+14 -1
View File
@@ -95,7 +95,7 @@ NEW_CONCEPTS = [
# ------------------------------------------------------------------- new object # ------------------------------------------------------------------- new object
NEW_OBJECTS = [ NEW_OBJECTS = [
("ex:BO_04_01_002", "BO-04.01-002", "Outlet", "ex:DD_04", "ex:ST_DD_04", "ex:SD_04_01", ("ex:BO_04_02_001", "BO-04.02-001", "Outlet", "ex:DD_04", "ex:ST_DD_04", "ex:SD_04_02",
"ex:BC_04_01_002", [], "MDM Customer -> SODH [split from BO-04.01-001, proposed to DD-04]"), "ex:BC_04_01_002", [], "MDM Customer -> SODH [split from BO-04.01-001, proposed to DD-04]"),
("ex:BO_16_01_002", "BO-16.01-002", "Exchange Rate", "ex:DD_16", "ex:ST_DD_16", "ex:SD_16_01", ("ex:BO_16_01_002", "BO-16.01-002", "Exchange Rate", "ex:DD_16", "ex:ST_DD_16", "ex:SD_16_01",
"ex:BC_16_01_002", [], "Group Finance -> SODH [split from BO-16.01-001, proposed to DD-16]"), "ex:BC_16_01_002", [], "Group Finance -> SODH [split from BO-16.01-001, proposed to DD-16]"),
@@ -176,7 +176,13 @@ def main():
continue continue
block = m.group(1) block = m.group(1)
# strip what a previous run may have written, so re-running is a no-op.
# Duplicated clauses carrying the SAME value merge under RDF set
# semantics: the graph stays valid and SHACL sees nothing, while the
# file quietly grows a redundant line on every pass.
new_block = re.sub(r'[ \t]*pr:composedOf\s+[^";]*;[ \t]*\n', '', block) new_block = re.sub(r'[ \t]*pr:composedOf\s+[^";]*;[ \t]*\n', '', block)
new_block = re.sub(r'[ \t]*pr:(aboutConcept|usesConcept)\s+[^";]*;[ \t]*\n',
'', new_block)
clause = ' pr:aboutConcept %s ;\n' % about clause = ' pr:aboutConcept %s ;\n' % about
if uses: if uses:
clause += ' pr:usesConcept %s ;\n' % " , ".join(uses) clause += ' pr:usesConcept %s ;\n' % " , ".join(uses)
@@ -244,6 +250,13 @@ def main():
for line in report: for line in report:
print(" " + line) print(" " + line)
print("=" * W) print("=" * W)
doubled = []
for m in re.finditer(r'^(ex:\w+) a pr:\w+ ;(.*?)\.\s*\n', text, re.S | re.M):
for prop in ("pr:hasName", "pr:aboutConcept", "pr:belongsTo"):
if len(re.findall(r'(?<![\w:])%s\s' % re.escape(prop), m.group(2))) > 1:
doubled.append("%s/%s" % (m.group(1), prop))
print(" duplicated single-valued properties: %s"
% (", ".join(doubled) if doubled else "none"))
bo = len(re.findall(r'a pr:BusinessObject', text)) bo = len(re.findall(r'a pr:BusinessObject', text))
bc = len(re.findall(r'a pr:BusinessConcept', text)) bc = len(re.findall(r'a pr:BusinessConcept', text))
print(" Business Objects %d | Business Concepts %d | composedOf left %d" print(" Business Objects %d | Business Concepts %d | composedOf left %d"
+11 -8
View File
@@ -71,14 +71,14 @@ DE_CONCEPT_EXACT = {
"DE-04.01-0006": "ex:BC_04_01_002", # Outlet Type -> Outlet "DE-04.01-0006": "ex:BC_04_01_002", # Outlet Type -> Outlet
"DE-04.01-0007": "ex:BC_04_01_002", # Channel -> Outlet "DE-04.01-0007": "ex:BC_04_01_002", # Channel -> Outlet
"DE-04.01-0010": "ex:BC_04_01_002", # Point of Sale Count -> Outlet "DE-04.01-0010": "ex:BC_04_01_002", # Point of Sale Count -> Outlet
"DE-04.01-0008": "ex:BC_04_01_003", # Country Code -> Country "DE-04.01-0008": "ex:BC_04_03_001", # Country Code -> Country
"DE-04.01-0009": "ex:BC_04_01_003", # Country Name -> Country "DE-04.01-0009": "ex:BC_04_03_001", # Country Name -> Country
"DE-16.01-0001": "ex:BC_16_01_001", # Currency Code -> Currency "DE-16.01-0001": "ex:BC_16_01_001", # Currency Code -> Currency
"DE-16.01-0002": "ex:BC_16_01_002", # FX Rate to-Euro -> Exchange Rate "DE-16.01-0002": "ex:BC_16_01_002", # FX Rate to-Euro -> Exchange Rate
"DE-16.01-0003": "ex:BC_16_01_002", # FX Rate to-USD -> Exchange Rate "DE-16.01-0003": "ex:BC_16_01_002", # FX Rate to-USD -> Exchange Rate
"DE-16.02-0001": "ex:BC_16_01_003", # Marketing Entity -> Marketing Entity "DE-16.02-0001": "ex:BC_16_03_001", # Marketing Entity -> Marketing Entity
"DE-16.02-0002": "ex:BC_16_01_004", # Fiscal Period Code -> Fiscal Period "DE-16.02-0002": "ex:BC_16_02_001", # Fiscal Period Code -> Fiscal Period
"DE-16.02-0003": "ex:BC_16_01_004", # Fiscal Quarter Code -> Fiscal Period "DE-16.02-0003": "ex:BC_16_02_001", # Fiscal Quarter Code -> Fiscal Period
} }
DE_CONCEPT_PREFIX = [ DE_CONCEPT_PREFIX = [
("DE-10.01", "ex:BC_10_01_001"), # product hierarchy -> Product ("DE-10.01", "ex:BC_10_01_001"), # product hierarchy -> Product
@@ -89,14 +89,17 @@ DE_CONCEPT_PREFIX = [
# ---- concepts the XOR forces us to name ------------------------------------ # ---- concepts the XOR forces us to name ------------------------------------
NEW_CONCEPTS = [ NEW_CONCEPTS = [
("ex:BC_04_01_003", "BC-04.01-003", "Country", "ex:DD_04", "ex:ST_DD_04", # Final identifiers from the outset. Creating them under one id and
# renumbering them later broke idempotence: the second run no longer found
# the original id and recreated the concept alongside the renamed one.
("ex:BC_04_03_001", "BC-04.03-001", "Country", "ex:DD_04", "ex:ST_DD_04",
"A sovereign territory used as the geographic frame for retail measurement, " "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 " "identified by its ISO 3166 code. Distinct from Customer and Outlet: it is where "
"they operate, not what they are."), "they operate, not what they are."),
("ex:BC_16_01_003", "BC-16.01-003", "Marketing Entity", "ex:DD_16", "ex:ST_DD_16", ("ex:BC_16_03_001", "BC-16.03-001", "Marketing Entity", "ex:DD_16", "ex:ST_DD_16",
"An organisational unit of the group holding commercial responsibility for a market, " "An organisational unit of the group holding commercial responsibility for a market, "
"and the level at which financial results are consolidated."), "and the level at which financial results are consolidated."),
("ex:BC_16_01_004", "BC-16.01-004", "Fiscal Period", "ex:DD_16", "ex:ST_DD_16", ("ex:BC_16_02_001", "BC-16.02-001", "Fiscal Period", "ex:DD_16", "ex:ST_DD_16",
"A reporting interval of the Pernod Ricard fiscal year, which runs July to June. " "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."), "Distinct from Calendar Date: the fiscal frame does not align with the Gregorian one."),
] ]
+261
View File
@@ -0,0 +1,261 @@
#!/usr/bin/env python3
"""
SODH - structure proposed for the borrowed domains
===================================================
Gives the nine TO_ARBITRATE concepts a home: a Business Object and a
Sub-Domain each, per the structure arbitrated with Bastien on 2026-07-27.
USAGE
python3 scripts/apply_structure_sodh.py # dry run
python3 scripts/apply_structure_sodh.py --apply # rewrite, .bak kept
STILL A PROPOSAL
Everything created here stays TO_ARBITRATE. DD-04, DD-10, DD-16 and DD-21
own these perimeters; the DGO is proposing a coherent structure, not
ratifying one. Replacing padding with an equally unilateral structure
would trade one problem for a tidier version of the same problem. The flag
comes off when Helene Puchot, Anas El Kesri and Gaelle Seret say so, and
OW-007 blocks publication until then.
IDENTIFIERS ARE FINAL FROM CREATION
Identifiers encode the sub-domain (BO-04.02-001 lives in SD-04.02), so the
upstream scripts now mint them correctly rather than having this one
renumber afterwards. Renaming after creation broke idempotence: the script
that created an object stopped recognising its own output and made a second
copy on the next run.
COMPOUND SUB-DOMAINS SPLIT TOO
'Customer & Outlet' and 'Marketing Entity & Fiscal Calendar' have the same
defect as the Business Objects that were split earlier: a name joined by
'&' has no single subject. Same rule, one level up.
"""
import os
import re
import shutil
import sys
REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TTL = os.path.join(REPO, "instances", "sodh.ttl")
W = 78
# No IRI renaming here. Objects are created with their final identifiers by
# the upstream scripts: renumbering after the fact broke idempotence, because
# the creating script then no longer recognised its own output.
# ---- sub-domains ----------------------------------------------------------
SD_RENAME = {
"ex:SD_04_01": "Customer Structure",
"ex:SD_16_01": "Currency & Exchange Rate",
"ex:SD_16_02": "Fiscal Calendar",
"ex:SD_21_01": "Standard Calendar",
}
SD_NEW = [
("ex:SD_04_02", "SD-04.02", "Outlet & Points of Sale", "ex:DD_04"),
("ex:SD_04_03", "SD-04.03", "Geography", "ex:DD_04"),
("ex:SD_16_03", "SD-16.03", "Organisation Structure", "ex:DD_16"),
]
# ---- business objects -----------------------------------------------------
BO_RENAME = {
"ex:BO_10_01_001": "Product Hierarchy",
"ex:BO_04_01_001": "Customer Hierarchy",
"ex:BO_21_01_001": "Analysis Period",
}
BO_NEW = [
("ex:BO_04_03_001", "BO-04.03-001", "Country", "ex:DD_04", "ex:ST_DD_04",
"ex:SD_04_03", "ex:BC_04_03_001"),
("ex:BO_16_02_001", "BO-16.02-001", "Fiscal Period", "ex:DD_16", "ex:ST_DD_16",
"ex:SD_16_02", "ex:BC_16_02_001"),
("ex:BO_16_03_001", "BO-16.03-001", "Marketing Entity", "ex:DD_16", "ex:ST_DD_16",
"ex:SD_16_03", "ex:BC_16_03_001"),
]
# ---- where each object finally sits ---------------------------------------
BELONGS = {
"ex:BO_10_01_001": "ex:SD_10_01",
"ex:BO_04_01_001": "ex:SD_04_01",
"ex:BO_04_02_001": "ex:SD_04_02",
"ex:BO_04_03_001": "ex:SD_04_03",
"ex:BO_16_01_001": "ex:SD_16_01",
"ex:BO_16_01_002": "ex:SD_16_01",
"ex:BO_16_02_001": "ex:SD_16_02",
"ex:BO_16_03_001": "ex:SD_16_03",
"ex:BO_21_01_001": "ex:SD_21_01",
}
# ---- subject of each borrowed object --------------------------------------
ABOUT = {
"ex:BO_10_01_001": "ex:BC_10_01_001", # Product Hierarchy -> Product
"ex:BO_04_01_001": "ex:BC_04_01_001", # Customer Hierarchy -> Customer
"ex:BO_04_02_001": "ex:BC_04_01_002", # Outlet -> Outlet
"ex:BO_04_03_001": "ex:BC_04_03_001", # Country -> Country
"ex:BO_16_01_001": "ex:BC_16_01_001", # Currency -> Currency
"ex:BO_16_01_002": "ex:BC_16_01_002", # Exchange Rate -> Exchange Rate
"ex:BO_16_02_001": "ex:BC_16_02_001", # Fiscal Period -> Fiscal Period
"ex:BO_16_03_001": "ex:BC_16_03_001", # Marketing Entity -> Marketing Entity
"ex:BO_21_01_001": "ex:BC_21_01_001", # Analysis Period -> Calendar Date
}
MARKETING_NOTE = ("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.")
def sd_block(i, ident, name, domain):
return ('%s a pr:SubDomain ;\n'
' pr:hasIdentifier "%s" ; pr:hasName "%s" ;\n'
' pr:belongsTo %s ; pr:owningDomain %s ;\n'
' pr:arbitrationStatus "TO_ARBITRATE" ;\n'
' pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; '
'pr:hasSource "DGO proposal, split of a compound sub-domain" .'
% (i, ident, name, domain, domain))
def bo_block(i, ident, name, domain, steward, sd, bc):
return ('%s a pr:BusinessObject ;\n'
' pr:hasIdentifier "%s" ; pr:hasName "%s" ;\n'
' pr:owningDomain %s ; pr:ownedBy %s ;\n'
' pr:belongsTo %s ; pr:monitoredBy %s ;\n'
' pr:aboutConcept %s ;\n'
' pr:arbitrationStatus "TO_ARBITRATE" ;\n'
' pr:hasStatus "DRAFT" ; pr:hasVersion "1.1" ; '
'pr:hasSource "DGO proposal, pending ratification by the owning domain" .'
% (i, ident, name, domain, steward, sd, steward, bc))
def _split_clauses(body):
"""Split a Turtle predicate list on ';' that sit outside string literals."""
out, buf, i, n = [], [], 0, len(body)
while i < n:
c = body[i]
if c == '"':
j = i + 1
while j < n and body[j] != '"':
j += 2 if body[j] == "\\" else 1
j = min(j + 1, n)
buf.append(body[i:j]); i = j; continue
if c == ";":
out.append("".join(buf)); buf = []; i += 1; continue
buf.append(c); i += 1
out.append("".join(buf))
return out
def set_prop(text, subject, prop, value):
"""
Set or replace a single-valued property inside a subject block.
Clause-level, not regex-level: a property can sit mid-line
(pr:hasIdentifier "X" ; pr:hasName "Y" ;) and its value can contain a
semicolon inside the literal. A line-anchored pattern silently misses both
and APPENDS instead of replacing, which produces a second value and a
cardinality violation -- exactly what happened on the first run here.
"""
pat = re.compile(r'(^%s a pr:\w+ ;)(.*?)(\.\s*\n)' % re.escape(subject), re.S | re.M)
m = pat.search(text)
if not m:
return text, False
kept = [c for c in _split_clauses(m.group(2))
if c.strip() and not re.match(r'\s*%s\s' % re.escape(prop), c)]
kept.insert(0, "\n %s %s " % (prop, value))
body = " ;".join(kept)
if not body.endswith("\n"):
body = body.rstrip() + "\n "
return text[:m.start()] + m.group(1) + body + m.group(3) + text[m.end():], True
def main():
apply_changes = "--apply" in sys.argv
if not os.path.exists(TTL):
print("Not found: %s" % TTL)
sys.exit(2)
text = original = open(TTL, encoding="utf-8").read()
report = []
# 2. sub-domains
for sd, name in SD_RENAME.items():
text, ok = set_prop(text, sd, "pr:hasName", '"%s"' % name)
if ok:
report.append("renamed %s -> %s" % (sd.replace("ex:SD_", "SD-"), name))
todo_sd = [s for s in SD_NEW if not re.search(r'^%s a ' % re.escape(s[0]), text, re.M)]
if todo_sd:
anchor = re.search(r'\n(?=ex:BC_\w+ a pr:BusinessConcept ;|ex:BO_\w+ a pr:BusinessObject ;)',
text)
blocks = "\n".join(sd_block(*s) for s in todo_sd)
text = (text[:anchor.start()] + "\n\n"
+ "# --- sub-domains from the v1.1 structure proposal --------------------\n"
+ "# 'Customer & Outlet' and 'Marketing Entity & Fiscal Calendar' had the\n"
+ "# same defect as the compound Business Objects: a name joined by '&'\n"
+ "# has no single subject. Same rule, one level up.\n"
+ blocks + "\n" + text[anchor.start():])
report.append("created %d sub-domains%s"
% (len(todo_sd), "" if todo_sd else " (already present)"))
# 3. new business objects
todo_bo = [b for b in BO_NEW if not re.search(r'^%s a ' % re.escape(b[0]), text, re.M)]
if todo_bo:
anchor = re.search(r'\n(?=ex:DO_\w+ a pr:DataObject ;|ex:M_\w+ a pr:Metric ;)', text)
blocks = "\n".join(bo_block(*b) for b in todo_bo)
text = (text[:anchor.start()] + "\n\n"
+ "# --- business objects for the borrowed concepts -----------------------\n"
+ "# Country, Fiscal Period and Marketing Entity had a concept but nothing\n"
+ "# to hold it. Proposed to DD-04 and DD-16, hence TO_ARBITRATE.\n"
+ blocks + "\n" + text[anchor.start():])
report.append("created %d business objects%s"
% (len(todo_bo), "" if todo_bo else " (already present)"))
# 4. renames, placement, subject
for bo, name in BO_RENAME.items():
text, _ = set_prop(text, bo, "pr:hasName", '"%s"' % name)
report.append("renamed %d business objects to the agreed nomenclature" % len(BO_RENAME))
n_b = n_a = 0
for bo, sd in BELONGS.items():
text, ok = set_prop(text, bo, "pr:belongsTo", sd)
n_b += ok
for bo, bc in ABOUT.items():
text, ok = set_prop(text, bo, "pr:aboutConcept", bc)
n_a += ok
report.append("placed %d objects in their sub-domain, %d subjects confirmed" % (n_b, n_a))
# 5. the naming caveat, recorded on the object rather than in a side note
text, ok = set_prop(text, "ex:BC_16_03_001", "pr:hasBusinessRule", '"%s"' % MARKETING_NOTE)
if ok:
report.append("recorded the Marketing/Management naming caveat on the concept")
# ---- report
print()
print("SODH STRUCTURE PROPOSAL %s" % ("APPLY" if apply_changes else "DRY RUN"))
print("=" * W)
for line in report:
print(" " + line)
print("=" * W)
defined = set(re.findall(r'^(ex:\w+) a ', text, re.M))
dangling = sorted(set(re.findall(r'\bex:\w+', text)) - defined)
print(" dangling references: %s" % (", ".join(dangling) if dangling else "none"))
for cls in ("SubDomain", "BusinessObject", "BusinessConcept"):
ids = re.findall(r'^(ex:\w+) a pr:%s\b' % cls, text, re.M)
dup = [i for i in set(ids) if ids.count(i) > 1]
print(" %-16s %3d duplicates %d" % (cls, len(ids), len(dup)))
doubled = []
for m in re.finditer(r'^(ex:\w+) a pr:\w+ ;(.*?)\.\s*\n', text, re.S | re.M):
for prop in ("pr:hasName", "pr:hasIdentifier", "pr:belongsTo", "pr:aboutConcept"):
if len(re.findall(r'%s\s' % re.escape(prop), m.group(2))) > 1:
doubled.append("%s/%s" % (m.group(1), prop))
print(" duplicated single-valued properties: %s"
% (", ".join(doubled) if doubled else "none"))
arb = len(re.findall(r'pr:arbitrationStatus "TO_ARBITRATE"', text))
print(" TO_ARBITRATE %3d (nothing publishes until the domains ratify)" % arb)
print("=" * W)
if text != original and apply_changes:
shutil.copy2(TTL, TTL + ".bak")
open(TTL, "w", encoding="utf-8").write(text)
print(" written, backup at %s.bak" % os.path.basename(TTL))
elif text != original:
print(" dry run -- re-run with --apply to write")
print()
if __name__ == "__main__":
main()