def isVEqualTo(self, other, deemP0Equal=False): """(bool) -- v-equality of two facts Note that facts may be in different instances """ if self.isTuple or other.isTuple: return False if self.isNil: return other.isNil if other.isNil: return False if not self.context.isEqualTo(other.context): return False if self.concept.isNumeric: if other.concept.isNumeric: if not self.unit.isEqualTo(other.unit): return False if self.modelXbrl.modelManager.validateInferDecimals: d = min((inferredDecimals(self), inferredDecimals(other))); p = None if isnan(d) and deemP0Equal: return True else: d = None; p = min((inferredPrecision(self), inferredPrecision(other))) if p == 0 and deemP0Equal: return True return roundValue(self.value,precision=p,decimals=d) == roundValue(other.value,precision=p,decimals=d) else: return False selfValue = self.value otherValue = other.value if isinstance(selfValue,str) and isinstance(otherValue,str): return selfValue.strip() == otherValue.strip() else: return selfValue == otherValue
def isVEqualTo(self, other): if self.isTuple or other.isTuple: return False if self.isNil: return other.isNil if other.isNil: return False if not self.context.isEqualTo(other.context): return False if self.concept.isNumeric: if other.concept.isNumeric: if not self.unit.isEqualTo(other.unit): return False if self.modelXbrl.modelManager.validateInferDecimals: d = min((inferredDecimals(self), inferredDecimals(other))); p = None else: d = None; p = min((inferredPrecision(self), inferredPrecision(other))) return roundValue(float(self.value),precision=p,decimals=d) == roundValue(float(other.value),precision=p,decimals=d) else: return False selfValue = self.value otherValue = other.value if isinstance(selfValue,str) and isinstance(otherValue,str): return selfValue.strip() == otherValue.strip() else: return selfValue == otherValue
def generateInfoset(dts, infosetFile): if dts.fileSource.isArchive: return import os, io from arelle import XmlUtil, XbrlConst from arelle.ValidateXbrlCalcs import inferredPrecision, inferredDecimals XmlUtil.setXmlns(dts.modelDocument, "ptv", "http://www.xbrl.org/2003/ptv") numFacts = 0 for fact in dts.facts: try: if fact.concept.periodType: fact.set("{http://www.xbrl.org/2003/ptv}periodType", fact.concept.periodType) if fact.concept.balance: fact.set("{http://www.xbrl.org/2003/ptv}balance", fact.concept.balance) if fact.isNumeric and not fact.isNil: fact.set("{http://www.xbrl.org/2003/ptv}decimals", str(inferredDecimals(fact))) fact.set("{http://www.xbrl.org/2003/ptv}precision", str(inferredPrecision(fact))) numFacts += 1 except Exception as err: dts.error("saveInfoset.exception", _("Facts exception %(fact)s %(value)s %(error)s."), modelObject=fact, fact=fact.qname, value=fact.effectiveValue, error = err) fh = open(infosetFile, "w", encoding="utf-8") XmlUtil.writexml(fh, dts.modelDocument.xmlDocument, encoding="utf-8") fh.close() dts.info("info:saveInfoset", _("Infoset of %(entryFile)s has %(numberOfFacts)s facts in infoset file %(infosetOutputFile)s."), modelObject=dts, entryFile=dts.uri, numberOfFacts=numFacts, infosetOutputFile=infosetFile)
def generateInstanceInfoset(dts, instanceInfosetFile): if dts.fileSource.isArchive: return import os, io from arelle import XmlUtil, XbrlConst from arelle.ValidateXbrlCalcs import inferredPrecision, inferredDecimals XmlUtil.setXmlns(dts.modelDocument, u"ptv", u"http://www.xbrl.org/2003/ptv") numFacts = 0 for fact in dts.facts: try: if fact.concept.periodType: fact.set(u"{http://www.xbrl.org/2003/ptv}periodType", fact.concept.periodType) if fact.concept.balance: fact.set(u"{http://www.xbrl.org/2003/ptv}balance", fact.concept.balance) if fact.isNumeric and not fact.isNil: fact.set(u"{http://www.xbrl.org/2003/ptv}decimals", unicode(inferredDecimals(fact))) fact.set(u"{http://www.xbrl.org/2003/ptv}precision", unicode(inferredPrecision(fact))) numFacts += 1 except Exception, err: dts.error(u"saveInfoset.exception", _(u"Facts exception %(fact)s %(value)s %(error)s."), modelObject=fact, fact=fact.qname, value=fact.effectiveValue, error = err)
def generateInstanceInfoset(dts, instanceInfosetFile): if dts.fileSource.isArchive: return import os, io from arelle import XmlUtil, XbrlConst from arelle.ValidateXbrlCalcs import inferredPrecision, inferredDecimals XmlUtil.setXmlns(dts.modelDocument, "ptv", "http://www.xbrl.org/2003/ptv") numFacts = 0 for fact in dts.facts: try: if fact.concept.periodType: fact.set("{http://www.xbrl.org/2003/ptv}periodType", fact.concept.periodType) if fact.concept.balance: fact.set("{http://www.xbrl.org/2003/ptv}balance", fact.concept.balance) if fact.isNumeric and not fact.isNil: fact.set("{http://www.xbrl.org/2003/ptv}decimals", str(inferredDecimals(fact))) fact.set("{http://www.xbrl.org/2003/ptv}precision", str(inferredPrecision(fact))) numFacts += 1 except Exception as err: dts.error("saveInfoset.exception", _("Facts exception %(fact)s %(value)s %(error)s."), modelObject=fact, fact=fact.qname, value=fact.effectiveValue, error = err) fh = open(instanceInfosetFile, "w", encoding="utf-8") XmlUtil.writexml(fh, dts.modelDocument.xmlDocument, encoding="utf-8") fh.close() dts.info("info:saveInstanceInfoset", _("Instance infoset of %(entryFile)s has %(numberOfFacts)s facts in infoset file %(infosetOutputFile)s."), modelObject=dts, entryFile=dts.uri, numberOfFacts=numFacts, infosetOutputFile=instanceInfosetFile)
def isVEqualTo(self, other, deemP0Equal=False): # facts may be in different instances if self.isTuple or other.isTuple: return False if self.isNil: return other.isNil if other.isNil: return False if not self.context.isEqualTo(other.context): return False if self.concept.isNumeric: if other.concept.isNumeric: if not self.unit.isEqualTo(other.unit): return False if self.modelXbrl.modelManager.validateInferDecimals: d = min((inferredDecimals(self), inferredDecimals(other))) p = None else: d = None p = min( (inferredPrecision(self), inferredPrecision(other))) if p == 0 and deemP0Equal: return True return roundValue(self.value, precision=p, decimals=d) == roundValue(other.value, precision=p, decimals=d) else: return False selfValue = self.value otherValue = other.value if isinstance(selfValue, str) and isinstance(otherValue, str): return selfValue.strip() == otherValue.strip() else: return selfValue == otherValue
def infer_precision_decimals(xc, p, args, attrName): if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) != 1: raise XPathContext.FunctionArgType(1,"xbrl:item") modelItem = xc.modelItem(args[0][0]) if modelItem: modelConcept = modelItem.concept if modelConcept.isNumeric: if modelConcept.isFraction: return 'INF' from arelle.ValidateXbrlCalcs import (inferredDecimals,inferredPrecision) p = inferredPrecision(modelItem) if attrName == "precision" else inferredDecimals(modelItem) if isinf(p): return 'INF' if isnan(p): raise XPathContext.XPathException(p, 'xfie:ItemIsNotNumeric', _('Argument 1 {0} is not inferrable.').format(attrName)) return p raise XPathContext.XPathException(p, 'xfie:ItemIsNotNumeric', _('Argument 1 is not reported with {0}.').format(attrName))
def infer_precision_decimals(xc, p, args, attrName): if len(args) != 1: raise XPathContext.FunctionNumArgs() if len(args[0]) != 1: raise XPathContext.FunctionArgType(1, "xbrl:item") modelItem = xc.modelItem(args[0][0]) if modelItem: modelConcept = modelItem.concept if modelConcept.isNumeric: if modelConcept.isFraction: return 'INF' from arelle.ValidateXbrlCalcs import (inferredDecimals, inferredPrecision) p = inferredPrecision( modelItem) if attrName == "precision" else inferredDecimals( modelItem) if isinf(p): return 'INF' if isnan(p): raise XPathContext.XPathException( p, 'xfie:ItemIsNotNumeric', _('Argument 1 {0} is not inferrable.').format(attrName)) return p raise XPathContext.XPathException( p, 'xfie:ItemIsNotNumeric', _('Argument 1 is not reported with {0}.').format(attrName))
def validate(val, modelXbrl, infosetModelXbrl): infoset = infosetModelXbrl.modelDocument if infoset.type == Type.INSTANCE: # compare facts (assumed out of order) infosetFacts = defaultdict(list) for fact in infosetModelXbrl.facts: infosetFacts[fact.qname].append(fact) if len(modelXbrl.factsInInstance) != len( infosetModelXbrl.factsInInstance): modelXbrl.error( "arelle:infosetTest", _("Fact counts mismatch, testcase instance %(foundFactCount)s, infoset instance %(expectedFactCount)s" ), modelObject=(modelXbrl.modelDocument, infosetModelXbrl.modelDocument), foundFactCount=len(modelXbrl.factsInInstance), expectedFactCount=len(infosetModelXbrl.factsInInstance)) else: for i, instFact in enumerate(modelXbrl.facts): infosetFact = None for fact in infosetFacts[instFact.qname]: if fact.isTuple and fact.isDuplicateOf(instFact, deemP0Equal=True): infosetFact = fact break elif fact.isItem and fact.isVEqualTo(instFact, deemP0Equal=True): infosetFact = fact break if infosetFact is None: # takes precision/decimals into account if fact is not None: fact.isVEqualTo(instFact, deemP0Equal=True) modelXbrl.error( "arelle:infosetTest", _("Fact %(factNumber)s mismatch %(concept)s"), modelObject=instFact, factNumber=(i + 1), concept=instFact.qname) else: ptvPeriodType = infosetFact.get( "{http://www.xbrl.org/2003/ptv}periodType") ptvBalance = infosetFact.get( "{http://www.xbrl.org/2003/ptv}balance") ptvDecimals = infosetFact.get( "{http://www.xbrl.org/2003/ptv}decimals") ptvPrecision = infosetFact.get( "{http://www.xbrl.org/2003/ptv}precision") if ptvPeriodType and ptvPeriodType != instFact.concept.periodType: modelXbrl.error( "arelle:infosetTest", _("Fact %(factNumber)s periodType mismatch %(concept)s expected %(expectedPeriodType)s found %(foundPeriodType)s" ), modelObject=(instFact, infosetFact), factNumber=(i + 1), concept=instFact.qname, expectedPeriodType=ptvPeriodType, foundPeriodType=instFact.concept.periodType) if ptvBalance and ptvBalance != instFact.concept.balance: modelXbrl.error( "arelle:infosetTest", _("Fact %(factNumber)s balance mismatch %(concept)s expected %(expectedBalance)s found %(foundBalance)s" ), modelObject=(instFact, infosetFact), factNumber=(i + 1), concept=instFact.qname, expectedBalance=ptvBalance, foundBalance=instFact.concept.balance) if ptvDecimals and ptvDecimals != str( inferredDecimals(fact)): modelXbrl.error( "arelle:infosetTest", _("Fact %(factNumber)s inferred decimals mismatch %(concept)s expected %(expectedDecimals)s found %(inferredDecimals)s" ), modelObject=(instFact, infosetFact), factNumber=(i + 1), concept=instFact.qname, expectedDecimals=ptvDecimals, inferredDecimals=str(inferredDecimals(fact))) if ptvPrecision and ptvPrecision != str( inferredPrecision(fact)): modelXbrl.error( "arelle:infosetTest", _("Fact %(factNumber)s inferred precision mismatch %(concept)s expected %(expectedPrecision)s found %(inferredPrecision)s" ), modelObject=(instFact, infosetFact), factNumber=(i + 1), concept=instFact.qname, expectedPrecisions=ptvPrecision, inferredPrecision=str(inferredPrecision(fact))) elif infoset.type == Type.ARCSINFOSET: # compare arcs for arcElt in XmlUtil.children(infoset.xmlRootElement, "http://www.xbrl.org/2003/ptv", "arc"): linkType = arcElt.get("linkType") arcRole = arcElt.get("arcRole") extRole = arcElt.get("extRole") fromObj = resolvePath(modelXbrl, arcElt.get("fromPath")) if fromObj is None: modelXbrl.error("arelle:infosetTest", _("Arc fromPath not found: %(fromPath)s"), modelObject=arcElt, fromPath=arcElt.get("fromPath")) continue if linkType in ("label", "reference"): labelLang = arcElt.get("labelLang") resRole = arcElt.get("resRole") if linkType == "label": expectedLabel = XmlUtil.text(arcElt) foundLabel = fromObj.label(preferredLabel=resRole, fallbackToQname=False, lang=None, strip=True, linkrole=extRole) if foundLabel != expectedLabel: modelXbrl.error( "arelle:infosetTest", _("Label expected='%(expectedLabel)s', found='%(foundLabel)s'" ), modelObject=arcElt, expectedLabel=expectedLabel, foundLabel=foundLabel) continue elif linkType == "reference": expectedRef = XmlUtil.innerText(arcElt) referenceFound = False for refrel in modelXbrl.relationshipSet( XbrlConst.conceptReference, extRole).fromModelObject(fromObj): ref = refrel.toModelObject if resRole == ref.role: foundRef = XmlUtil.innerText(ref) if foundRef != expectedRef: modelXbrl.error( "arelle:infosetTest", _("Reference inner text expected='%(expectedRef)s, found='%(foundRef)s'" ), modelObject=arcElt, expectedRef=expectedRef, foundRef=foundRef) referenceFound = True break if referenceFound: continue modelXbrl.error( "arelle:infosetTest", _("%(linkType)s not found containing '%(text)s' linkRole %(linkRole)s" ), modelObject=arcElt, linkType=linkType.title(), text=XmlUtil.innerText(arcElt), linkRole=extRole) else: toObj = resolvePath(modelXbrl, arcElt.get("toPath")) if toObj is None: modelXbrl.error("arelle:infosetTest", _("Arc toPath not found: %(toPath)s"), modelObject=arcElt, toPath=arcElt.get("toPath")) continue weight = arcElt.get("weight") if weight is not None: weight = float(weight) order = arcElt.get("order") if order is not None: order = float(order) preferredLabel = arcElt.get("preferredLabel") found = False for rel in modelXbrl.relationshipSet( arcRole, extRole).fromModelObject(fromObj): if (rel.toModelObject == toObj and (weight is None or rel.weight == weight) and (order is None or rel.order == order)): found = True if not found: modelXbrl.error( "arelle:infosetTest", _("Arc not found: from %(fromPath)s, to %(toPath)s, role %(arcRole)s, linkRole $(extRole)s" ), modelObject=arcElt, fromPath=arcElt.get("fromPath"), toPath=arcElt.get("toPath"), arcRole=arcRole, linkRole=extRole) continue # validate dimensions of each fact factElts = XmlUtil.children(modelXbrl.modelDocument.xmlRootElement, None, "*") for itemElt in XmlUtil.children(infoset.xmlRootElement, None, "item"): try: qnElt = XmlUtil.child(itemElt, None, "qnElement") factQname = qname(qnElt, XmlUtil.text(qnElt)) sPointer = int(XmlUtil.child(itemElt, None, "sPointer").text) factElt = factElts[sPointer - 1] # 1-based xpath indexing if factElt.qname != factQname: modelXbrl.error( "arelle:infosetTest", _("Fact %(sPointer)s mismatch Qname, expected %(qnElt)s, observed %(factQname)s" ), modelObject=itemElt, sPointer=sPointer, qnElt=factQname, factQname=factElt.qname) elif not factElt.isItem or factElt.context is None: modelXbrl.error( "arelle:infosetTest", _("Fact %(sPointer)s has no context: %(qnElt)s"), modelObject=(itemElt, factElt), sPointer=sPointer, qnElt=factQname) else: context = factElt.context memberElts = XmlUtil.children(itemElt, None, "member") numNonDefaults = 0 for memberElt in memberElts: dimElt = XmlUtil.child(memberElt, None, "qnDimension") qnDim = qname(dimElt, XmlUtil.text(dimElt)) isDefault = XmlUtil.text( XmlUtil.child(memberElt, None, "bDefaulted")) == "true" if not isDefault: numNonDefaults += 1 if not ( (qnDim in context.qnameDims and not isDefault) or (qnDim in factElt.modelXbrl.qnameDimensionDefaults and isDefault)): modelXbrl.error( "arelle:infosetTest", _("Fact %(sPointer)s (qnElt)s dimension mismatch %(qnDim)s" ), modelObject=(itemElt, factElt, context), sPointer=sPointer, qnElt=factQname, qnDim=qnDim) if numNonDefaults != len(context.qnameDims): modelXbrl.error( "arelle:infosetTest", _("Fact %(sPointer)s (qnElt)s dimensions count mismatch" ), modelObject=(itemElt, factElt, context), sPointer=sPointer, qnElt=factQname) except (IndexError, ValueError, AttributeError) as err: modelXbrl.error( "arelle:infosetTest", _("Invalid entity fact dimensions infoset sPointer: %(test)s, error details: %(error)s" ), modelObject=itemElt, test=XmlUtil.innerTextList(itemElt), error=str(err))
def _precision(node, sphinxContext, args): fact = factArg(node, sphinxContext, args, 0) return inferredPrecision(fact)
def evaluate(xpCtx, varSet, derivedFact): # there may be multiple consis assertions parenting any formula for consisAsserRel in xpCtx.modelXbrl.relationshipSet( XbrlConst.consistencyAssertionFormula).toModelObject(varSet): consisAsser = consisAsserRel.fromModelObject hasProportionalAcceptanceRadius = consisAsser.hasProportionalAcceptanceRadius hasAbsoluteAcceptanceRadius = consisAsser.hasAbsoluteAcceptanceRadius if derivedFact is None: continue isNumeric = derivedFact.isNumeric if isNumeric and not derivedFact.isNil: derivedFactInferredPrecision = inferredPrecision(derivedFact) if derivedFactInferredPrecision == 0 and not hasProportionalAcceptanceRadius and not hasAbsoluteAcceptanceRadius: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency assertion {0} formula {1} fact {2} has zero precision and no radius is defined, skipping consistency assertion" ).format(consisAsser.id, varSet.xlinkLabel, derivedFact), "info", "formula:trace") continue # check xbrl validity of new fact # find source facts which match derived fact aspectMatchedInputFacts = [] isStrict = consisAsser.isStrict for inputFact in xpCtx.modelXbrl.facts: if (not inputFact.isNil and inputFact.qname == derivedFact.qname and inputFact.context.isEqualTo( derivedFact.context, dimensionalAspectModel=(varSet.aspectModel == "dimensional")) and (not isNumeric or inputFact.unit.isEqualTo(derivedFact.unit))): aspectMatchedInputFacts.append(inputFact) if len(aspectMatchedInputFacts) == 0: if isStrict: if derivedFact.isNil: isSatisfied = True else: isSatisfied = False else: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency assertion {0} Formula {1} no input facts matched to {2}, skipping consistency assertion" ).format(consisAsser.id, varSet.xlinkLabel, derivedFact), "info", "formula:trace") continue elif derivedFact.isNil: isSatisfied = False else: isSatisfied = True paramQnamesAdded = [] for paramRel in consisAsser.orderedVariableRelationships: paramQname = paramRel.variableQname paramVar = paramRel.toModelObject paramValue = xpCtx.inScopeVars.get(paramVar.qname) paramAlreadyInVars = paramQname in xpCtx.inScopeVars if not paramAlreadyInVars: paramQnamesAdded.append(paramQname) xpCtx.inScopeVars[paramQname] = paramValue for fact in aspectMatchedInputFacts: if isSatisfied != True: break if fact.isNil: if not derivedFact.isNil: isSatisfied = False elif isNumeric: factInferredPrecision = inferredPrecision(fact) if factInferredPrecision == 0 and not hasProportionalAcceptanceRadius and not hasAbsoluteAcceptanceRadius: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency assertion {0} Formula {1} input fact matched to {2} has zero precision and no radius, skipping consistency assertion" ).format(consisAsser.id, varSet.xlinkLabel, derivedFact), "info", "formula:trace") isSatisfied = None break if hasProportionalAcceptanceRadius or hasAbsoluteAcceptanceRadius: acceptance = consisAsser.evalRadius( xpCtx, derivedFact.vEqValue) if acceptance is not None: if hasProportionalAcceptanceRadius: acceptance *= derivedFact.vEqValue isSatisfied = fabs(derivedFact.vEqValue - fact.vEqValue) <= fabs(acceptance) else: isSatisfied = None # no radius else: p = min(derivedFactInferredPrecision, factInferredPrecision) if (p == 0 or roundValue(derivedFact.vEqValue, precision=p) != roundValue(fact.vEqValue, precision=p)): isSatisfied = False else: if not xEqual(fact.concept, fact.element, derivedFact.element, equalMode=S_EQUAL2): isSatisfied = False for paramQname in paramQnamesAdded: xpCtx.inScopeVars.pop(paramQname) if isSatisfied is None: continue # no evaluation if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency Assertion {0} result {1}").format( consisAsser.id, isSatisfied), "info", "formula:trace") message = consisAsser.message(isSatisfied) if message: xpCtx.modelXbrl.error(message.evaluate(xpCtx), "info", "message:" + consisAsser.id) if isSatisfied: consisAsser.countSatisfied += 1 else: consisAsser.countNotSatisfied += 1
def evaluate(xpCtx, varSet, derivedFact): # there may be multiple consis assertions parenting any formula for consisAsserRel in xpCtx.modelXbrl.relationshipSet(XbrlConst.consistencyAssertionFormula).toModelObject(varSet): consisAsser = consisAsserRel.fromModelObject hasProportionalAcceptanceRadius = consisAsser.hasProportionalAcceptanceRadius hasAbsoluteAcceptanceRadius = consisAsser.hasAbsoluteAcceptanceRadius if derivedFact is None: continue isNumeric = derivedFact.isNumeric if isNumeric and not derivedFact.isNil: derivedFactInferredPrecision = inferredPrecision(derivedFact) if derivedFactInferredPrecision == 0 and not hasProportionalAcceptanceRadius and not hasAbsoluteAcceptanceRadius: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency assertion {0} formula {1} fact {2} has zero precision and no radius is defined, skipping consistency assertion").format( consisAsser.id, varSet.xlinkLabel, derivedFact), "info", "formula:trace") continue # check xbrl validity of new fact # find source facts which match derived fact aspectMatchedInputFacts = [] isStrict = consisAsser.isStrict for inputFact in xpCtx.modelXbrl.facts: if (not inputFact.isNil and inputFact.qname == derivedFact.qname and inputFact.context.isEqualTo(derivedFact.context, dimensionalAspectModel=(varSet.aspectModel == "dimensional")) and (not isNumeric or inputFact.unit.isEqualTo(derivedFact.unit))): aspectMatchedInputFacts.append( inputFact ) if len(aspectMatchedInputFacts) == 0: if isStrict: if derivedFact.isNil: isSatisfied = True else: isSatisfied = False else: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency assertion {0} Formula {1} no input facts matched to {2}, skipping consistency assertion").format( consisAsser.id, varSet.xlinkLabel, derivedFact), "info", "formula:trace") continue elif derivedFact.isNil: isSatisfied = False else: isSatisfied = True paramQnamesAdded = [] for paramRel in consisAsser.orderedVariableRelationships: paramQname = paramRel.variableQname paramVar = paramRel.toModelObject paramValue = xpCtx.inScopeVars.get(paramVar.qname) paramAlreadyInVars = paramQname in xpCtx.inScopeVars if not paramAlreadyInVars: paramQnamesAdded.append(paramQname) xpCtx.inScopeVars[paramQname] = paramValue for fact in aspectMatchedInputFacts: if isSatisfied != True: break if fact.isNil: if not derivedFact.isNil: isSatisfied = False elif isNumeric: factInferredPrecision = inferredPrecision(fact) if factInferredPrecision == 0 and not hasProportionalAcceptanceRadius and not hasAbsoluteAcceptanceRadius: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency assertion {0} Formula {1} input fact matched to {2} has zero precision and no radius, skipping consistency assertion").format( consisAsser.id, varSet.xlinkLabel, derivedFact), "info", "formula:trace") isSatisfied = None break if hasProportionalAcceptanceRadius or hasAbsoluteAcceptanceRadius: acceptance = consisAsser.evalRadius(xpCtx, derivedFact.vEqValue) if acceptance is not None: if hasProportionalAcceptanceRadius: acceptance *= derivedFact.vEqValue isSatisfied = fabs(derivedFact.vEqValue - fact.vEqValue) <= fabs(acceptance) else: isSatisfied = None # no radius else: p = min(derivedFactInferredPrecision, factInferredPrecision) if (p == 0 or roundValue(derivedFact.vEqValue, precision=p) != roundValue(fact.vEqValue, precision=p)): isSatisfied = False else: if not xEqual(fact.concept, fact.element, derivedFact.element, equalMode=S_EQUAL2): isSatisfied = False for paramQname in paramQnamesAdded: xpCtx.inScopeVars.pop(paramQname) if isSatisfied is None: continue # no evaluation if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.error( _("Consistency Assertion {0} result {1}").format( consisAsser.id, isSatisfied), "info", "formula:trace") message = consisAsser.message(isSatisfied) if message: xpCtx.modelXbrl.error(message.evaluate(xpCtx), "info", "message:" + consisAsser.id) if isSatisfied: consisAsser.countSatisfied += 1 else: consisAsser.countNotSatisfied += 1
def validate(val, modelXbrl, infosetModelXbrl): infoset = infosetModelXbrl.modelDocument if infoset.type == Type.INSTANCE: # compare facts (assumed out of order) infosetFacts = defaultdict(list) for fact in infosetModelXbrl.facts: infosetFacts[fact.qname].append(fact) if len(modelXbrl.factsInInstance) != len(infosetModelXbrl.factsInInstance): modelXbrl.error("arelle:infosetTest", _("Fact counts mismatch, testcase instance %(foundFactCount)s, infoset instance %(expectedFactCount)s"), modelObject=(modelXbrl.modelDocument, infosetModelXbrl.modelDocument), foundFactCount=len(modelXbrl.factsInInstance), expectedFactCount=len(infosetModelXbrl.factsInInstance)) else: for i, instFact in enumerate(modelXbrl.facts): infosetFact = None for fact in infosetFacts[instFact.qname]: if fact.isTuple and fact.isDuplicateOf(instFact, deemP0Equal=True): infosetFact = fact break elif fact.isItem and fact.isVEqualTo(instFact, deemP0Equal=True): infosetFact = fact break if infosetFact is None: # takes precision/decimals into account if fact is not None: fact.isVEqualTo(instFact, deemP0Equal=True) modelXbrl.error("arelle:infosetTest", _("Fact %(factNumber)s mismatch %(concept)s"), modelObject=instFact, factNumber=(i+1), concept=instFact.qname) else: ptvPeriodType = infosetFact.get("{http://www.xbrl.org/2003/ptv}periodType") ptvBalance = infosetFact.get("{http://www.xbrl.org/2003/ptv}balance") ptvDecimals = infosetFact.get("{http://www.xbrl.org/2003/ptv}decimals") ptvPrecision = infosetFact.get("{http://www.xbrl.org/2003/ptv}precision") if ptvPeriodType and ptvPeriodType != instFact.concept.periodType: modelXbrl.error("arelle:infosetTest", _("Fact %(factNumber)s periodType mismatch %(concept)s expected %(expectedPeriodType)s found %(foundPeriodType)s"), modelObject=(instFact, infosetFact), factNumber=(i+1), concept=instFact.qname, expectedPeriodType=ptvPeriodType, foundPeriodType=instFact.concept.periodType) if ptvBalance and ptvBalance != instFact.concept.balance: modelXbrl.error("arelle:infosetTest", _("Fact %(factNumber)s balance mismatch %(concept)s expected %(expectedBalance)s found %(foundBalance)s"), modelObject=(instFact, infosetFact), factNumber=(i+1), concept=instFact.qname, expectedBalance=ptvBalance, foundBalance=instFact.concept.balance) if ptvDecimals and ptvDecimals != str(inferredDecimals(fact)): modelXbrl.error("arelle:infosetTest", _("Fact %(factNumber)s inferred decimals mismatch %(concept)s expected %(expectedDecimals)s found %(inferredDecimals)s"), modelObject=(instFact, infosetFact), factNumber=(i+1), concept=instFact.qname, expectedDecimals=ptvDecimals, inferredDecimals=str(inferredDecimals(fact))) if ptvPrecision and ptvPrecision != str(inferredPrecision(fact)): modelXbrl.error("arelle:infosetTest", _("Fact %(factNumber)s inferred precision mismatch %(concept)s expected %(expectedPrecision)s found %(inferredPrecision)s"), modelObject=(instFact, infosetFact), factNumber=(i+1), concept=instFact.qname, expectedPrecisions=ptvPrecision, inferredPrecision=str(inferredPrecision(fact))) elif infoset.type == Type.ARCSINFOSET: # compare arcs for arcElt in XmlUtil.children(infoset.xmlRootElement, "http://www.xbrl.org/2003/ptv", "arc"): linkType = arcElt.get("linkType") arcRole = arcElt.get("arcRole") extRole = arcElt.get("extRole") fromObj = resolvePath(modelXbrl, arcElt.get("fromPath")) if fromObj is None: modelXbrl.error("arelle:infosetTest", _("Arc fromPath not found: %(fromPath)s"), modelObject=arcElt, fromPath=arcElt.get("fromPath")) continue if linkType in ("label", "reference"): labelLang = arcElt.get("labelLang") resRole = arcElt.get("resRole") if linkType == "label": expectedLabel = XmlUtil.text(arcElt) foundLabel = fromObj.label(preferredLabel=resRole,fallbackToQname=False,lang=None,strip=True,linkrole=extRole) if foundLabel != expectedLabel: modelXbrl.error("arelle:infosetTest", _("Label expected='%(expectedLabel)s', found='%(foundLabel)s'"), modelObject=arcElt, expectedLabel=expectedLabel, foundLabel=foundLabel) continue elif linkType == "reference": expectedRef = XmlUtil.innerText(arcElt) referenceFound = False for refrel in modelXbrl.relationshipSet(XbrlConst.conceptReference,extRole).fromModelObject(fromObj): ref = refrel.toModelObject if resRole == ref.role: foundRef = XmlUtil.innerText(ref) if foundRef != expectedRef: modelXbrl.error("arelle:infosetTest", _("Reference inner text expected='%(expectedRef)s, found='%(foundRef)s'"), modelObject=arcElt, expectedRef=expectedRef, foundRef=foundRef) referenceFound = True break if referenceFound: continue modelXbrl.error("arelle:infosetTest", _("%(linkType)s not found containing '%(text)s' linkRole %(linkRole)s"), modelObject=arcElt, linkType=linkType.title(), text=XmlUtil.innerText(arcElt), linkRole=extRole) else: toObj = resolvePath(modelXbrl, arcElt.get("toPath")) if toObj is None: modelXbrl.error("arelle:infosetTest", _("Arc toPath not found: %(toPath)s"), modelObject=arcElt, toPath=arcElt.get("toPath")) continue weight = arcElt.get("weight") if weight is not None: weight = float(weight) order = arcElt.get("order") if order is not None: order = float(order) preferredLabel = arcElt.get("preferredLabel") found = False for rel in modelXbrl.relationshipSet(arcRole, extRole).fromModelObject(fromObj): if (rel.toModelObject == toObj and (weight is None or rel.weight == weight) and (order is None or rel.order == order)): found = True if not found: modelXbrl.error("arelle:infosetTest", _("Arc not found: from %(fromPath)s, to %(toPath)s, role %(arcRole)s, linkRole $(extRole)s"), modelObject=arcElt, fromPath=arcElt.get("fromPath"), toPath=arcElt.get("toPath"), arcRole=arcRole, linkRole=extRole) continue # validate dimensions of each fact factElts = XmlUtil.children(modelXbrl.modelDocument.xmlRootElement, None, "*") for itemElt in XmlUtil.children(infoset.xmlRootElement, None, "item"): try: qnElt = XmlUtil.child(itemElt,None,"qnElement") factQname = qname(qnElt, XmlUtil.text(qnElt)) sPointer = int(XmlUtil.child(itemElt,None,"sPointer").text) factElt = factElts[sPointer - 1] # 1-based xpath indexing if factElt.qname != factQname: modelXbrl.error("arelle:infosetTest", _("Fact %(sPointer)s mismatch Qname, expected %(qnElt)s, observed %(factQname)s"), modelObject=itemElt, sPointer=sPointer, qnElt=factQname, factQname=factElt.qname) elif not factElt.isItem or factElt.context is None: modelXbrl.error("arelle:infosetTest", _("Fact %(sPointer)s has no context: %(qnElt)s"), modelObject=(itemElt,factElt), sPointer=sPointer, qnElt=factQname) else: context = factElt.context memberElts = XmlUtil.children(itemElt,None,"member") numNonDefaults = 0 for memberElt in memberElts: dimElt = XmlUtil.child(memberElt, None, "qnDimension") qnDim = qname(dimElt, XmlUtil.text(dimElt)) isDefault = XmlUtil.text(XmlUtil.child(memberElt, None, "bDefaulted")) == "true" if not isDefault: numNonDefaults += 1 if not ((qnDim in context.qnameDims and not isDefault) or (qnDim in factElt.modelXbrl.qnameDimensionDefaults and isDefault)): modelXbrl.error("arelle:infosetTest", _("Fact %(sPointer)s (qnElt)s dimension mismatch %(qnDim)s"), modelObject=(itemElt, factElt, context), sPointer=sPointer, qnElt=factQname, qnDim=qnDim) if numNonDefaults != len(context.qnameDims): modelXbrl.error("arelle:infosetTest", _("Fact %(sPointer)s (qnElt)s dimensions count mismatch"), modelObject=(itemElt, factElt, context), sPointer=sPointer, qnElt=factQname) except (IndexError, ValueError, AttributeError) as err: modelXbrl.error("arelle:infosetTest", _("Invalid entity fact dimensions infoset sPointer: %(test)s, error details: %(error)s"), modelObject=itemElt, test=XmlUtil.innerTextList(itemElt), error=str(err))
def evaluate(xpCtx, varSet, derivedFact): # there may be multiple consis assertions parenting any formula for consisAsserRel in xpCtx.modelXbrl.relationshipSet(XbrlConst.consistencyAssertionFormula).toModelObject(varSet): consisAsser = consisAsserRel.fromModelObject hasProportionalAcceptanceRadius = consisAsser.hasProportionalAcceptanceRadius hasAbsoluteAcceptanceRadius = consisAsser.hasAbsoluteAcceptanceRadius if derivedFact is None: continue isNumeric = derivedFact.isNumeric if isNumeric and not derivedFact.isNil: derivedFactInferredPrecision = inferredPrecision(derivedFact) if derivedFactInferredPrecision == 0 and not hasProportionalAcceptanceRadius and not hasAbsoluteAcceptanceRadius: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.info(u"formula:trace", _(u"Consistency assertion %(id)s formula %(xlinkLabel)s fact %(derivedFact)s has zero precision and no radius is defined, skipping consistency assertion"), modelObject=consisAsser, id=consisAsser.id, xlinkLabel=varSet.xlinkLabel, derivedFact=derivedFact) continue # check xbrl validity of new fact # find source facts which match derived fact aspectMatchedInputFacts = [] isStrict = consisAsser.isStrict for inputFact in xpCtx.modelXbrl.facts: if (not inputFact.isNil and inputFact.qname == derivedFact.qname and inputFact.context.isEqualTo(derivedFact.context, dimensionalAspectModel=(varSet.aspectModel == u"dimensional")) and (not isNumeric or inputFact.unit.isEqualTo(derivedFact.unit))): aspectMatchedInputFacts.append( inputFact ) if len(aspectMatchedInputFacts) == 0: if isStrict: if derivedFact.isNil: isSatisfied = True else: isSatisfied = False else: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.info(u"formula:trace", _(u"Consistency assertion %(id)s formula %(xlinkLabel)s no input facts matched to %(derivedFact)s, skipping consistency assertion"), modelObject=consisAsser, id=consisAsser.id, xlinkLabel=varSet.xlinkLabel, derivedFact=derivedFact) continue elif derivedFact.isNil: isSatisfied = False else: isSatisfied = True paramQnamesAdded = [] for paramRel in consisAsser.orderedVariableRelationships: paramQname = paramRel.variableQname paramVar = paramRel.toModelObject paramValue = xpCtx.inScopeVars.get(paramVar.parameterQname) paramAlreadyInVars = paramQname in xpCtx.inScopeVars if not paramAlreadyInVars: paramQnamesAdded.append(paramQname) xpCtx.inScopeVars[paramQname] = paramValue acceptance = None for fact in aspectMatchedInputFacts: if isSatisfied != True: break if fact.isNil: if not derivedFact.isNil: isSatisfied = False elif isNumeric: factInferredPrecision = inferredPrecision(fact) if factInferredPrecision == 0 and not hasProportionalAcceptanceRadius and not hasAbsoluteAcceptanceRadius: if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.info(u"formula:trace", _(u"Consistency assertion %(id)s formula %(xlinkLabel)s input fact matched to %(derivedFact)s has zero precision and no radius, skipping consistency assertion"), modelObject=consisAsser, id=consisAsser.id, xlinkLabel=varSet.xlinkLabel, derivedFact=derivedFact) isSatisfied = None break if hasProportionalAcceptanceRadius or hasAbsoluteAcceptanceRadius: acceptance = consisAsser.evalRadius(xpCtx, derivedFact.vEqValue) if acceptance is not None: if hasProportionalAcceptanceRadius: acceptance *= derivedFact.vEqValue isSatisfied = fabs(derivedFact.vEqValue - fact.vEqValue) <= fabs(acceptance) else: isSatisfied = None # no radius else: p = min(derivedFactInferredPrecision, factInferredPrecision) if (p == 0 or roundValue(derivedFact.value, precision=p) != roundValue(fact.value, precision=p)): isSatisfied = False else: if not xEqual(fact, derivedFact, equalMode=S_EQUAL2): isSatisfied = False if isSatisfied is not None: # None means no evaluation if xpCtx.formulaOptions.traceVariableSetExpressionResult: xpCtx.modelXbrl.info(u"formula:trace", _(u"Consistency assertion %(id)s result %(result)s"), modelObject=consisAsser, id=consisAsser.id, result=isSatisfied) message = consisAsser.message(isSatisfied) if message is not None: xpCtx.inScopeVars[XbrlConst.qnCaAspectMatchedFacts] = aspectMatchedInputFacts xpCtx.inScopeVars[XbrlConst.qnCaAcceptanceRadius] = acceptance xpCtx.inScopeVars[XbrlConst.qnCaAbsoluteAcceptanceRadiusExpression] = consisAsser.get(u"absoluteAcceptanceRadius") xpCtx.inScopeVars[XbrlConst.qnCaProportionalAcceptanceRadiusExpression] = consisAsser.get(u"proportionalAcceptanceRadius") xpCtx.modelXbrl.info(u"message:" + consisAsser.id, message.evaluate(xpCtx), modelObject=message, messageCodes=(u"message:{variableSetID|xlinkLabel}")) xpCtx.inScopeVars.pop(XbrlConst.qnCaAspectMatchedFacts) xpCtx.inScopeVars.pop(XbrlConst.qnCaAcceptanceRadius) xpCtx.inScopeVars.pop(XbrlConst.qnCaAbsoluteAcceptanceRadiusExpression) xpCtx.inScopeVars.pop(XbrlConst.qnCaProportionalAcceptanceRadiusExpression) if isSatisfied: consisAsser.countSatisfied += 1 else: consisAsser.countNotSatisfied += 1 for paramQname in paramQnamesAdded: xpCtx.inScopeVars.pop(paramQname)