def attributeDict(modelXbrl, elt, exclusions=set(), equalMode=S_EQUAL, excludeIDs=NO_IDs_EXCLUDED, ns2ns1Tbl=None, keyByTag=False, distinguishNaNs=False): if not hasattr(elt,"xValid"): XmlValidate.validate(modelXbrl, elt) attrs = {} # TBD: replace with validated attributes for modelAttribute in elt.xAttributes.values(): attrTag = modelAttribute.attrTag ns, sep, localName = attrTag.partition('}') attrNsURI = ns[1:] if sep else None if ns2ns1Tbl and attrNsURI in ns2ns1Tbl: attrNsURI = ns2ns1Tbl[attrNsURI] if (attrTag not in exclusions and (attrNsURI is None or attrNsURI not in exclusions)): if keyByTag: qname = attrTag elif attrNsURI is not None: qname = QName(None, attrNsURI, localName) else: qname = QName(None, None, attrTag) try: if excludeIDs and modelAttribute.xValid == XmlValidate.VALID_ID: continue if modelAttribute.xValid != XmlValidate.UNKNOWN: value = modelAttribute.sValue if equalMode <= S_EQUAL2 else modelAttribute.xValue else: # unable to validate, no schema definition, use string value of attribute value = modelAttribute.text if distinguishNaNs and isinstance(value,float) and math.isnan(value): value = (value,elt) attrs[qname] = value except KeyError: pass # what should be done if attribute failed to have psvi value return attrs
def qname(self): try: return self._elementQname except AttributeError: self._elementQname = QName(self.prefix, self.namespaceURI, self.localName) return self._elementQname
def _xbrlTypeMethod(node, sphinxContext, args): concept = conceptArg(node, sphinxContext, args, 0) baseTypeLocalName = concept.baseXbrliType if not baseTypeLocalName: return NONE return QName("{http://www.xbrl.org/2003/instance}xbrli:" + baseTypeLocalName)
def _traverse(self, parent, depth=0, top_name=None): """Traverse a parse tree for qnames :returns: dictionary of set, keyed by qname. The set is the list of rules that use the qname :rtype: dict """ qnames = collections.defaultdict(set) # When the depth is 2, we are at a top level expression (i.e. rule, constant, namespace declaration, function). Get the name of the expression. if depth == 2: top_name = "{}: {}".format( parent['exprName'], parent.get('fullName') or parent.get('constantName') or parent.get('functionName')) if isinstance(parent, dict): if parent.get('exprName') == 'qname': qnames[QName( parent['prefix'] if parent['prefix'] != '*' else None, parent['namespace_uri'], parent['localName'])].add(top_name) #qnames.add(QName(parent['prefix'] if parent['prefix'] != '*' else None, parent['namespace_uri'], parent['localName'])) else: for k, v in parent.items(): # Skip 'arcrole', 'role' and 'drsRole' in navigation if it is an unprefixed qname if not (parent.get('exprName') == 'navigation' and k in ('arcrole', 'role', 'drsRole') and v.get('exprName') == 'qname' and v.get('prefix') == '*'): # Remove .networks property arguments if they are unprefixed qnames if (parent.get('exprName') == 'property' and parent.get('propertyName') == 'networks' and k == 'propertyArgs'): v = [ x for x in v if not (x.get('exprName') == 'qname' and x.get('prefix') == '*') ] new_qnames = self._traverse(v, depth + 1, top_name) qnames = { k: qnames.get(k, set()) | new_qnames.get(k, set()) for k in qnames.keys() | new_qnames.keys() } #qnames |= self._traverse(v, depth+1, top_name) elif isinstance(parent, list): for x in parent: new_qnames = self._traverse(x, depth + 1, top_name) qnames = { k: qnames.get(k, set()) | new_qnames.get(k, set()) for k in qnames.keys() | new_qnames.keys() } #qnames |= self._traverse(x, depth+1, top_name) return qnames
def convert_file_data_item(item, type, xule_context): if type is None: return xv.XuleValue(xule_context, item, 'string') elif type == 'qname': if item.count(':') == 0: prefix = '*' # This indicates the default namespace local_name = item elif item.count(':') == 1: prefix, local_name = item.split(':') else: raise XuleProcessingError( _("While processing a data file, QName in a file can only have one ':', found {} ':'s" .format(item.count(':'))), xule_context) namespace = xule_context.rule_set.getNamespaceUri(prefix) return xv.XuleValue( xule_context, QName(prefix if prefix != '*' else None, namespace, local_name), 'qname') elif type == 'int': try: return xv.XuleValue(xule_context, int(item), 'int') except ValueError: raise XuleProcessingError( _("While processing a data file, cannot convert '{}' to an {}." .format(item, type)), xule_context) elif type == 'float': try: return xv.XuleValue(xule_context, float(item), 'float') except ValueError: raise XuleProcessingError( _("While processing a data file, cannot convert '{}' to a {}.". format(item, type)), xule_context) elif type == 'decimal': try: return xv.XuleValue(xule_context, decimal.Decimal(item), 'decimal') except decimal.InvalidOperation: raise XuleProcessingError( _("While processing a data file, cannot convert '{}' to a {}.". format(item, type)), xule_context) elif type == 'string': return xv.XuleValue(xule_context, item, type) else: raise XuleProcessingError( _("While processing a data file, {} is not implemented.".format( type)), xule_context)
def func_qname(xule_context, *args): namespace_uri = args[0] local_name = args[1] if namespace_uri.type not in ('string', 'uri', 'unbound', 'none'): raise XuleProcessingError( _("Function 'qname' requires the namespace_uri argument to be a string, uri or none, found '%s'" % namespace_uri.type), xule_context) if local_name.type != 'string': raise XuleProcessingError( _("Function 'qname' requires the local_part argument to be a string, found '%s'" % local_name.type), xule_context) if namespace_uri.type == 'unbound': return xv.XuleValue( xule_context, qname(local_name.value, noPrefixIsNoNamespace=True), 'qname') else: '''INSTEAD OF PASSING None FOR THE PREFIX, THIS SHOULD FIND THE PREFIX FOR THE NAMESPACE URI FROM THE RULE FILE. IF IT CANNOT FIND ONE, IT SHOULD CREATE ONE.''' return xv.XuleValue(xule_context, QName(None, namespace_uri.value, local_name.value), 'qname')
def func_qname(xule_context, *args): namespace_uri_arg = args[0] local_name_arg = args[1] if namespace_uri_arg.type not in ('string', 'uri', 'unbound', 'none'): raise XuleProcessingError( _("Function 'qname' requires the namespace_uri argument to be a string, uri or none, found '%s'" % namespace_uri_arg.type), xule_context) if local_name_arg.type != 'string': raise XuleProcessingError( _("Function 'qname' requires the local_part argument to be a string, found '%s'" % local_name_arg.type), xule_context) if namespace_uri_arg.type == 'unbound': return xv.XuleValue( xule_context, qname(local_name_arg.value, noPrefixIsNoNamespace=True), 'qname') else: # get the prefix from the rule file prefix = get_prefix(xule_context, namespace_uri_arg.value) return xv.XuleValue( xule_context, QName(prefix, namespace_uri_arg.value, local_name_arg.value), 'qname')
def _prep_roll_forward_constants(xule_context): xule_context.roll_forward = dict() us_gaap_ns = [ ns for ns in xule_context.model.namespaceDocs if re.match(r'(http://fasb.org/|http://xbrl.us/)us-gaap/', ns) ] if len(us_gaap_ns) == 0: raise XuleProcessingError(_("US GAAP taxonomy is not found."), xule_context) if len(us_gaap_ns) > 1: raise XuleProcessingError( _("Found more than one version for the US GAAP taxonomy: %s" % ", ".join(us_gaap_ns)), xule_context) xule_context.roll_forward['US_GAAP_NS'] = us_gaap_ns = us_gaap_ns[0] xule_context.roll_forward['INCOME_STATEMENT_LOCATION_AXIS'] = QName( None, xule_context.roll_forward['US_GAAP_NS'], 'IncomeStatementLocationAxis') xule_context.roll_forward['ROLL_FORWARD_EXCEPTIONS'] = \ {QName(None, xule_context.roll_forward['US_GAAP_NS'], 'ShareBasedCompensationArrangementByShareBasedPaymentAwardEquityInstrumentsOtherThanOptionsNonvestedWeightedAverageGrantDateFairValue') ,QName(None, xule_context.roll_forward['US_GAAP_NS'], 'ShareBasedCompensationArrangementByShareBasedPaymentAwardOptionsOutstandingWeightedAverageExercisePrice') ,QName(None, xule_context.roll_forward['US_GAAP_NS'], 'SharebasedCompensationArrangementBySharebasedPaymentAwardOptionsNonvestedWeightedAverageGrantDateFairValue') ,QName(None, xule_context.roll_forward['US_GAAP_NS'], 'PartnersCapital') ,QName(None, xule_context.roll_forward['US_GAAP_NS'], 'PartnersCapitalAccountUnits')} xule_context.roll_forward['TOTAL_LABELS'] = ( 'http://www.xbrl.org/2003/role/totalLabel', 'http://www.xbrl.org/2009/role/negatedTotalLabel') xule_context.roll_forward['BASE_RF_CALCS'] = None xule_context.roll_forward['BASE_PERIOD_TOTALS'] = None xule_context.roll_forward['EXT_DEFAULTS'] = None xule_context.roll_forward['CALC_EXCEPTIONS'] = \ {QName(None, xule_context.roll_forward['US_GAAP_NS'], 'AllowanceForLoanAndLeaseLossesPeriodIncreaseDecrease'):{QName(None, xule_context.roll_forward['US_GAAP_NS'], 'ProvisionForLoanAndLeaseLosses'): 1, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'AllowanceForLoanAndLeaseLossesRecoveriesOfBadDebts'):1}, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'DefinedBenefitPlanBenefitObligationPeriodIncreaseDecrease'):{QName(None, xule_context.roll_forward['US_GAAP_NS'], 'DefinedBenefitPlanForeignCurrencyExchangeRateChangesBenefitObligation'):-1, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'DefinedBenefitPlanActuarialGainLoss'):-1, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'DefinedBenefitPlanOtherChanges'):1}, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'FiniteLivedIntangibleAssetsPeriodIncreaseDecrease'):{QName(None, xule_context.roll_forward['US_GAAP_NS'], 'AmortizationOfIntangibleAssets'):-1, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'ImpairmentOfIntangibleAssetsFinitelived'):-1}, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'RestructuringReservePeriodIncreaseDecrease'):{QName(None, xule_context.roll_forward['US_GAAP_NS'], 'RestructuringReserveSettledWithCash'):-1, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'RestructuringReserveSettledWithoutCash'):-1}, QName(None, xule_context.roll_forward['US_GAAP_NS'], 'ShareBasedCompensationArrangementByShareBasedPaymentAwardOptionsOutstandingPeriodIncreaseDecrease'):{QName(None, xule_context.roll_forward['US_GAAP_NS'], 'ShareBasedCompensationArrangementByShareBasedPaymentAwardOptionsGrantsInPeriod'):1} } xule_context.roll_forward['EFFECTIVE_WEIGHTS'] = dict() xule_context.roll_forward['HAS_CALC'] = dict() xule_context.roll_forward['LINE_ITEM_CUBE'] = None
def createViewer(self, scriptUrl="js/dist/ixbrlviewer.js"): """ Create an iXBRL file with XBRL data as a JSON blob, and script tags added """ dts = self.dts iv = iXBRLViewer(dts) idGen = 0 self.roleMap.getPrefix(XbrlConst.standardLabel, "std") self.roleMap.getPrefix(XbrlConst.documentationLabel, "doc") self.roleMap.getPrefix(XbrlConst.summationItem, "calc") self.roleMap.getPrefix(XbrlConst.parentChild, "pres") for f in dts.facts: if f.id is None: f.set("id", "ixv-%d" % (idGen)) idGen += 1 conceptName = self.nsmap.qname(f.qname) scheme, ident = f.context.entityIdentifier aspects = { "c": conceptName, "e": self.nsmap.qname( QName(self.nsmap.getPrefix(scheme, "e"), scheme, ident)), } factData = { "v": f.value, "a": aspects, } if f.format is not None: factData["f"] = str(f.format) if f.isNumeric: if f.unit is not None: # XXX does not support complex units unit = self.nsmap.qname(f.unit.measures[0][0]) aspects["u"] = unit else: # The presence of the unit aspect is used by the viewer to # identify numeric facts. If the fact has no unit (invalid # XBRL, but we want to support it for draft documents), # include the unit aspect with a null value. aspects["u"] = None d = inferredDecimals(f) if d != float("INF") and not math.isnan(d): factData["d"] = d for d, v in f.context.qnameDims.items(): if v.memberQname is None: # Typed dimension, not yet supported. continue aspects[self.nsmap.qname(v.dimensionQname)] = self.nsmap.qname( v.memberQname) self.addConcept(v.dimension) self.addConcept(v.member) if f.context.isForeverPeriod: aspects["p"] = "f" elif f.context.isInstantPeriod: aspects["p"] = self.dateFormat( f.context.instantDatetime.isoformat()) elif f.context.isStartEndPeriod: aspects["p"] = "%s/%s" % ( self.dateFormat(f.context.startDatetime.isoformat()), self.dateFormat(f.context.endDatetime.isoformat())) self.taxonomyData["facts"][f.id] = factData self.addConcept(f.concept) self.taxonomyData["prefixes"] = self.nsmap.prefixmap self.taxonomyData["roles"] = self.roleMap.prefixmap self.taxonomyData["rels"] = self.getRelationships() dts.info("viewer:info", "Creating iXBRL viewer") if dts.modelDocument.type == Type.INLINEXBRLDOCUMENTSET: # Sort by object index to preserve order in which files were specified. docSet = sorted(dts.modelDocument.referencesDocument.keys(), key=lambda x: x.objectIndex) docSetFiles = list( map(lambda x: os.path.basename(x.filepath), docSet)) self.taxonomyData["docSetFiles"] = docSetFiles for n in range(0, len(docSet)): iv.addFile( iXBRLViewerFile(docSetFiles[n], docSet[n].xmlDocument)) xmlDocument = docSet[0].xmlDocument else: xmlDocument = dts.modelDocument.xmlDocument filename = os.path.basename(dts.modelDocument.filepath) iv.addFile(iXBRLViewerFile(filename, xmlDocument)) taxonomyDataJSON = self.escapeJSONForScriptTag( json.dumps(self.taxonomyData, indent=1, allow_nan=False)) for child in xmlDocument.getroot(): if child.tag == '{http://www.w3.org/1999/xhtml}body': child.append(etree.Comment("BEGIN IXBRL VIEWER EXTENSIONS")) e = etree.fromstring( "<script xmlns='http://www.w3.org/1999/xhtml' src='%s' type='text/javascript' />" % scriptUrl) # Don't self close e.text = '' child.append(e) # Putting this in the header can interfere with character set # auto detection e = etree.fromstring( "<script xmlns='http://www.w3.org/1999/xhtml' type='application/x.ixbrl-viewer+json'></script>" ) e.text = taxonomyDataJSON child.append(e) child.append(etree.Comment("END IXBRL VIEWER EXTENSIONS")) break return iv
def createViewer(self, scriptUrl="js/dist/ixbrlviewer.js"): """ Create an iXBRL file with XBRL data as a JSON blob, and script tags added """ dts = self.dts idGen = 0 self.roleMap.getPrefix(XbrlConst.standardLabel,"std") self.roleMap.getPrefix(XbrlConst.documentationLabel,"doc") self.roleMap.getPrefix(XbrlConst.summationItem,"calc") self.roleMap.getPrefix(XbrlConst.parentChild,"pres") for f in dts.facts: if f.id is None: f.set("id","ixv-%d" % (idGen)) idGen += 1 conceptName = self.nsmap.qname(f.qname) scheme, ident = f.context.entityIdentifier aspects = { "c": conceptName, "e": self.nsmap.qname(QName(self.nsmap.getPrefix(scheme,"e"), scheme, ident)), } factData = { "v": f.value, "a": aspects, } if f.format is not None: factData["f"] = str(f.format) if f.isNumeric: if f.unit: # XXX does not support complex units unit = self.nsmap.qname(f.unit.measures[0][0]) aspects["u"] = unit d = inferredDecimals(f) if d != float("INF") and not math.isnan(d): factData["d"] = d for d, v in f.context.qnameDims.items(): if v.memberQname is None: # Typed dimension, not yet supported. continue aspects[self.nsmap.qname(v.dimensionQname)] = self.nsmap.qname(v.memberQname) self.addConcept(v.dimension) self.addConcept(v.member) # XXX does not support forever periods if f.context.isInstantPeriod: aspects["p"] = self.dateFormat(f.context.instantDatetime.isoformat()) elif f.context.isStartEndPeriod: aspects["p"] = "%s/%s" % ( self.dateFormat(f.context.startDatetime.isoformat()), self.dateFormat(f.context.endDatetime.isoformat()) ) self.taxonomyData["facts"][f.id] = factData self.addConcept(f.concept) self.taxonomyData["prefixes"] = self.nsmap.prefixmap self.taxonomyData["roles"] = self.roleMap.prefixmap self.taxonomyData["rels"] = self.getRelationnShips() taxonomyDataJSON = self.escapeJSONForScriptTag(json.dumps(self.taxonomyData, indent=1, allow_nan=False)) dts.info("viewer:info", "Creating iXBRL viewer") for child in dts.modelDocument.xmlDocument.getroot(): if child.tag == '{http://www.w3.org/1999/xhtml}body': child.append(etree.Comment("BEGIN IXBRL VIEWER EXTENSIONS")) e = etree.fromstring("<script xmlns='http://www.w3.org/1999/xhtml' src='%s' />" % scriptUrl) e.text = '' child.append(e) # Putting this in the header can interfere with character set # auto detection e = etree.fromstring("<script xmlns='http://www.w3.org/1999/xhtml' id='taxonomy-data' type='application/json'></script>") e.text = taxonomyDataJSON child.append(e) child.append(etree.Comment("END IXBRL VIEWER EXTENSIONS")) break return dts.modelDocument.xmlDocument
def addFact(self, f): if f.id is None: f.set("id", "ixv-%d" % (self.idGen)) self.idGen += 1 conceptName = self.nsmap.qname(f.qname) scheme, ident = f.context.entityIdentifier aspects = { "c": conceptName, "e": self.nsmap.qname( QName(self.nsmap.getPrefix(scheme, "e"), scheme, ident)), } factData = { "a": aspects, } if f.isNil: factData["v"] = None elif f.concept is not None and f.concept.isEnumeration: qnEnums = f.xValue if not isinstance(qnEnums, list): qnEnums = (qnEnums, ) factData["v"] = " ".join(self.nsmap.qname(qn) for qn in qnEnums) for qn in qnEnums: self.addConcept(self.dts.qnameConcepts.get(qn)) else: factData["v"] = f.value if f.value == INVALIDixVALUE: factData["err"] = 'INVALID_IX_VALUE' if f.format is not None: factData["f"] = str(f.format) if f.isNumeric: if f.unit is not None and len(f.unit.measures[0]): # XXX does not support complex units unit = self.nsmap.qname(f.unit.measures[0][0]) aspects["u"] = unit else: # The presence of the unit aspect is used by the viewer to # identify numeric facts. If the fact has no unit (invalid # XBRL, but we want to support it for draft documents), # include the unit aspect with a null value. aspects["u"] = None d = inferredDecimals(f) if d != float("INF") and not math.isnan(d): factData["d"] = d for d, v in f.context.qnameDims.items(): if v.memberQname is not None: aspects[self.nsmap.qname(v.dimensionQname)] = self.nsmap.qname( v.memberQname) self.addConcept(v.member) self.addConcept(v.dimension, dimensionType="e") elif v.typedMember is not None: aspects[self.nsmap.qname( v.dimensionQname)] = v.typedMember.text self.addConcept(v.dimension, dimensionType="t") if f.context.isForeverPeriod: aspects["p"] = "f" elif f.context.isInstantPeriod and f.context.instantDatetime is not None: aspects["p"] = self.dateFormat( f.context.instantDatetime.isoformat()) elif f.context.isStartEndPeriod and f.context.startDatetime is not None and f.context.endDatetime is not None: aspects["p"] = "%s/%s" % ( self.dateFormat(f.context.startDatetime.isoformat()), self.dateFormat(f.context.endDatetime.isoformat())) frels = self.footnoteRelationshipSet.fromModelObject(f) if frels: for frel in frels: if frel.toModelObject is not None: factData.setdefault("fn", []).append(frel.toModelObject.id) self.taxonomyData["facts"][f.id] = factData self.addConcept(f.concept)