def createContext(self, entityIdentScheme, entityIdentValue, periodType, periodStart, periodEndInstant, priItem, dims, segOCCs, scenOCCs, afterSibling=None, beforeSibling=None): xbrlElt = self.modelDocument.xmlRootElement if afterSibling == AUTO_LOCATE_ELEMENT: afterSibling = XmlUtil.lastChild(xbrlElt, XbrlConst.xbrli, ("schemaLocation", "roleType", "arcroleType", "context")) cntxId = 'c-{0:02n}'.format( len(self.contexts) + 1) newCntxElt = XmlUtil.addChild(xbrlElt, XbrlConst.xbrli, "context", attributes=("id", cntxId), afterSibling=afterSibling, beforeSibling=beforeSibling) entityElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "entity") XmlUtil.addChild(entityElt, XbrlConst.xbrli, "identifier", attributes=("scheme", entityIdentScheme), text=entityIdentValue) periodElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "period") if periodType == "forever": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "forever") elif periodType == "instant": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "instant", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) elif periodType == "duration": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "startDate", text=XmlUtil.dateunionValue(periodStart)) XmlUtil.addChild(periodElt, XbrlConst.xbrli, "endDate", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) segmentElt = None scenarioElt = None from arelle.ModelInstanceObject import ModelDimensionValue if dims: # requires primary item to determin ambiguous concepts ''' in theory we have to check full set of dimensions for validity in source or any other context element, but for shortcut will see if each dimension is already reported in an unambiguous valid contextElement ''' from arelle.PrototypeInstanceObject import FactPrototype, ContextPrototype, DimValuePrototype fp = FactPrototype(self, priItem, dims.items()) # force trying a valid prototype's context Elements if not isFactDimensionallyValid(self, fp, setPrototypeContextElements=True): self.info("arelleLinfo", _("Create context for %(priItem)s, cannot determine valid context elements, no suitable hypercubes"), modelObject=self, priItem=priItem) fpDims = fp.context.qnameDims for dimQname in sorted(fpDims.keys()): dimValue = fpDims[dimQname] if isinstance(dimValue, DimValuePrototype): dimMemberQname = dimValue.memberQname # None if typed dimension contextEltName = dimValue.contextElement else: # qname for explicit or node for typed dimMemberQname = None contextEltName = None if contextEltName == "segment": if segmentElt is None: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") contextElt = segmentElt elif contextEltName == "scenario": if scenarioElt is None: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") contextElt = scenarioElt else: self.info("arelleLinfo", _("Create context, %(dimension)s, cannot determine context element, either no all relationship or validation issue"), modelObject=self, dimension=dimQname), continue dimConcept = self.qnameConcepts[dimQname] dimAttr = ("dimension", XmlUtil.addQnameValue(xbrlElt, dimConcept.qname)) if dimConcept.isTypedDimension: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "xbrldi:typedMember", attributes=dimAttr) if isinstance(dimValue, (ModelDimensionValue, DimValuePrototype)) and dimValue.isTyped: XmlUtil.copyNodes(dimElt, dimValue.typedMember) elif dimMemberQname: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "xbrldi:explicitMember", attributes=dimAttr, text=XmlUtil.addQnameValue(xbrlElt, dimMemberQname)) if segOCCs: if segmentElt is None: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") XmlUtil.copyNodes(segmentElt, segOCCs) if scenOCCs: if scenarioElt is None: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") XmlUtil.copyNodes(scenarioElt, scenOCCs) self.modelDocument.contextDiscover(newCntxElt) XmlValidate.validate(self, newCntxElt) return newCntxElt
def produceOutputFact(xpCtx, formula, result): priorErrorCount = len(xpCtx.modelXbrl.errors) # assemble context conceptQname = aspectValue(xpCtx, formula, Aspect.CONCEPT, "xbrlfe:missingConceptRule") if isinstance(conceptQname, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} concept: {1}").format(formula, conceptQname.msg), "err", conceptQname.err) modelConcept = None else: modelConcept = xpCtx.modelXbrl.qnameConcepts[conceptQname] if modelConcept is None or not modelConcept.isItem: xpCtx.modelXbrl.error( _("Formula {0} concept {1} is not an item").format( formula, conceptQname), "err", "xbrlfe:missingConceptRule") # entity entityIdentScheme = aspectValue(xpCtx, formula, Aspect.SCHEME, "xbrlfe:missingEntityIdentifierRule") if isinstance(entityIdentScheme, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} entity identifier scheme: {1}").format( formula, entityIdentScheme.msg), "err", str(entityIdentScheme)) entityIdentValue = None else: entityIdentValue = aspectValue(xpCtx, formula, Aspect.VALUE, "xbrlfe:missingEntityIdentifierRule") if isinstance(entityIdentValue, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} entity identifier value: {1}").format( formula, entityIdentValue.msg), "err", str(entityIdentScheme)) # period periodType = aspectValue(xpCtx, formula, Aspect.PERIOD_TYPE, "xbrlfe:missingPeriodRule") periodStart = None periodEndInstant = None if isinstance(periodType, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period type: {1}").format(formula, periodType.msg), "err", str(periodType)) elif periodType == "instant": periodEndInstant = aspectValue(xpCtx, formula, Aspect.INSTANT, "xbrlfe:missingPeriodRule") if isinstance(periodEndInstant, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period start: {1}").format( formula, periodEndInstant.msg), "err", str(periodEndInstant)) elif periodType == "duration": periodStart = aspectValue(xpCtx, formula, Aspect.START, "xbrlfe:missingPeriodRule") if isinstance(periodStart, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period start: {1}").format( formula, periodStart.msg), "err", str(periodStart)) periodEndInstant = aspectValue(xpCtx, formula, Aspect.END, "xbrlfe:missingPeriodRule") if isinstance(periodEndInstant, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period end: {1}").format( formula, periodEndInstant.msg), "err", str(periodEndInstant)) # unit if modelConcept and modelConcept.isNumeric: unitSource = aspectValue(xpCtx, formula, Aspect.UNIT_MEASURES, None) multDivBy = aspectValue(xpCtx, formula, Aspect.MULTIPLY_BY, "xbrlfe:missingUnitRule") if isinstance(multDivBy, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} unit: {1}").format(formula, multDivBy.msg), "err", str(multDivBy) if isinstance(multDivBy, VariableBindingError) else "xbrlfe:missingUnitRule") multiplyBy = () divideBy = () # prevent errors later if bad else: divMultBy = aspectValue(xpCtx, formula, Aspect.DIVIDE_BY, "xbrlfe:missingUnitRule") if isinstance(divMultBy, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} unit: {1}").format(formula, divMultBy.msg), "err", str(multDivBy) if isinstance(divMultBy, VariableBindingError) else "xbrlfe:missingUnitRule") multiplyBy = () divideBy = () # prevent errors later if bad else: multiplyBy = unitSource[0] + multDivBy[0] + divMultBy[1] divideBy = unitSource[1] + multDivBy[1] + divMultBy[0] # remove cancelling mult/div units lookForCommonUnits = True while lookForCommonUnits: lookForCommonUnits = False for commonUnit in multiplyBy: if commonUnit in divideBy: multiplyBy.remove(commonUnit) divideBy.remove(commonUnit) lookForCommonUnits = True break if len(multiplyBy) == 0: # if no units add pure multiplyBy.append(XbrlConst.qnXbrliPure) # dimensions segOCCs = [] scenOCCs = [] if formula.aspectModel == "dimensional": dimAspects = {} dimQnames = aspectValue(xpCtx, formula, Aspect.DIMENSIONS, None) if dimQnames: for dimQname in dimQnames: dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname] dimErr = "xbrlfe:missing{0}DimensionRule".format( "typed" if dimConcept and dimConcept.isTypedDimension else "explicit") dimValue = aspectValue(xpCtx, formula, dimQname, dimErr) if isinstance(dimValue, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} dimension {1}: {2}").format( formula, dimQname, dimValue.msg), "err", dimErr) elif dimValue and xpCtx.modelXbrl.qnameDimensionDefaults.get( dimQname) != dimValue: dimAspects[dimQname] = dimValue segOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SEGMENT, None) scenOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SCENARIO, None) else: dimAspects = None # non-dimensional segOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SEGMENT, None) scenOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SCENARIO, None) if priorErrorCount < len(xpCtx.modelXbrl.errors): return None # had errors, don't produce output fact # does context exist in out instance document outputInstanceQname = formula.outputInstanceQname outputXbrlInstance = xpCtx.inScopeVars[outputInstanceQname] xbrlElt = outputXbrlInstance.modelDocument.xmlRootElement # in source instance document # add context prevCntx = outputXbrlInstance.matchContext(entityIdentScheme, entityIdentValue, periodType, periodStart, periodEndInstant, dimAspects, segOCCs, scenOCCs) if prevCntx: cntxId = prevCntx.id newCntxElt = prevCntx.element else: cntxId = 'c-{0:02n}'.format(len(outputXbrlInstance.contexts) + 1) newCntxElt = XmlUtil.addChild( xbrlElt, XbrlConst.xbrli, "context", attributes=("id", cntxId), afterSibling=xpCtx.outputLastContext.get(outputInstanceQname)) xpCtx.outputLastContext[outputInstanceQname] = newCntxElt entityElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "entity") XmlUtil.addChild(entityElt, XbrlConst.xbrli, "identifier", attributes=("scheme", entityIdentScheme), text=entityIdentValue) periodElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "period") if periodType == "forever": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "forever") elif periodType == "instant": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "instant", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) elif periodType == "duration": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "startDate", text=XmlUtil.dateunionValue(periodStart)) XmlUtil.addChild(periodElt, XbrlConst.xbrli, "endDate", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) segmentElt = None scenarioElt = None from arelle.ModelObject import ModelDimensionValue if dimAspects: for dimQname in sorted(dimAspects.keys()): dimValue = dimAspects[dimQname] if isinstance(dimValue, ModelDimensionValue): if dimValue.isExplicit: dimMemberQname = dimValue.memberQname contextEltName = dimValue.contextElement else: # qname for explicit or node for typed dimMemberQname = dimValue contextEltName = xpCtx.modelXbrl.qnameDimensionContextElement.get( dimQname) if contextEltName == "segment": if not segmentElt: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") contextElt = segmentElt elif contextEltName == "scenario": if not scenarioElt: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") contextElt = scenarioElt else: continue dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname] dimAttr = ("dimension", XmlUtil.addQnameValue(xbrlElt, dimConcept.qname)) if dimConcept.isTypedDimension: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "typedMember", attributes=dimAttr) if isinstance(dimValue, ModelDimensionValue) and dimValue.isTyped: XmlUtil.copyChildren(dimElt, dimValue.typedMember) elif dimMemberQname: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "explicitMember", attributes=dimAttr, text=XmlUtil.addQnameValue( xbrlElt, dimMemberQname)) if segOCCs: if not segmentElt: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") XmlUtil.copyNodes(segmentElt, segOCCs) if scenOCCs: if not scenarioElt: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") XmlUtil.copyNodes(scenarioElt, scenOCCs) outputXbrlInstance.modelDocument.contextDiscover(newCntxElt) # does unit exist # add unit if modelConcept.isNumeric: prevUnit = outputXbrlInstance.matchUnit(multiplyBy, divideBy) if prevUnit: unitId = prevUnit.id newUnitElt = prevUnit.element else: unitId = 'u-{0:02n}'.format(len(outputXbrlInstance.units) + 1) newUnitElt = XmlUtil.addChild( xbrlElt, XbrlConst.xbrli, "unit", attributes=("id", unitId), afterSibling=xpCtx.outputLastUnit.get(outputInstanceQname)) xpCtx.outputLastUnit[outputInstanceQname] = newUnitElt if len(divideBy) == 0: for multiply in multiplyBy: XmlUtil.addChild(newUnitElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue( xbrlElt, multiply)) else: divElt = XmlUtil.addChild(newUnitElt, XbrlConst.xbrli, "divide") numElt = XmlUtil.addChild(divElt, XbrlConst.xbrli, "unitNumerator") denElt = XmlUtil.addChild(divElt, XbrlConst.xbrli, "unitDenominator") for multiply in multiplyBy: XmlUtil.addChild(numElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue( xbrlElt, multiply)) for divide in divideBy: XmlUtil.addChild(denElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue( xbrlElt, divide)) outputXbrlInstance.modelDocument.unitDiscover(newUnitElt) # add fact attrs = [("contextRef", cntxId)] precision = None decimals = None if modelConcept.isNumeric: attrs.append(("unitRef", unitId)) value = formula.evaluate(xpCtx) valueSeqLen = len(value) if valueSeqLen > 1: xpCtx.modelXbrl.error( _("Formula {0} value is a sequence of length {1}").format( formula, valueSeqLen), "err", "xbrlfe:nonSingletonOutputValue") else: if valueSeqLen == 0: #xsi:nil if no value attrs.append((XbrlConst.qnXsiNil, "true")) v = None else: # add precision/decimals for non-fraction numerics if modelConcept.isNumeric and not modelConcept.isFraction: if formula.hasDecimals: decimals = formula.evaluateRule(xpCtx, Aspect.DECIMALS) attrs.append(("decimals", decimals)) else: if formula.hasPrecision: precision = formula.evaluateRule( xpCtx, Aspect.PRECISION) else: precision = 0 attrs.append(("precision", precision)) x = value[0] if isinstance(x, float): from math import (log10, isnan, isinf, fabs) if (isnan(x) or (precision and (isinf(precision) or precision == 0)) or (decimals and isinf(decimals))): v = string(xpCtx, x) elif decimals is not None: v = "%.*f" % (int(decimals), x) elif precision is not None: a = fabs(x) log = log10(a) if a != 0 else 0 v = "%.*f" % (int(precision) - int(log) - (1 if a >= 1 else 0), x) else: # no implicit precision yet v = string(xpCtx, x) elif isinstance(x, QName): v = XmlUtil.addQnameValue(xbrlElt, x) elif isinstance(x, datetime.datetime): v = XmlUtil.dateunionValue(x) else: v = string(xpCtx, x) itemElt = XmlUtil.addChild( xbrlElt, conceptQname, attributes=attrs, text=v, afterSibling=xpCtx.outputLastFact.get(outputInstanceQname)) xpCtx.outputLastFact[outputInstanceQname] = itemElt newFact = outputXbrlInstance.modelDocument.factDiscover( itemElt, outputXbrlInstance.facts) return newFact
def createContext(self, entityIdentScheme, entityIdentValue, periodType, periodStart, periodEndInstant, priItem, dims, segOCCs, scenOCCs, afterSibling=None, beforeSibling=None): xbrlElt = self.modelDocument.xmlRootElement if afterSibling == AUTO_LOCATE_ELEMENT: afterSibling = XmlUtil.lastChild( xbrlElt, XbrlConst.xbrli, ("schemaLocation", "roleType", "arcroleType", "context")) cntxId = 'c-{0:02n}'.format(len(self.contexts) + 1) newCntxElt = XmlUtil.addChild(xbrlElt, XbrlConst.xbrli, "context", attributes=("id", cntxId), afterSibling=afterSibling, beforeSibling=beforeSibling) entityElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "entity") XmlUtil.addChild(entityElt, XbrlConst.xbrli, "identifier", attributes=("scheme", entityIdentScheme), text=entityIdentValue) periodElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "period") if periodType == "forever": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "forever") elif periodType == "instant": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "instant", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) elif periodType == "duration": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "startDate", text=XmlUtil.dateunionValue(periodStart)) XmlUtil.addChild(periodElt, XbrlConst.xbrli, "endDate", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) segmentElt = None scenarioElt = None from arelle.ModelInstanceObject import ModelDimensionValue if dims: # requires primary item to determin ambiguous concepts ''' in theory we have to check full set of dimensions for validity in source or any other context element, but for shortcut will see if each dimension is already reported in an unambiguous valid contextElement ''' from arelle.PrototypeInstanceObject import FactPrototype, ContextPrototype, DimValuePrototype fp = FactPrototype(self, priItem, dims.items()) # force trying a valid prototype's context Elements if not isFactDimensionallyValid( self, fp, setPrototypeContextElements=True): self.info( "arelleLinfo", _("Create context for %(priItem)s, cannot determine valid context elements, no suitable hypercubes" ), modelObject=self, priItem=priItem) fpDims = fp.context.qnameDims for dimQname in sorted(fpDims.keys()): dimValue = fpDims[dimQname] if isinstance(dimValue, DimValuePrototype): dimMemberQname = dimValue.memberQname # None if typed dimension contextEltName = dimValue.contextElement else: # qname for explicit or node for typed dimMemberQname = None contextEltName = None if contextEltName == "segment": if segmentElt is None: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") contextElt = segmentElt elif contextEltName == "scenario": if scenarioElt is None: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") contextElt = scenarioElt else: self.info( "arelleLinfo", _("Create context, %(dimension)s, cannot determine context element, either no all relationship or validation issue" ), modelObject=self, dimension=dimQname), continue dimConcept = self.qnameConcepts[dimQname] dimAttr = ("dimension", XmlUtil.addQnameValue(xbrlElt, dimConcept.qname)) if dimConcept.isTypedDimension: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "xbrldi:typedMember", attributes=dimAttr) if isinstance(dimValue, (ModelDimensionValue, DimValuePrototype)) and dimValue.isTyped: XmlUtil.copyNodes(dimElt, dimValue.typedMember) elif dimMemberQname: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "xbrldi:explicitMember", attributes=dimAttr, text=XmlUtil.addQnameValue( xbrlElt, dimMemberQname)) if segOCCs: if segmentElt is None: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") XmlUtil.copyNodes(segmentElt, segOCCs) if scenOCCs: if scenarioElt is None: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") XmlUtil.copyNodes(scenarioElt, scenOCCs) self.modelDocument.contextDiscover(newCntxElt) XmlValidate.validate(self, newCntxElt) return newCntxElt
def produceOutputFact(xpCtx, formula, result): priorErrorCount = len(xpCtx.modelXbrl.errors) # assemble context conceptQname = aspectValue(xpCtx, formula, Aspect.CONCEPT, "xbrlfe:missingConceptRule") if isinstance(conceptQname, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} concept: {1}").format( formula, conceptQname.msg), "err", conceptQname.err) modelConcept = None else: modelConcept = xpCtx.modelXbrl.qnameConcepts[conceptQname] if modelConcept is None or not modelConcept.isItem: xpCtx.modelXbrl.error( _("Formula {0} concept {1} is not an item").format( formula, conceptQname), "err", "xbrlfe:missingConceptRule") # entity entityIdentScheme = aspectValue(xpCtx, formula, Aspect.SCHEME, "xbrlfe:missingEntityIdentifierRule") if isinstance(entityIdentScheme, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} entity identifier scheme: {1}").format( formula, entityIdentScheme.msg ), "err", str(entityIdentScheme)) entityIdentValue = None else: entityIdentValue = aspectValue(xpCtx, formula, Aspect.VALUE, "xbrlfe:missingEntityIdentifierRule") if isinstance(entityIdentValue, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} entity identifier value: {1}").format( formula, entityIdentValue.msg ), "err", str(entityIdentScheme)) # period periodType = aspectValue(xpCtx, formula, Aspect.PERIOD_TYPE, "xbrlfe:missingPeriodRule") periodStart = None periodEndInstant = None if isinstance(periodType, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period type: {1}").format( formula, periodType.msg ), "err", str(periodType)) elif periodType == "instant": periodEndInstant = aspectValue(xpCtx, formula, Aspect.INSTANT, "xbrlfe:missingPeriodRule") if isinstance(periodEndInstant, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period start: {1}").format( formula, periodEndInstant.msg ), "err", str(periodEndInstant)) elif periodType == "duration": periodStart = aspectValue(xpCtx, formula, Aspect.START, "xbrlfe:missingPeriodRule") if isinstance(periodStart, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period start: {1}").format( formula, periodStart.msg ), "err", str(periodStart)) periodEndInstant = aspectValue(xpCtx, formula, Aspect.END, "xbrlfe:missingPeriodRule") if isinstance(periodEndInstant, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} period end: {1}").format( formula, periodEndInstant.msg ), "err", str(periodEndInstant)) # unit if modelConcept and modelConcept.isNumeric: unitSource = aspectValue(xpCtx, formula, Aspect.UNIT_MEASURES, None) multDivBy = aspectValue(xpCtx, formula, Aspect.MULTIPLY_BY, "xbrlfe:missingUnitRule") if isinstance(multDivBy, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} unit: {1}").format( formula, multDivBy.msg ), "err", str(multDivBy) if isinstance(multDivBy, VariableBindingError) else "xbrlfe:missingUnitRule") multiplyBy = (); divideBy = () # prevent errors later if bad else: divMultBy = aspectValue(xpCtx, formula, Aspect.DIVIDE_BY, "xbrlfe:missingUnitRule") if isinstance(divMultBy, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} unit: {1}").format( formula, divMultBy.msg ), "err", str(multDivBy) if isinstance(divMultBy, VariableBindingError) else "xbrlfe:missingUnitRule") multiplyBy = (); divideBy = () # prevent errors later if bad else: multiplyBy = unitSource[0] + multDivBy[0] + divMultBy[1] divideBy = unitSource[1] + multDivBy[1] + divMultBy[0] # remove cancelling mult/div units lookForCommonUnits = True while lookForCommonUnits: lookForCommonUnits = False for commonUnit in multiplyBy: if commonUnit in divideBy: multiplyBy.remove(commonUnit) divideBy.remove(commonUnit) lookForCommonUnits = True break if len(multiplyBy) == 0: # if no units add pure multiplyBy.append(XbrlConst.qnXbrliPure) # dimensions segOCCs = [] scenOCCs = [] if formula.aspectModel == "dimensional": dimAspects = {} dimQnames = aspectValue(xpCtx, formula, Aspect.DIMENSIONS, None) if dimQnames: for dimQname in dimQnames: dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname] dimErr = "xbrlfe:missing{0}DimensionRule".format("typed" if dimConcept and dimConcept.isTypedDimension else "explicit") dimValue = aspectValue(xpCtx, formula, dimQname, dimErr) if isinstance(dimValue, VariableBindingError): xpCtx.modelXbrl.error( _("Formula {0} dimension {1}: {2}").format( formula, dimQname, dimValue.msg ), "err", dimErr) elif dimValue and xpCtx.modelXbrl.qnameDimensionDefaults.get(dimQname) != dimValue: dimAspects[dimQname] = dimValue segOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SEGMENT, None) scenOCCs = aspectValue(xpCtx, formula, Aspect.NON_XDT_SCENARIO, None) else: dimAspects = None # non-dimensional segOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SEGMENT, None) scenOCCs = aspectValue(xpCtx, formula, Aspect.COMPLETE_SCENARIO, None) if priorErrorCount < len(xpCtx.modelXbrl.errors): return None # had errors, don't produce output fact # does context exist in out instance document outputInstanceQname = formula.outputInstanceQname outputXbrlInstance = xpCtx.inScopeVars[outputInstanceQname] xbrlElt = outputXbrlInstance.modelDocument.xmlRootElement # in source instance document # add context prevCntx = outputXbrlInstance.matchContext( entityIdentScheme, entityIdentValue, periodType, periodStart, periodEndInstant, dimAspects, segOCCs, scenOCCs) if prevCntx: cntxId = prevCntx.id newCntxElt = prevCntx.element else: cntxId = 'c-{0:02n}'.format( len(outputXbrlInstance.contexts) + 1) newCntxElt = XmlUtil.addChild(xbrlElt, XbrlConst.xbrli, "context", attributes=("id", cntxId), afterSibling=xpCtx.outputLastContext.get(outputInstanceQname)) xpCtx.outputLastContext[outputInstanceQname] = newCntxElt entityElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "entity") XmlUtil.addChild(entityElt, XbrlConst.xbrli, "identifier", attributes=("scheme", entityIdentScheme), text=entityIdentValue) periodElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "period") if periodType == "forever": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "forever") elif periodType == "instant": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "instant", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) elif periodType == "duration": XmlUtil.addChild(periodElt, XbrlConst.xbrli, "startDate", text=XmlUtil.dateunionValue(periodStart)) XmlUtil.addChild(periodElt, XbrlConst.xbrli, "endDate", text=XmlUtil.dateunionValue(periodEndInstant, subtractOneDay=True)) segmentElt = None scenarioElt = None from arelle.ModelObject import ModelDimensionValue if dimAspects: for dimQname in sorted(dimAspects.keys()): dimValue = dimAspects[dimQname] if isinstance(dimValue, ModelDimensionValue): if dimValue.isExplicit: dimMemberQname = dimValue.memberQname contextEltName = dimValue.contextElement else: # qname for explicit or node for typed dimMemberQname = dimValue contextEltName = xpCtx.modelXbrl.qnameDimensionContextElement.get(dimQname) if contextEltName == "segment": if not segmentElt: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") contextElt = segmentElt elif contextEltName == "scenario": if not scenarioElt: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") contextElt = scenarioElt else: continue dimConcept = xpCtx.modelXbrl.qnameConcepts[dimQname] dimAttr = ("dimension", XmlUtil.addQnameValue(xbrlElt, dimConcept.qname)) if dimConcept.isTypedDimension: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "typedMember", attributes=dimAttr) if isinstance(dimValue, ModelDimensionValue) and dimValue.isTyped: XmlUtil.copyChildren(dimElt, dimValue.typedMember) elif dimMemberQname: dimElt = XmlUtil.addChild(contextElt, XbrlConst.xbrldi, "explicitMember", attributes=dimAttr, text=XmlUtil.addQnameValue(xbrlElt, dimMemberQname)) if segOCCs: if not segmentElt: segmentElt = XmlUtil.addChild(entityElt, XbrlConst.xbrli, "segment") XmlUtil.copyNodes(segmentElt, segOCCs) if scenOCCs: if not scenarioElt: scenarioElt = XmlUtil.addChild(newCntxElt, XbrlConst.xbrli, "scenario") XmlUtil.copyNodes(scenarioElt, scenOCCs) outputXbrlInstance.modelDocument.contextDiscover(newCntxElt) # does unit exist # add unit if modelConcept.isNumeric: prevUnit = outputXbrlInstance.matchUnit(multiplyBy, divideBy) if prevUnit: unitId = prevUnit.id newUnitElt = prevUnit.element else: unitId = 'u-{0:02n}'.format( len(outputXbrlInstance.units) + 1) newUnitElt = XmlUtil.addChild(xbrlElt, XbrlConst.xbrli, "unit", attributes=("id", unitId), afterSibling=xpCtx.outputLastUnit.get(outputInstanceQname)) xpCtx.outputLastUnit[outputInstanceQname] = newUnitElt if len(divideBy) == 0: for multiply in multiplyBy: XmlUtil.addChild(newUnitElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue(xbrlElt, multiply)) else: divElt = XmlUtil.addChild(newUnitElt, XbrlConst.xbrli, "divide") numElt = XmlUtil.addChild(divElt, XbrlConst.xbrli, "unitNumerator") denElt = XmlUtil.addChild(divElt, XbrlConst.xbrli, "unitDenominator") for multiply in multiplyBy: XmlUtil.addChild(numElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue(xbrlElt, multiply)) for divide in divideBy: XmlUtil.addChild(denElt, XbrlConst.xbrli, "measure", text=XmlUtil.addQnameValue(xbrlElt, divide)) outputXbrlInstance.modelDocument.unitDiscover(newUnitElt) # add fact attrs = [("contextRef", cntxId)] precision = None decimals = None if modelConcept.isNumeric: attrs.append(("unitRef", unitId)) value = formula.evaluate(xpCtx) valueSeqLen = len(value) if valueSeqLen > 1: xpCtx.modelXbrl.error( _("Formula {0} value is a sequence of length {1}").format( formula, valueSeqLen ), "err", "xbrlfe:nonSingletonOutputValue") else: if valueSeqLen == 0: #xsi:nil if no value attrs.append((XbrlConst.qnXsiNil, "true")) v = None else: # add precision/decimals for non-fraction numerics if modelConcept.isNumeric and not modelConcept.isFraction: if formula.hasDecimals: decimals = formula.evaluateRule(xpCtx, Aspect.DECIMALS) attrs.append(("decimals", decimals)) else: if formula.hasPrecision: precision = formula.evaluateRule(xpCtx, Aspect.PRECISION) else: precision = 0 attrs.append(("precision", precision)) x = value[0] if isinstance(x,float): from math import (log10, isnan, isinf, fabs) if (isnan(x) or (precision and (isinf(precision) or precision == 0)) or (decimals and isinf(decimals))): v = string(xpCtx, x) elif decimals is not None: v = "%.*f" % ( int(decimals), x) elif precision is not None: a = fabs(x) log = log10(a) if a != 0 else 0 v = "%.*f" % ( int(precision) - int(log) - (1 if a >= 1 else 0), x) else: # no implicit precision yet v = string(xpCtx, x) elif isinstance(x,QName): v = XmlUtil.addQnameValue(xbrlElt, x) elif isinstance(x,datetime.datetime): v = XmlUtil.dateunionValue(x) else: v = string(xpCtx, x) itemElt = XmlUtil.addChild(xbrlElt, conceptQname, attributes=attrs, text=v, afterSibling=xpCtx.outputLastFact.get(outputInstanceQname)) xpCtx.outputLastFact[outputInstanceQname] = itemElt newFact = outputXbrlInstance.modelDocument.factDiscover(itemElt, outputXbrlInstance.facts) return newFact