def validate(self): self.modelXbrl.info( "info", "Validating calculations inferring %(inferMode)s", inferMode=_("precision") if self.inferPrecision else _("decimals"), ) # identify equal contexts self.modelXbrl.profileActivity() uniqueContextHashes = {} for context in self.modelXbrl.contexts.values(): h = context.contextDimAwareHash if h in uniqueContextHashes: if context.isEqualTo(uniqueContextHashes[h]): self.mapContext[context] = uniqueContextHashes[h] else: uniqueContextHashes[h] = context del uniqueContextHashes self.modelXbrl.profileActivity("... identify equal contexts", minTimeToShow=1.0) # identify equal contexts uniqueUnitHashes = {} for unit in self.modelXbrl.units.values(): h = unit.hash if h in uniqueUnitHashes: if unit.isEqualTo(uniqueUnitHashes[h]): self.mapUnit[unit] = uniqueUnitHashes[h] else: uniqueUnitHashes[h] = unit self.modelXbrl.profileActivity("... identify equal units", minTimeToShow=1.0) # identify concepts participating in essence-alias relationships # identify calcluation & essence-alias base sets (by key) for baseSetKey in self.modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname: if arcrole in (XbrlConst.essenceAlias, XbrlConst.requiresElement): conceptsSet = { XbrlConst.essenceAlias: self.conceptsInEssencesAlias, XbrlConst.requiresElement: self.conceptsInRequiresElement, }[arcrole] for modelRel in self.modelXbrl.relationshipSet( arcrole, ELR, linkqname, arcqname ).modelRelationships: for concept in (modelRel.fromModelObject, modelRel.toModelObject): conceptsSet.add(concept) self.modelXbrl.profileActivity("... identify requires-element and esseance-aliased concepts", minTimeToShow=1.0) self.bindFacts(self.modelXbrl.facts, [self.modelXbrl.modelDocument.xmlRootElement]) self.modelXbrl.profileActivity("... bind facts", minTimeToShow=1.0) # identify calcluation & essence-alias base sets (by key) for baseSetKey in self.modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname: if arcrole in (XbrlConst.summationItem, XbrlConst.essenceAlias, XbrlConst.requiresElement): relsSet = self.modelXbrl.relationshipSet(arcrole, ELR, linkqname, arcqname) if arcrole == XbrlConst.summationItem: fromRelationships = relsSet.fromModelObjects() for sumConcept, modelRels in fromRelationships.items(): sumBindingKeys = self.sumConceptBindKeys[sumConcept] dupBindingKeys = set() boundSumKeys = set() # determine boundSums for modelRel in modelRels: itemBindingKeys = self.itemConceptBindKeys[modelRel.toModelObject] boundSumKeys |= sumBindingKeys & itemBindingKeys # add up rounded items boundSums = defaultdict(decimal.Decimal) for modelRel in modelRels: weight = modelRel.weightDecimal itemConcept = modelRel.toModelObject for itemBindKey in boundSumKeys: ancestor, contextHash, unit = itemBindKey factKey = (itemConcept, ancestor, contextHash, unit) if factKey in self.itemFacts: for fact in self.itemFacts[factKey]: if fact in self.duplicatedFacts: dupBindingKeys.add(itemBindKey) else: boundSums[itemBindKey] += self.roundFact(fact) * weight for sumBindKey in boundSumKeys: ancestor, contextHash, unit = sumBindKey factKey = (sumConcept, ancestor, contextHash, unit) if factKey in self.sumFacts: for fact in self.sumFacts[factKey]: if fact in self.duplicatedFacts: dupBindingKeys.add(sumBindKey) elif sumBindKey not in dupBindingKeys: roundedSum = self.roundFact(fact) roundedItemsSum = self.roundFact(fact, vDecimal=boundSums[sumBindKey]) if roundedItemsSum != self.roundFact(fact): self.modelXbrl.error( "xbrl.5.2.5.2:calcInconsistency", _( "Calculation inconsistent from %(concept)s in link role %(linkrole)s reported sum %(reportedSum)s computed sum %(computedSum)s context %(contextID)s unit %(unitID)s" ), modelObject=sumConcept, concept=sumConcept.qname, linkrole=ELR, reportedSum=roundedSum, computedSum=roundedItemsSum, contextID=context.id, unitID=unit.id, ) elif arcrole == XbrlConst.essenceAlias: for modelRel in relsSet.modelRelationships: essenceConcept = modelRel.fromModelObject aliasConcept = modelRel.toModelObject essenceBindingKeys = self.esAlConceptBindKeys[essenceConcept] aliasBindingKeys = self.esAlConceptBindKeys[aliasConcept] for esAlBindKey in essenceBindingKeys & aliasBindingKeys: ancestor, contextHash = esAlBindKey essenceFactsKey = (essenceConcept, ancestor, contextHash) aliasFactsKey = (aliasConcept, ancestor, contextHash) if essenceFactsKey in self.esAlFacts and aliasFactsKey in self.esAlFacts: for eF in self.esAlFacts[essenceFactsKey]: for aF in self.esAlFacts[aliasFactsKey]: essenceUnit = self.mapUnit.get(eF.unit, eF.unit) aliasUnit = self.mapUnit.get(aF.unit, aF.unit) if essenceUnit != aliasUnit: self.modelXbrl.error( "xbrl.5.2.6.2.2:essenceAliasUnitsInconsistency", _( "Essence-Alias inconsistent units from %(essenceConcept)s to %(aliasConcept)s in link role %(linkrole)s context %(contextID)s" ), modelObject=essenceConcept, essenceConcept=essenceConcept.qname, aliasConcept=aliasConcept.qname, linkrole=ELR, contextID=context.id, ) if not XbrlUtil.vEqual(eF, aF): self.modelXbrl.error( "xbrl.5.2.6.2.2:essenceAliasUnitsInconsistency", _( "Essence-Alias inconsistent value from %(essenceConcept)s to %(aliasConcept)s in link role %(linkrole)s context %(contextID)s" ), modelObject=essenceConcept, essenceConcept=essenceConcept.qname, aliasConcept=aliasConcept.qname, linkrole=ELR, contextID=context.id, ) elif arcrole == XbrlConst.requiresElement: for modelRel in relsSet.modelRelationships: sourceConcept = modelRel.fromModelObject requiredConcept = modelRel.toModelObject if ( sourceConcept in self.requiresElementFacts and not requiredConcept in self.requiresElementFacts ): self.modelXbrl.error( "xbrl.5.2.6.2.4:requiresElementInconsistency", _( "Requires-Element %(requiringConcept)s missing required fact for %(requiredConcept)s in link role %(linkrole)s" ), modelObject=sourceConcept, requiringConcept=sourceConcept.qname, requiredConcept=requiredConcept.qname, linkrole=ELR, ) self.modelXbrl.profileActivity("... find inconsistencies", minTimeToShow=1.0) self.modelXbrl.profileActivity() # reset
def validate(self): if not self.modelXbrl.contexts and not self.modelXbrl.facts: return # skip if no contexts or facts if not self.inferDecimals: # infering precision is now contrary to XBRL REC section 5.2.5.2 self.modelXbrl.info("xbrl.5.2.5.2:inferringPrecision","Validating calculations inferring precision.") # identify equal contexts self.modelXbrl.profileActivity() uniqueContextHashes = {} for context in self.modelXbrl.contexts.values(): h = context.contextDimAwareHash if h in uniqueContextHashes: if context.isEqualTo(uniqueContextHashes[h]): self.mapContext[context] = uniqueContextHashes[h] else: uniqueContextHashes[h] = context del uniqueContextHashes self.modelXbrl.profileActivity("... identify equal contexts", minTimeToShow=1.0) # identify equal contexts uniqueUnitHashes = {} for unit in self.modelXbrl.units.values(): h = unit.hash if h in uniqueUnitHashes: if unit.isEqualTo(uniqueUnitHashes[h]): self.mapUnit[unit] = uniqueUnitHashes[h] else: uniqueUnitHashes[h] = unit self.modelXbrl.profileActivity("... identify equal units", minTimeToShow=1.0) # identify concepts participating in essence-alias relationships # identify calcluation & essence-alias base sets (by key) for baseSetKey in self.modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname: if arcrole in (XbrlConst.essenceAlias, XbrlConst.requiresElement): conceptsSet = {XbrlConst.essenceAlias:self.conceptsInEssencesAlias, XbrlConst.requiresElement:self.conceptsInRequiresElement}[arcrole] for modelRel in self.modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname).modelRelationships: for concept in (modelRel.fromModelObject, modelRel.toModelObject): if concept is not None and concept.qname is not None: conceptsSet.add(concept) self.modelXbrl.profileActivity("... identify requires-element and esseance-aliased concepts", minTimeToShow=1.0) self.bindFacts(self.modelXbrl.facts,[self.modelXbrl.modelDocument.xmlRootElement]) self.modelXbrl.profileActivity("... bind facts", minTimeToShow=1.0) # identify calcluation & essence-alias base sets (by key) for baseSetKey in self.modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname: if arcrole in (XbrlConst.summationItem, XbrlConst.essenceAlias, XbrlConst.requiresElement): relsSet = self.modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname) if arcrole == XbrlConst.summationItem: fromRelationships = relsSet.fromModelObjects() for sumConcept, modelRels in fromRelationships.items(): sumBindingKeys = self.sumConceptBindKeys[sumConcept] dupBindingKeys = set() boundSumKeys = set() # determine boundSums for modelRel in modelRels: itemConcept = modelRel.toModelObject if itemConcept is not None and itemConcept.qname is not None: itemBindingKeys = self.itemConceptBindKeys[itemConcept] boundSumKeys |= sumBindingKeys & itemBindingKeys # add up rounded items boundSums = defaultdict(decimal.Decimal) # sum of facts meeting factKey boundSummationItems = defaultdict(list) # corresponding fact refs for messages for modelRel in modelRels: weight = modelRel.weightDecimal itemConcept = modelRel.toModelObject if itemConcept is not None: for itemBindKey in boundSumKeys: ancestor, contextHash, unit = itemBindKey factKey = (itemConcept, ancestor, contextHash, unit) if factKey in self.itemFacts: for fact in self.itemFacts[factKey]: if fact in self.duplicatedFacts: dupBindingKeys.add(itemBindKey) else: roundedValue = roundFact(fact, self.inferDecimals) boundSums[itemBindKey] += roundedValue * weight boundSummationItems[itemBindKey].append(wrappedFactWithWeight(fact,weight,roundedValue)) for sumBindKey in boundSumKeys: ancestor, contextHash, unit = sumBindKey factKey = (sumConcept, ancestor, contextHash, unit) if factKey in self.sumFacts: sumFacts = self.sumFacts[factKey] for fact in sumFacts: if fact in self.duplicatedFacts: dupBindingKeys.add(sumBindKey) elif sumBindKey not in dupBindingKeys: roundedSum = roundFact(fact, self.inferDecimals) roundedItemsSum = roundFact(fact, self.inferDecimals, vDecimal=boundSums[sumBindKey]) if roundedItemsSum != roundFact(fact, self.inferDecimals): d = inferredDecimals(fact) if isnan(d) or isinf(d): d = 4 _boundSummationItems = boundSummationItems[sumBindKey] unreportedContribingItemQnames = [] # list the missing/unreported contributors in relationship order for modelRel in modelRels: itemConcept = modelRel.toModelObject if (itemConcept is not None and (itemConcept, ancestor, contextHash, unit) not in self.itemFacts): unreportedContribingItemQnames.append(str(itemConcept.qname)) self.modelXbrl.log('INCONSISTENCY', "xbrl.5.2.5.2:calcInconsistency", _("Calculation inconsistent from %(concept)s in link role %(linkrole)s reported sum %(reportedSum)s computed sum %(computedSum)s context %(contextID)s unit %(unitID)s unreportedContributingItems %(unreportedContributors)s"), modelObject=wrappedSummationAndItems(fact, roundedSum, _boundSummationItems), concept=sumConcept.qname, linkrole=ELR, linkroleDefinition=self.modelXbrl.roleTypeDefinition(ELR), reportedSum=Locale.format_decimal(self.modelXbrl.locale, roundedSum, 1, max(d,0)), computedSum=Locale.format_decimal(self.modelXbrl.locale, roundedItemsSum, 1, max(d,0)), contextID=fact.context.id, unitID=fact.unit.id, unreportedContributors=", ".join(unreportedContribingItemQnames) or "none") del unreportedContribingItemQnames[:] boundSummationItems.clear() # dereference facts in list elif arcrole == XbrlConst.essenceAlias: for modelRel in relsSet.modelRelationships: essenceConcept = modelRel.fromModelObject aliasConcept = modelRel.toModelObject essenceBindingKeys = self.esAlConceptBindKeys[essenceConcept] aliasBindingKeys = self.esAlConceptBindKeys[aliasConcept] for esAlBindKey in essenceBindingKeys & aliasBindingKeys: ancestor, contextHash = esAlBindKey essenceFactsKey = (essenceConcept, ancestor, contextHash) aliasFactsKey = (aliasConcept, ancestor, contextHash) if essenceFactsKey in self.esAlFacts and aliasFactsKey in self.esAlFacts: for eF in self.esAlFacts[essenceFactsKey]: for aF in self.esAlFacts[aliasFactsKey]: essenceUnit = self.mapUnit.get(eF.unit,eF.unit) aliasUnit = self.mapUnit.get(aF.unit,aF.unit) if essenceUnit != aliasUnit: self.modelXbrl.log('INCONSISTENCY', "xbrl.5.2.6.2.2:essenceAliasUnitsInconsistency", _("Essence-Alias inconsistent units from %(essenceConcept)s to %(aliasConcept)s in link role %(linkrole)s context %(contextID)s"), modelObject=(modelRel, eF, aF), essenceConcept=essenceConcept.qname, aliasConcept=aliasConcept.qname, linkrole=ELR, linkroleDefinition=self.modelXbrl.roleTypeDefinition(ELR), contextID=eF.context.id) if not XbrlUtil.vEqual(eF, aF): self.modelXbrl.log('INCONSISTENCY', "xbrl.5.2.6.2.2:essenceAliasUnitsInconsistency", _("Essence-Alias inconsistent value from %(essenceConcept)s to %(aliasConcept)s in link role %(linkrole)s context %(contextID)s"), modelObject=(modelRel, eF, aF), essenceConcept=essenceConcept.qname, aliasConcept=aliasConcept.qname, linkrole=ELR, linkroleDefinition=self.modelXbrl.roleTypeDefinition(ELR), contextID=eF.context.id) elif arcrole == XbrlConst.requiresElement: for modelRel in relsSet.modelRelationships: sourceConcept = modelRel.fromModelObject requiredConcept = modelRel.toModelObject if sourceConcept in self.requiresElementFacts and \ not requiredConcept in self.requiresElementFacts: self.modelXbrl.log('INCONSISTENCY', "xbrl.5.2.6.2.4:requiresElementInconsistency", _("Requires-Element %(requiringConcept)s missing required fact for %(requiredConcept)s in link role %(linkrole)s"), modelObject=sourceConcept, requiringConcept=sourceConcept.qname, requiredConcept=requiredConcept.qname, linkrole=ELR, linkroleDefinition=self.modelXbrl.roleTypeDefinition(ELR)) self.modelXbrl.profileActivity("... find inconsistencies", minTimeToShow=1.0) self.modelXbrl.profileActivity() # reset
def validate(self): self.modelXbrl.error(_("Validating calculations inferring {0}") .format(_("precision") if self.inferPrecision else _("decimals"))) # identify equal contexts contexts = tuple(self.modelXbrl.contexts.values()) for i in range(len(contexts)): cntx1 = contexts[i] for j in range(i+1, len(contexts)): cntx2 = contexts[j] if cntx1.isEqualTo(cntx2) and not cntx2 in self.mapContext: self.mapContext[cntx2] = cntx1 # identify equal contexts units = tuple(self.modelXbrl.units.values()) for i in range(len(units)): unit1 = units[i] for j in range(i+1, len(units)): unit2 = units[j] if unit1.isEqualTo(unit2) and not unit2 in self.mapUnit: self.mapUnit[unit2] = unit1 # identify concepts participating in essence-alias relationships # identify calcluation & essence-alias base sets (by key) for baseSetKey in self.modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname: if arcrole in (XbrlConst.essenceAlias, XbrlConst.requiresElement): conceptsSet = {XbrlConst.essenceAlias:self.conceptsInEssencesAlias, XbrlConst.requiresElement:self.conceptsInRequiresElement}[arcrole] for modelRel in self.modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname).modelRelationships: for concept in (modelRel.fromModelObject, modelRel.toModelObject): conceptsSet.add(concept) self.bindFacts(self.modelXbrl.facts,[self.modelXbrl.modelDocument.xmlRootElement]) # identify calcluation & essence-alias base sets (by key) for baseSetKey in self.modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname: if arcrole in (XbrlConst.summationItem, XbrlConst.essenceAlias, XbrlConst.requiresElement): relsSet = self.modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname) if arcrole == XbrlConst.summationItem: fromRelationships = relsSet.fromModelObjects() for sumConcept, modelRels in fromRelationships.items(): sumBindingKeys = self.sumConceptBindKeys[sumConcept] dupBindingKeys = set() boundSumKeys = set() # determine boundSums for modelRel in modelRels: itemBindingKeys = self.itemConceptBindKeys[modelRel.toModelObject] boundSumKeys |= sumBindingKeys & itemBindingKeys # add up rounded items boundSums = defaultdict(float) for modelRel in modelRels: weight = modelRel.weight itemConcept = modelRel.toModelObject for itemBindKey in boundSumKeys: ancestor, context, unit = itemBindKey factKey = (itemConcept, ancestor, context, unit) if factKey in self.itemFacts: for fact in self.itemFacts[factKey]: if fact in self.duplicatedFacts: dupBindingKeys.add(itemBindKey) else: boundSums[itemBindKey] += self.roundFact(fact) * weight for sumBindKey in boundSumKeys: ancestor, context, unit = sumBindKey factKey = (sumConcept, ancestor, context, unit) if factKey in self.sumFacts: for fact in self.sumFacts[factKey]: if fact in self.duplicatedFacts: dupBindingKeys.add(sumBindKey) elif sumBindKey not in dupBindingKeys: roundedSum = self.roundFact(fact) roundedItemsSum = self.roundFact(fact, vFloat=boundSums[sumBindKey]) if roundedItemsSum != self.roundFact(fact): self.modelXbrl.error( _("Calculation inconsistent from {0} in link role {1} reported sum {2} computed sum {3} context {4} unit {5}").format( sumConcept.qname, ELR, roundedSum, roundedItemsSum, context.id, unit.id), "err", "xbrl.5.2.5.2:calcInconsistency") elif arcrole == XbrlConst.essenceAlias: for modelRel in relsSet.modelRelationships: essenceConcept = modelRel.fromModelObject aliasConcept = modelRel.toModelObject essenceBindingKeys = self.esAlConceptBindKeys[essenceConcept] aliasBindingKeys = self.esAlConceptBindKeys[aliasConcept] for esAlBindKey in essenceBindingKeys & aliasBindingKeys: ancestor, context = esAlBindKey essenceFactsKey = (essenceConcept, ancestor, context) aliasFactsKey = (aliasConcept, ancestor, context) if essenceFactsKey in self.esAlFacts and aliasFactsKey in self.esAlFacts: for eF in self.esAlFacts[essenceFactsKey]: for aF in self.esAlFacts[aliasFactsKey]: essenceUnit = self.mapUnit.get(eF.unit,eF.unit) aliasUnit = self.mapUnit.get(aF.unit,aF.unit) if essenceUnit != aliasUnit: self.modelXbrl.error( _("Essence-Alias inconsistent units from {0} to {1} in link role {2} context {3}").format( essenceConcept.qname, aliasConcept.qname, ELR, context.id), "err", "xbrl.5.2.6.2.2:essenceAliasUnitsInconsistency") if not XbrlUtil.vEqual(essenceConcept, eF.element, aliasConcept, aF.element): self.modelXbrl.error( _("Essence-Alias inconsistent value from {0} to {1} in link role {2} context {3}").format( essenceConcept.qname, aliasConcept.qname, ELR, context.id), "err", "xbrl.5.2.6.2.2:essenceAliasUnitsInconsistency") elif arcrole == XbrlConst.requiresElement: for modelRel in relsSet.modelRelationships: sourceConcept = modelRel.fromModelObject requiredConcept = modelRel.toModelObject if sourceConcept in self.requiresElementFacts and \ not requiredConcept in self.requiresElementFacts: self.modelXbrl.error( _("Requies-Element {0} missing required fact for {1} in link role {2}").format( sourceConcept.qname, requiredConcept.qname, ELR), "err", "xbrl.5.2.6.2.4:requiresElementInconsistency")