def __init__(self, modelXbrl, tabWin, arcrole, linkrole=None, linkqname=None, arcqname=None, lang=None, treeColHdr=None): super().__init__(modelXbrl, tabWin, XbrlConst.baseSetArcroleLabel(arcrole)[1:], True, lang) self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname self.treeColHdr = treeColHdr
def insertResources(self): self.showStatus("insert resources") table = self.getTable('resource', 'resource_id', ('role_uri_id', 'qname_id', 'document_id', 'document_line_number', 'document_column_number'), ('role_uri_id', 'qname_id', 'document_id', 'document_line_number', 'document_column_number'), tuple((self.uriId[resource.role], self.qnameId[resource.qname], self.documentIds[resource.modelDocument.uri], resource.sourceline, 0) for arcrole in (XbrlConst.conceptLabel, XbrlConst.conceptReference) for rel in self.modelXbrl.relationshipSet(arcrole).modelRelationships for resource in (rel.fromModelObject, rel.toModelObject) if isinstance(resource, ModelResource))) self.resourceId = dict(((roleId, qnId, docId, line, offset), id) for id, roleId, qnId, docId, line, offset in table) self.showStatus("insert labels") table = self.getTable('label_resource', 'resource_id', ('resource_id', 'label', 'xml_lang'), ('resource_id',), tuple((self.resourceId[self.uriId[resource.role], self.qnameId[resource.qname], self.documentIds[resource.modelDocument.uri], resource.sourceline, 0], resource.elementText, resource.xmlLang) for arcrole in (XbrlConst.conceptLabel, XbrlConst.conceptReference) for rel in self.modelXbrl.relationshipSet(arcrole).modelRelationships for resource in (rel.fromModelObject, rel.toModelObject) if isinstance(resource, ModelResource) and XbrlConst.isLabelRole(resource.role)))
def __init__(self, modelXbrl, tabWin, arcrole, linkrole=None, linkqname=None, arcqname=None, lang=None, treeColHdr=None, showLinkroles=True, showRelationships=True, showColumns=True, expandAll=False, hasTableIndex=False): self.isEbaTableIndex = False if isinstance(arcrole, (list, tuple)): tabName = arcrole[0] else: tabName = XbrlConst.baseSetArcroleLabel(arcrole)[1:] tabName = tabName + " (" + modelXbrl.getInstanceFilenameForView() + ")" super(ViewRelationshipSet, self).__init__(modelXbrl, tabWin, tabName, True, lang) self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname self.treeColHdr = treeColHdr self.showLinkroles = showLinkroles self.showRelationships = showRelationships self.showColumns = showColumns self.expandAllOnFirstDisplay = expandAll self.hasTableIndex = hasTableIndex self.isResourceArcrole = False
def ebaDecimals(locale, value, concept, defaultDecimals): ''' :type locale: dict :type value: string :type concept: ModelConcept :type defaultDecimals: str :rtype (boolean, str) ''' isPercent = concept.typeQname == qnPercentItemType isInteger = XbrlConst.isIntegerXsdType(concept.type.baseXsdType) isMonetary = concept.isMonetary decimalsFound, decimals = decimalsComputer(locale, value, concept, defaultDecimals) if not(decimalsFound) or decimals == 'INF': return (decimalsFound, decimals) else: # the default values are for non-monetary items lowerBound = -20 upperBound = 20 decimalsAsInteger = int(decimals) if isMonetary: lowerBound = -3 elif isInteger: lowerBound = upperBound = 0 elif isPercent: # percent values lowerBound = 4 upperBound = 20 if decimalsAsInteger<lowerBound: decimals = str(lowerBound) decimalsAsInteger = lowerBound if decimalsAsInteger>upperBound: decimals = 'INF' # approximation decimalsAsInteger = upperBound return (decimalsFound, decimals)
def __init__( self, modelXbrl, tabWin, arcrole, linkrole=None, linkqname=None, arcqname=None, lang=None, treeColHdr=None, showLinkroles=True, showRelationships=True, showColumns=True, expandAll=False, hasTableIndex=False, ): if isinstance(arcrole, (list, tuple)): tabName = arcrole[0] else: tabName = XbrlConst.baseSetArcroleLabel(arcrole)[1:] super(ViewRelationshipSet, self).__init__(modelXbrl, tabWin, tabName, True, lang) self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname self.treeColHdr = treeColHdr self.showLinkroles = showLinkroles self.showRelationships = showRelationships self.showColumns = showColumns self.expandAllOnFirstDisplay = expandAll self.hasTableIndex = hasTableIndex self.isResourceArcrole = False
def view(self, arcrole, linkrole=None, linkqname=None, arcqname=None): # determine relationships indent depth for dimensions linkbases # set up treeView widget and tabbed pane if arcrole == XbrlConst.parentChild: # extra columns heading = ["Presentation Relationships", "Pref. Label", "Type", "References"] elif arcrole == XbrlConst.summationItem: # add columns for calculation relationships heading = ["Calculation Relationships", "Weight", "Balance"] elif arcrole == "XBRL-dimensions": # add columns for dimensional information heading = ["Dimensions Relationships", "Arcrole","CntxElt","Closed","Usable"] elif arcrole == "Table-rendering": heading = ["Table Relationships", "Axis", "Abs", "Mrg", "Header", "Primary Item", "Dimensions"] elif isinstance(arcrole, (list,tuple)) or XbrlConst.isResourceArcrole(arcrole): self.isResourceArcrole = True self.showReferences = isinstance(arcrole, _STR_BASE) and arcrole.endswith("-reference") heading = ["Resource Relationships", "Arcrole","Resource","ResourceRole","Language"] else: heading = [os.path.basename(arcrole).title() + " Relationships"] # relationship set based on linkrole parameter, to determine applicable linkroles relationshipSet = self.modelXbrl.relationshipSet(arcrole, linkrole, linkqname, arcqname) self.arcrole = arcrole self.maxNumDims = 1 if relationshipSet: # sort URIs by definition linkroleUris = [] for linkroleUri in relationshipSet.linkRoleUris: modelRoleTypes = self.modelXbrl.roleTypes.get(linkroleUri) if modelRoleTypes: roledefinition = (modelRoleTypes[0].genLabel(lang=self.lang, strip=True) or modelRoleTypes[0].definition or linkroleUri) else: roledefinition = linkroleUri linkroleUris.append((roledefinition, linkroleUri)) linkroleUris.sort() for roledefinition, linkroleUri in linkroleUris: linkRelationshipSet = self.modelXbrl.relationshipSet(arcrole, linkroleUri, linkqname, arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.treeDepth(rootConcept, rootConcept, 2, arcrole, linkRelationshipSet, set()) # avoid use of lastColSpan, html and excel do not use multiple cols for dimensions for now #if "Dimensions" == heading[-1]: # lastColSpan = self.maxNumDims #else: lastColSpan = None self.addRow(heading, asHeader=True, lastColSpan=lastColSpan) # must do after determining tree depth self.setColWidths([COL_WIDTHS.get(hdg, 80 if hdg.endswith(" Relationships") else 8) for hdg in heading]) if relationshipSet: # for each URI in definition order for roledefinition, linkroleUri in linkroleUris: #Deepak-Khopade-isdr: 15-Feb-2017 added "roleDefinition" to see it on presentation tree attr = {"roleUri": linkroleUri, "roleDefinition": roledefinition} self.addRow([roledefinition], treeIndent=0, colSpan=len(heading), xmlRowElementName="linkRole", xmlRowEltAttr=attr, xmlCol0skipElt=True) linkRelationshipSet = self.modelXbrl.relationshipSet(arcrole, linkroleUri, linkqname, arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.viewConcept(rootConcept, rootConcept, "", self.labelrole, 1, arcrole, linkRelationshipSet, set())
def labelroles(modelXbrl, includeConceptName=False): # returns sorted list of tuples of arcrole basename and uri return sorted( set( (XbrlConst.labelroleLabel(r), r) for r in (modelXbrl.labelroles | ({XbrlConst.conceptNameLabelRole} if includeConceptName else set())) ) )
def __init__(self, modelXbrl, tabWin, arcrole, linkrole=None, linkqname=None, arcqname=None, lang=None, treeColHdr=None): if isinstance(arcrole, (list,tuple)): tabName = arcrole[0] else: tabName = XbrlConst.baseSetArcroleLabel(arcrole)[1:] super(ViewRelationshipSet, self).__init__(modelXbrl, tabWin, tabName, True, lang) self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname self.treeColHdr = treeColHdr self.isResourceArcrole = False
def view(self, arcrole, linkrole=None, linkqname=None, arcqname=None): # determine relationships indent depth for dimensions linkbases # set up treeView widget and tabbed pane if arcrole == XbrlConst.parentChild: # extra columns heading = ["Presentation Relationships", "Pref. Label", "Type", "References"] elif arcrole == XbrlConst.summationItem: # add columns for calculation relationships heading = ["Calculation Relationships", "Weight", "Balance"] elif arcrole == "XBRL-dimensions": # add columns for dimensional information heading = ["Dimensions Relationships", "Arcrole","CntxElt","Closed","Usable"] elif isinstance(arcrole, (list,tuple)) or XbrlConst.isResourceArcrole(arcrole): self.isResourceArcrole = True self.showReferences = isinstance(arcrole, _STR_BASE) and arcrole.endswith("-reference") heading = ["Resource Relationships", "Arcrole","Resource","ResourceRole","Language"] else: heading = [os.path.basename(arcrole).title() + " Relationships"] # relationship set based on linkrole parameter, to determine applicable linkroles relationshipSet = self.modelXbrl.relationshipSet(arcrole, linkrole, linkqname, arcqname) self.arcrole = arcrole if relationshipSet: # sort URIs by definition linkroleUris = [] for linkroleUri in relationshipSet.linkRoleUris: modelRoleTypes = self.modelXbrl.roleTypes.get(linkroleUri) if modelRoleTypes: roledefinition = (modelRoleTypes[0].genLabel(lang=self.lang, strip=True) or modelRoleTypes[0].definition or linkroleUri) else: roledefinition = linkroleUri linkroleUris.append((roledefinition, linkroleUri)) linkroleUris.sort() for roledefinition, linkroleUri in linkroleUris: linkRelationshipSet = self.modelXbrl.relationshipSet(arcrole, linkroleUri, linkqname, arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.treeDepth(rootConcept, rootConcept, 2, arcrole, linkRelationshipSet, set()) self.addRow(heading, asHeader=True) # must do after determining tree depth if relationshipSet: # for each URI in definition order for roledefinition, linkroleUri in linkroleUris: attr = {"role": linkroleUri} self.addRow([roledefinition], treeIndent=0, colSpan=len(heading), xmlRowElementName="linkRole", xmlRowEltAttr=attr, xmlCol0skipElt=True) linkRelationshipSet = self.modelXbrl.relationshipSet(arcrole, linkroleUri, linkqname, arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.viewConcept(rootConcept, rootConcept, "", self.labelrole, 1, arcrole, linkRelationshipSet, set())
def __init__(self, modelXbrl, tabWin, arcrole, linkrole=None, linkqname=None, arcqname=None, lang=None, treeColHdr=None, showLinkroles=True, showRelationships=True, showColumns=True, expandAll=False): if isinstance(arcrole, (list,tuple)): tabName = arcrole[0] else: tabName = XbrlConst.baseSetArcroleLabel(arcrole)[1:] super(ViewRelationshipSet, self).__init__(modelXbrl, tabWin, tabName, True, lang) self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname self.treeColHdr = treeColHdr self.showLinkroles = showLinkroles self.showRelationships = showRelationships self.showColumns = showColumns self.expandAllOnFirstDisplay = expandAll self.isResourceArcrole = False
def insertResources(self): self.showStatus("insert resources") # deduplicate resources (may be on multiple arcs) # note that lxml has no column numbers, use objectIndex as pseudo-column number uniqueResources = dict(((self.documentIds[resource.modelDocument.uri], resource.sourceline, resource.objectIndex), resource) for arcrole in (XbrlConst.conceptLabel, XbrlConst.conceptReference) for rel in self.modelXbrl.relationshipSet(arcrole).modelRelationships if rel.fromModelObject is not None and rel.toModelObject is not None for resource in (rel.fromModelObject, rel.toModelObject) if isinstance(resource, ModelResource)) table = self.getTable('resource', 'resource_id', ('role_uri_id', 'qname_id', 'document_id', 'document_line_number', 'document_column_number'), ('document_id', 'document_line_number', 'document_column_number'), tuple((self.uriId[resource.role], self.qnameId[resource.qname], self.documentIds[resource.modelDocument.uri], resource.sourceline, resource.objectIndex) for resource in uniqueResources.values()), checkIfExisting=True) self.resourceId = dict(((docId, line, offset), id) for id, docId, line, offset in table) self.showStatus("insert labels") table = self.getTable('label_resource', 'resource_id', ('resource_id', 'label', 'xml_lang'), ('resource_id',), tuple((self.resourceId[self.documentIds[resource.modelDocument.uri], resource.sourceline, resource.objectIndex], resource.textValue, resource.xmlLang) for arcrole in (XbrlConst.conceptLabel, XbrlConst.conceptReference) for rel in self.modelXbrl.relationshipSet(arcrole).modelRelationships if rel.fromModelObject is not None and rel.toModelObject is not None for resource in (rel.fromModelObject, rel.toModelObject) if isinstance(resource, ModelResource) and XbrlConst.isLabelRole(resource.role)), checkIfExisting=True)
def validate(self, modelXbrl, parameters=None): self.parameters = parameters self.NCnamePattern = re.compile( "^[_A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]" r"[_\-\." "\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*$" ) self.precisionPattern = re.compile("^([0-9]+|INF)$") self.decimalsPattern = re.compile("^(-?[0-9]+|INF)$") self.isoCurrencyPattern = re.compile(r"^[A-Z]{3}$") self.modelXbrl = modelXbrl self.validateDisclosureSystem = modelXbrl.modelManager.validateDisclosureSystem self.disclosureSystem = modelXbrl.modelManager.disclosureSystem self.validateEFM = self.validateDisclosureSystem and self.disclosureSystem.EFM self.validateGFM = self.validateDisclosureSystem and self.disclosureSystem.GFM self.validateEFMorGFM = self.validateDisclosureSystem and self.disclosureSystem.EFMorGFM self.validateHMRC = self.validateDisclosureSystem and self.disclosureSystem.HMRC self.validateSBRNL = self.validateDisclosureSystem and self.disclosureSystem.SBRNL self.validateXmlLang = self.validateDisclosureSystem and self.disclosureSystem.xmlLangPattern self.validateCalcLB = modelXbrl.modelManager.validateCalcLB self.validateInferDecimals = modelXbrl.modelManager.validateInferDecimals # xlink validation modelXbrl.modelManager.showStatus(_("validating links")) modelLinks = set() self.remoteResourceLocElements = set() self.genericArcArcroles = set() for baseSetExtLinks in modelXbrl.baseSets.values(): for baseSetExtLink in baseSetExtLinks: modelLinks.add( baseSetExtLink) # ext links are unique (no dups) for modelLink in modelLinks: fromToArcs = {} locLabels = {} resourceLabels = {} resourceArcTos = [] for arcElt in modelLink.element.childNodes: if arcElt.nodeType == 1: xlinkType = arcElt.getAttributeNS(XbrlConst.xlink, "type") # locator must have an href if xlinkType == "locator": if not arcElt.hasAttributeNS(XbrlConst.xlink, "href"): modelXbrl.error( _("Linkbase {0} extended link {1} locator {2} missing href" ).format( modelLink.modelDocument.basename, modelLink.role, arcElt.hasAttributeNS( XbrlConst.xlink, "label")), "err", "xlink:locatorHref") locLabels[arcElt.getAttributeNS( XbrlConst.xlink, "label")] = arcElt elif xlinkType == "resource": resourceLabels[arcElt.getAttributeNS( XbrlConst.xlink, "label")] = arcElt # can be no duplicated arcs between same from and to elif xlinkType == "arc": fromLabel = arcElt.getAttributeNS( XbrlConst.xlink, "from") toLabel = arcElt.getAttributeNS(XbrlConst.xlink, "to") fromTo = (fromLabel, toLabel) if fromTo in fromToArcs: modelXbrl.error( _("Linkbase {0} extended link {1} duplicate arcs from {2} to {3}" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel), "err", "xlink:dupArcs") else: fromToArcs[fromTo] = arcElt if arcElt.namespaceURI == XbrlConst.link: if arcElt.localName in arcNamesTo21Resource: #("labelArc","referenceArc"): resourceArcTos.append( (toLabel, arcElt.getAttribute("use"))) elif self.isGenericArc(arcElt): arcrole = arcElt.getAttributeNS( XbrlConst.xlink, "arcrole") self.genericArcArcroles.add(arcrole) if arcrole in (XbrlConst.elementLabel, XbrlConst.elementReference): resourceArcTos.append((toLabel, arcrole)) # values of type (not needed for validating parsers) if xlinkType not in xlinkTypeValues: # ("", "simple", "extended", "locator", "arc", "resource", "title", "none"): modelXbrl.error( _("Linkbase {0} extended link {1} type {2} invalid" ).format(modelLink.modelDocument.basename, modelLink.role, xlinkType), "err", "xlink:type") # values of actuate (not needed for validating parsers) xlinkActuate = arcElt.getAttributeNS( XbrlConst.xlink, "actuate") if xlinkActuate not in xlinkActuateValues: # ("", "onLoad", "onRequest", "other", "none"): modelXbrl.error( _("Linkbase {0} extended link {1} actuate {2} invalid" ).format(modelLink.modelDocument.basename, modelLink.role, xlinkActuate), "err", "xlink:actuate") # values of show (not needed for validating parsers) xlinkShow = arcElt.getAttributeNS(XbrlConst.xlink, "show") if xlinkShow not in xlinkShowValues: # ("", "new", "replace", "embed", "other", "none"): modelXbrl.error( _("Linkbase {0} extended link {1} show {2} invalid" ).format(modelLink.modelDocument.basename, modelLink.role, xlinkShow), "err", "xlink:show") # values of label, from, to (not needed for validating parsers) for name in xlinkLabelAttributes: # ("label", "from", "to"): value = arcElt.getAttributeNS(XbrlConst.xlink, name) if value != "" and not self.NCnamePattern.match(value): modelXbrl.error( _("Linkbase {0} extended link {1} element {2} {3} '{4}' not an NCname" ).format(modelLink.modelDocument.basename, modelLink.role, arcElt.tagName, name, value), "err", "xlink:{0}".format(name)) # check from, to of arcs have a resource or loc for fromTo, arcElt in fromToArcs.items(): fromLabel, toLabel in fromTo for name, value, sect in (("from", fromLabel, "3.5.3.9.2"), ("to", toLabel, "3.5.3.9.3")): if value not in locLabels and value not in resourceLabels: modelXbrl.error( _("Arc in linkbase {0} extended link {1} from {2} to {3} attribute \"{4}\" has no matching loc or resource label" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel, name), "err", "xbrl.{0}:arcResource".format(sect)) if arcElt.localName == "footnoteArc" and arcElt.namespaceURI == XbrlConst.link and \ arcElt.getAttributeNS(XbrlConst.xlink,"arcrole") == XbrlConst.factFootnote: if fromLabel not in locLabels: modelXbrl.error( _("FootnoteArc in {0} extended link {1} from {2} to {3} \"from\" is not a loc" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel), "err", "xbrl.4.11.1.3.1:factFootnoteArcFrom") if toLabel not in resourceLabels or qname( resourceLabels[toLabel] ) != XbrlConst.qnLinkFootnote: modelXbrl.error( _("FootnoteArc in {0} extended link {1} from {2} to {3} \"to\" is not a footnote resource" ).format(modelLink.modelDocument.basename, modelLink.role, fromLabel, toLabel), "err", "xbrl.4.11.1.3.1:factFootnoteArcTo") # check unprohibited label arcs to remote locs for resourceArcTo in resourceArcTos: resourceArcToLabel, resourceArcUse = resourceArcTo if resourceArcToLabel in locLabels: toLabel = locLabels[resourceArcToLabel] if resourceArcUse == "prohibited": self.remoteResourceLocElements.add(toLabel) else: modelXbrl.error( _("Unprohibited labelArc in linkbase {0} extended link {1} has illegal remote resource loc labeled {2} href {3}" ).format( modelLink.modelDocument.basename, modelLink.role, resourceArcToLabel, toLabel.getAttributeNS( XbrlConst.xlink, "href")), "err", "xbrl.5.2.2.3:labelArcRemoteResource") elif resourceArcToLabel in resourceLabels: toResource = resourceLabels[resourceArcToLabel] if resourceArcUse == XbrlConst.elementLabel: if not self.isGenericLabel(toResource): modelXbrl.error( _("Generic label arc in linkbase {0} extended link {1} to {2} must target a generic label" ).format(modelLink.modelDocument.basename, modelLink.role, resourceArcToLabel), "err", "xbrlle.2.1.1:genericLabelTarget") elif resourceArcUse == XbrlConst.elementReference: if not self.isGenericReference(toResource): modelXbrl.error( _("Generic reference arc in linkbase {0} extended link {1} to {2} must target a generic reference" ).format(modelLink.modelDocument.basename, modelLink.role, resourceArcToLabel), "err", "xbrlre.2.1.1:genericReferenceTarget") self.dimensionDefaults = {} modelXbrl.qnameDimensionDefaults = {} modelXbrl.qnameDimensionContextElement = {} # check base set cycles, dimensions modelXbrl.modelManager.showStatus(_("validating relationship sets")) for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if arcrole.startswith("XBRL-") or ELR is None or \ linkqname is None or arcqname is None: continue elif arcrole in XbrlConst.standardArcroleCyclesAllowed: cyclesAllowed, specSect = XbrlConst.standardArcroleCyclesAllowed[ arcrole] elif arcrole in self.modelXbrl.arcroleTypes and len( self.modelXbrl.arcroleTypes[arcrole]) > 0: cyclesAllowed = self.modelXbrl.arcroleTypes[arcrole][ 0].cyclesAllowed if arcrole in self.genericArcArcroles: specSect = "xbrlgene:violatedCyclesConstraint" else: specSect = "xbrl.5.1.4.3:cycles" else: cyclesAllowed = "any" specSect = None if cyclesAllowed != "any" or arcrole in (XbrlConst.summationItem,) \ or arcrole in self.genericArcArcroles \ or arcrole.startswith(XbrlConst.formulaStartsWith): relsSet = modelXbrl.relationshipSet(arcrole, ELR, linkqname, arcqname) if cyclesAllowed != "any" and \ (XbrlConst.isStandardExtLinkQname(linkqname) and XbrlConst.isStandardArcQname(arcqname)) \ or arcrole in self.genericArcArcroles: noUndirected = cyclesAllowed == "none" fromRelationships = relsSet.fromModelObjects() for relFrom, rels in fromRelationships.items(): cycleFound = self.fwdCycle(relsSet, rels, noUndirected, {relFrom}) if cycleFound: modelXbrl.error( _("Relationships have a {0} cycle in arcrole {1} link role {2} link {3}, arc {4} starting from {5}" ).format(cycleFound, arcrole, ELR, linkqname, arcqname, relFrom.qname), "err", "{0}".format(specSect)) break # check calculation arcs for weight issues (note calc arc is an "any" cycles) if arcrole == XbrlConst.summationItem: for modelRel in relsSet.modelRelationships: weight = modelRel.weight fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept and toConcept: if weight == 0: modelXbrl.error( _("Calculation relationship has zero weight from {0} to {1} in link role {2}" ).format(fromConcept.qname, toConcept.qname, ELR), "err", "xbrl.5.2.5.2.1:zeroWeight") fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if (fromBalance == toBalance and weight < 0) or \ (fromBalance != toBalance and weight > 0): modelXbrl.error( _("Calculation relationship has illegal weight {0} from {1}, {2}, to {3}, {4} in link role {5} (per 5.1.1.2 Table 6)" ).format(weight, fromConcept.qname, fromBalance, toConcept.qname, toBalance, ELR), "err", "xbrl.5.1.1.2:balanceCalcWeight") if not fromConcept.isNumeric or not toConcept.isNumeric: modelXbrl.error( _("Calculation relationship has illegal concept from {0}{1} to {2}{3} in link role {4}" ).format( fromConcept.qname, "" if fromConcept.isNumeric else " (non-numeric)", toConcept.qname, "" if fromConcept.isNumeric else " (non-numeric)", ELR), "err", "xbrl.5.2.5.2:nonNumericCalc") # check presentation relationships for preferredLabel issues elif arcrole == XbrlConst.parentChild: for modelRel in relsSet.modelRelationships: preferredLabel = modelRel.preferredLabel toConcept = modelRel.toModelObject if preferredLabel and toConcept and \ toConcept.label(preferredLabel=preferredLabel,fallbackToQname=False) is None: modelXbrl.error( _("Presentation relationship from {0} to {1} in link role {2} missing preferredLabel {3}" ).format(modelRel.fromModelObject.qname, toConcept.qname, ELR, preferredLabel), "err", "xbrl.5.2.4.2.1:preferredLabelMissing") # check essence-alias relationships elif arcrole == XbrlConst.essenceAlias: for modelRel in relsSet.modelRelationships: fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept and toConcept: if fromConcept.type != toConcept.type or fromConcept.periodType != toConcept.periodType: modelXbrl.error( _("Essence-alias relationship from {0} to {1} in link role {2} has different types or periodTypes" ).format(fromConcept.qname, toConcept.qname, ELR), "err", "xbrl.5.2.6.2.2:essenceAliasTypes") fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if fromBalance and toBalance and fromBalance != toBalance: modelXbrl.error( _("Essence-alias relationship from {0} to {1} in link role {2} has different balances" ).format(fromConcept.qname, toConcept.qname, ELR), "err", "xbrl.5.2.6.2.2:essenceAliasBalance") elif modelXbrl.hasXDT and arcrole.startswith( XbrlConst.dimStartsWith): ValidateXbrlDimensions.checkBaseSet(self, arcrole, ELR, relsSet) elif modelXbrl.hasFormulae and arcrole.startswith( XbrlConst.formulaStartsWith): ValidateFormula.checkBaseSet(self, arcrole, ELR, relsSet) # instance checks modelXbrl.modelManager.showStatus(_("validating instance")) self.footnoteRefs = set() if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE or \ modelXbrl.modelDocument.type == ModelDocument.Type.INLINEXBRL: for f in modelXbrl.facts: concept = f.concept if concept: if concept.isNumeric: unit = f.unit if f.unitID is None or unit is None: self.modelXbrl.error( _("Fact {0} context {1} is numeric and must have a unit" ).format(modelXbrl.modelDocument.basename, f.qname, f.contextID), "err", "xbrl.4.6.2:numericUnit") else: if concept.isMonetary: measures = unit.measures if not measures or len(measures[0]) != 1 or len(measures[1]) != 0 or \ measures[0][0].namespaceURI != XbrlConst.iso4217 or \ not self.isoCurrencyPattern.match(measures[0][0].localName): self.modelXbrl.error( _("Fact {0} context {1} must have a monetary unit {2}" ).format( modelXbrl.modelDocument.basename, f.qname, f.contextID, f.unitID), "err", "xbrl.4.8.2:monetaryFactUnit") elif concept.isShares: measures = unit.measures if not measures or len(measures[0]) != 1 or len(measures[1]) != 0 or \ measures[0][0] != XbrlConst.qnXbrliShares: self.modelXbrl.error( _("Fact {0} context {1} must have a xbrli:shares unit {2}" ).format(f.qname, f.contextID, f.unitID), "err", "xbrl.4.8.2:sharesFactUnit") precision = f.precision hasPrecision = precision is not None if hasPrecision and precision != "INF" and not precision.isdigit( ): self.modelXbrl.error( _("Fact {0} context {1} precision {2} is invalid" ).format(f.qname, f.contextID, precision), "err", "xbrl.4.6.4:precision") decimals = f.decimals hasDecimals = decimals is not None if hasPrecision and not self.precisionPattern.match( precision): self.modelXbrl.error( _("Fact {0} context {1} precision {2} is invalid" ).format(f.qname, f.contextID, precision), "err", "xbrl.4.6.4:precision") if hasPrecision and hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} can not have both precision and decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:bothPrecisionAndDecimals") if hasDecimals and not self.decimalsPattern.match( decimals): self.modelXbrl.error( _("Fact {0} context {1} decimals {2} is invalid" ).format(f.qname, f.contextID, decimals), "err", "xbrl.4.6.5:decimals") if concept.isItem: context = f.context if context is None: self.modelXbrl.error( _("Item {0} must have a context").format( f.qname), "err", "xbrl.4.6.1:itemContextRef") else: periodType = concept.periodType if (periodType == "instant" and not context.isInstantPeriod) or \ (periodType == "duration" and not (context.isStartEndPeriod or context.isForeverPeriod)): self.modelXbrl.error( _("Fact {0} context {1} has period type {2} conflict with context" ).format(f.qname, f.contextID, periodType), "err", "xbrl.4.7.2:contextPeriodType") if modelXbrl.hasXDT: ValidateXbrlDimensions.checkFact(self, f) # check precision and decimals if f.xsiNil == "true": if hasPrecision or hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} can not be nil and have either precision or decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:nilPrecisionDecimals") elif concept.isFraction: if hasPrecision or hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} is a fraction concept and cannot have either precision or decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:fractionPrecisionDecimals") numerator, denominator = f.fractionValue if not (numerator == "INF" or numerator.isnumeric()): self.modelXbrl.error( _("Fact {0} context {1} is a fraction with invalid numerator {2}" ).format(f.qname, f.contextID, numerator), "err", "xbrl.5.1.1:fractionPrecisionDecimals") if not denominator.isnumeric() or int( denominator) == 0: self.modelXbrl.error( _("Fact {0} context {1} is a fraction with invalid denominator {2}" ).format(f.qname, f.contextID, denominator), "err", "xbrl.5.1.1:fractionPrecisionDecimals") else: if modelXbrl.modelDocument.type != ModelDocument.Type.INLINEXBRL: for child in f.element.childNodes: if child.nodeType == 1: self.modelXbrl.error( _("Fact {0} context {1} may not have child elements {2}" ).format(f.qname, f.contextID, child.tagName), "err", "xbrl.5.1.1:itemMixedContent") break if concept.isNumeric and not hasPrecision and not hasDecimals: self.modelXbrl.error( _("Fact {0} context {1} is a numeric concept and must have either precision or decimals" ).format(f.qname, f.contextID), "err", "xbrl.4.6.3:missingPrecisionDecimals") elif concept.isTuple: if f.contextID: self.modelXbrl.error( _("Tuple {0} must not have a context").format( f.qname), "err", "xbrl.4.6.1:tupleContextRef") if hasPrecision or hasDecimals: self.modelXbrl.error( _("Fact {0} is a tuple and cannot have either precision or decimals" ).format(f.qname), "err", "xbrl.4.6.3:tuplePrecisionDecimals") # custom attributes may be allowed by anyAttribute but not by 2.1 for attrQname, attrValue in XbrlUtil.attributes( self.modelXbrl, concept, f.element): if attrQname.namespaceURI in (XbrlConst.xbrli, XbrlConst.link, XbrlConst.xlink, XbrlConst.xl): self.modelXbrl.error( _("Fact {0} is a tuple and must not have attribute in this namespace {1}" ).format(f.qname, attrQname), "err", "xbrl.4.9:tupleAttribute") else: self.modelXbrl.error( _("Fact {0} must be an item or tuple").format( f.qname), "err", "xbrl.4.6:notItemOrTuple") from arelle.ModelObject import ModelInlineFact if isinstance(f, ModelInlineFact): self.footnoteRefs.update(f.footnoteRefs) #instance checks for cntx in modelXbrl.contexts.values(): if cntx.isStartEndPeriod: try: if cntx.endDatetime <= cntx.startDatetime: self.modelXbrl.error( _("Context {0} must have startDate less than endDate" ).format(cntx.id), "err", "xbrl.4.7.2:periodStartBeforeEnd") except ValueError as err: self.modelXbrl.error( _("Context {0} startDate or endDate: {1}").format( cntx.id, err), "err", "xbrl.4.7.2:contextDateError") elif cntx.isInstantPeriod: try: cntx.instantDatetime #parse field except ValueError as err: self.modelXbrl.error( _("Context {0} instant date: {1}").format( cntx.id, err), "err", "xbrl.4.7.2:contextDateError") self.segmentScenario(cntx.segment, cntx.id, "segment", "4.7.3.2") self.segmentScenario(cntx.scenario, cntx.id, "scenario", "4.7.4") if modelXbrl.hasXDT: ValidateXbrlDimensions.checkContext(self, cntx) for unit in modelXbrl.units.values(): mulDivMeasures = unit.measures if mulDivMeasures: for measures in mulDivMeasures: for measure in measures: if measure.namespaceURI == XbrlConst.xbrli and not \ measure in (XbrlConst.qnXbrliPure, XbrlConst.qnXbrliShares): self.modelXbrl.error( _("Unit {0} illegal measure: {1}").format( unit.id, measure), "err", "xbrl.4.8.2:measureElement") for numeratorMeasure in mulDivMeasures[0]: if numeratorMeasure in mulDivMeasures[1]: self.modelXbrl.error( _("Unit {0} numerator measure: {1} also appears as denominator measure" ).format(unit.id, numeratorMeasure), "err", "xbrl.4.8.4:measureBothNumDenom") #concepts checks modelXbrl.modelManager.showStatus(_("validating concepts")) for concept in modelXbrl.qnameConcepts.values(): conceptType = concept.type if XbrlConst.isStandardNamespace(concept.namespaceURI) or \ not concept.modelDocument.inDTS: continue if concept.isTuple: # must be global if not concept.element.parentNode.localName == "schema": self.modelXbrl.error( _("Tuple {0} must be declared globally").format( concept.qname), "err", "xbrl.4.9:tupleGloballyDeclared") if concept.periodType: self.modelXbrl.error( _("Tuple {0} must not have periodType").format( concept.qname), "err", "xbrl.4.9:tuplePeriodType") if concept.balance: self.modelXbrl.error( _("Tuple {0} must not have balance").format( concept.qname), "err", "xbrl.4.9:tupleBalance") # check attribute declarations for attributeQname in conceptType.attributes: if attributeQname.namespaceURI in (XbrlConst.xbrli, XbrlConst.link, XbrlConst.xlink, XbrlConst.xl): self.modelXbrl.error( _("Tuple {0} must not have attribute in this namespace {1}" ).format(concept.qname, attributeQname), "err", "xbrl.4.9:tupleAttribute") # check for mixed="true" or simple content if XmlUtil.descendantAttr(conceptType.element, XbrlConst.xsd, ("complexType", "complexContent"), "mixed") == "true": self.modelXbrl.error( _("Tuple {0} must not have mixed content").format( concept.qname), "err", "xbrl.4.9:tupleMixedContent") if XmlUtil.descendant(conceptType.element, XbrlConst.xsd, "simpleContent"): self.modelXbrl.error( _("Tuple {0} must not have simple content").format( concept.qname), "err", "xbrl.4.9:tupleSimpleContent") # child elements must be item or tuple for elementQname in conceptType.elements: childConcept = self.modelXbrl.qnameConcepts.get( elementQname) if childConcept is None: self.modelXbrl.error( _("Tuple {0} element {1} not defined").format( concept.qname, elementQname), "err", "xbrl.4.9:tupleElementUndefined") elif not ( childConcept.isItem or childConcept.isTuple or # isItem/isTuple do not include item or tuple itself childConcept.qname == XbrlConst.qnXbrliItem or # subs group includes item as member childConcept.qname == XbrlConst.qnXbrliTuple): self.modelXbrl.error( _("Tuple {0} must not have element {1} not an item or tuple" ).format(concept.qname, elementQname), "err", "xbrl.4.9:tupleElementItemOrTuple") elif concept.isItem: if concept.periodType not in periodTypeValues: #("instant","duration"): self.modelXbrl.error( _("Item {0} must have a valid periodType").format( concept.qname), "err", "xbrl.5.1.1.1:itemPeriodType") if concept.isMonetary: if concept.balance not in balanceValues: #(None, "credit","debit"): self.modelXbrl.error( _("Item {0} must have a valid balance {1}").format( concept.qname, concept.balance), "err", "xbrl.5.1.1.2:itemBalance") else: if concept.balance: self.modelXbrl.error( _("Item {0} may not have a balance").format( concept.qname), "err", "xbrl.5.1.1.2:itemBalance") if concept.baseXbrliType not in baseXbrliTypes: self.modelXbrl.error( _("Item {0} type {1} invalid").format( concept.qname, concept.baseXbrliType), "err", "xbrl.5.1.1.3:itemType") if modelXbrl.hasXDT: if concept.isHypercubeItem and not concept.abstract == "true": self.modelXbrl.error( _("Hypercube item {0} must be abstract").format( concept.qname), "err", "xbrldte:HypercubeElementIsNotAbstractError") elif concept.isDimensionItem and not concept.abstract == "true": self.modelXbrl.error( _("Dimension item {0} must be abstract").format( concept.qname), "err", "xbrldte:DimensionElementIsNotAbstractError") if modelXbrl.hasXDT: ValidateXbrlDimensions.checkConcept(self, concept) modelXbrl.modelManager.showStatus(_("validating DTS")) self.DTSreferenceResourceIDs = {} ValidateXbrlDTS.checkDTS(self, modelXbrl.modelDocument, []) del self.DTSreferenceResourceIDs if self.validateCalcLB: modelXbrl.modelManager.showStatus( _("Validating instance calculations")) ValidateXbrlCalcs.validate( modelXbrl, inferPrecision=(not self.validateInferDecimals)) if modelXbrl.modelManager.validateUtr: ValidateUtr.validate(modelXbrl) if modelXbrl.hasFormulae: ValidateFormula.validate(self) modelXbrl.modelManager.showStatus(_("ready"), 2000)
def validate(self, modelVersReport): self.modelVersReport = modelVersReport versReport = modelVersReport.modelDocument if not hasattr(versReport, "xmlDocument"): # not parsed return for DTSname in ("fromDTS", "toDTS"): DTSmodelXbrl = getattr(versReport, DTSname) if DTSmodelXbrl is None or DTSmodelXbrl.modelDocument is None: self.modelVersReport.error( _("{0} is missing or not loaded").format(DTSname), "err", "vere:invalidDTSIdentifier") else: # validate DTS ValidateXbrl.ValidateXbrl(DTSmodelXbrl).validate(DTSmodelXbrl) if len(DTSmodelXbrl.errors) > 0: self.modelVersReport.error( _("{0} {1} has errors: {2}").format( DTSname, DTSmodelXbrl.modelDocument.basename, DTSmodelXbrl.errors), "err", "vere:invalidDTSIdentifier") # validate linkbases ValidateXbrl.ValidateXbrl( self.modelVersReport).validate(modelVersReport) versReportElt = versReport.xmlRootElement # check actions for assignmentRef in versReportElt.getElementsByTagNameNS( XbrlConst.ver, "assignmentRef"): ref = assignmentRef.getAttribute("ref") if ref not in versReport.idObjects or \ not isinstance(versReport.idObjects[ref], ModelVersObject.ModelAssignment): self.modelVersReport.error( _("AssignmentRef {0} does not reference an assignment"). format(ref), "err", "vere:invalidAssignmentRef") # check namespace renames for NSrename in versReport.namespaceRenameFrom.values(): if NSrename.fromURI not in versReport.fromDTS.namespaceDocs: self.modelVersReport.error( _("NamespaceRename fromURI {0} does not reference a schema in fromDTS" ).format(NSrename.fromURI), "err", "vere:invalidNamespaceMapping") if NSrename.toURI not in versReport.toDTS.namespaceDocs: self.modelVersReport.error( _("NamespaceRename toURI {0} does not reference a schema in toDTS" ).format(NSrename.toURI), "err", "vere:invalidNamespaceMapping") # check role changes for roleChange in versReport.roleChanges.values(): if roleChange.fromURI not in versReport.fromDTS.roleTypes: self.modelVersReport.error( _("RoleChange fromURI {0} does not reference a roleType in fromDTS" ).format(roleChange.fromURI), "err", "vere:invalidRoleChange") if roleChange.toURI not in versReport.toDTS.roleTypes: self.modelVersReport.error( _("RoleChange toURI {0} does not reference a roleType in toDTS" ).format(roleChange.toURI), "err", "vere:invalidRoleChange") # check reportRefs # check actions for reportRef in versReportElt.getElementsByTagNameNS( XbrlConst.ver, "reportRef"): xlinkType = reportRef.getAttributeNS(XbrlConst.xlink, "type") if xlinkType != "simple": self.modelVersReport.error( _("ReportRef xlink:type {0} must be \"simple\"").format( xlinkType), "err", "vere:invalidXlinkType") # if existing it must be valid href = reportRef.getAttributeNS(XbrlConst.xlink, "href") # TBD if not reportRef.hasAttributeNS(XbrlConst.xlink, "arcrole"): self.modelVersReport.error( _("ReportRef xlink:arcrole is missing"), "err", "vere:missingXlinkArcrole") else: arcrole = reportRef.getAttributeNS(XbrlConst.xlink, "arcrole") if arcrole != "http://xbrl.org/arcrole/2010/versioning/related-report": self.modelVersReport.error( _("ReportRef xlink:arcrole {0} is invalid").format( arcrole), "err", "vere:invalidXlinkArcrole") if versReport.fromDTS and versReport.toDTS: # check concept changes of concept basic for conceptChange in versReport.conceptBasicChanges: if conceptChange.name != "conceptAdd" and \ (conceptChange.fromConcept is None or \ conceptChange.fromConcept.qname not in versReport.fromDTS.qnameConcepts): self.modelVersReport.error( _("{0} fromConcept {1} does not reference a concept in fromDTS" ).format(conceptChange.name, conceptChange.fromConceptQname), "err", "vercbe:invalidConceptReference") if conceptChange.name != "conceptDelete" and \ (conceptChange.toConcept is None or \ conceptChange.toConcept.qname not in versReport.toDTS.qnameConcepts): self.modelVersReport.error( _("{0} toConcept {1} does not reference a concept in toDTS" ).format(conceptChange.name, conceptChange.toConceptQname), "err", "vercbe:invalidConceptReference") # check concept changes of concept extended for conceptChange in versReport.conceptExtendedChanges: fromConcept = conceptChange.fromConcept toConcept = conceptChange.toConcept fromResource = conceptChange.fromResource toResource = conceptChange.toResource # fromConcept checks if not conceptChange.name.endswith("Add"): if not fromConcept: self.modelVersReport.error( _("{0} {1} fromConcept {2} does not reference a concept in fromDTS" ).format(conceptChange.actionId, conceptChange.name, conceptChange.fromConceptQname), "err", "vercbe:invalidConceptReference") # tuple check elif "Child" in conceptChange.name and \ not versReport.fromDTS.qnameConcepts[fromConcept.qname] \ .isTuple: self.modelVersReport.error( _("{0} {1} fromConcept {2} must be defined as a tuple" ).format(conceptChange.actionId, conceptChange.name, conceptChange.fromConceptQname), "err", "vercbe:invalidConceptReference") # resource check elif "Label" in conceptChange.name: if not fromResource: self.modelVersReport.error( _("{0} {1} fromResource {2} does not reference a resource in fromDTS" ).format(conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = fromConcept.relationshipToResource( fromResource, XbrlConst.conceptLabel) if relationship: if relationship.qname != XbrlConst.qnLinkLabelArc or \ relationship.parentQname != XbrlConst.qnLinkLabelLink or \ fromResource.qname != XbrlConst.qnLinkLabel: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements" ).format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidConceptLabelIdentifier") else: relationship = fromConcept.relationshipToResource( fromResource, XbrlConst.elementLabel) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements" ).format( conceptChange.actionId, conceptChange.name, conceptChange. fromResourceValue, conceptChange. fromConceptQname), "err", "vercee:invalidConceptLabelIdentifier" ) else: self.modelVersReport.error( _("{0} {1} fromResource {2} does not have a label relationship to {3} in fromDTS" ).format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidContentResourceIdentifier" ) elif "Reference" in conceptChange.name: if not fromResource: self.modelVersReport.error( _("{0} {1} fromResource {2} does not reference a resource in fromDTS" ).format(conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = fromConcept.relationshipToResource( fromResource, XbrlConst.conceptReference) if relationship: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ fromResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements" ).format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidConceptReferenceIdentifier" ) else: relationship = fromConcept.relationshipToResource( fromResource, XbrlConst.elementReference) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements" ).format( conceptChange.actionId, conceptChange.name, conceptChange. fromResourceValue, conceptChange. fromConceptQname), "err", "vercee:invalidConceptReferenceIdentifier" ) else: self.modelVersReport.error( _("{0} {1} fromResource {2} does not have a reference relationship to {3} in fromDTS" ).format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidContentResourceIdentifier" ) # toConcept checks if not conceptChange.name.endswith("Delete"): if not toConcept: self.modelVersReport.error( _("{0} {1} toConcept {2} does not reference a concept in toDTS" ).format(conceptChange.actionId, conceptChange.name, conceptChange.toConceptQname), "err", "vercbe:invalidConceptReference") # tuple check elif "Child" in conceptChange.name and \ not versReport.toDTS.qnameConcepts[toConcept.qname] \ .isTuple: self.modelVersReport.error( _("{0} {1} toConcept {2} must be defined as a tuple" ).format(conceptChange.actionId, conceptChange.name, conceptChange.toConceptQname), "err", "vercbe:invalidConceptReference") # resource check elif "Label" in conceptChange.name: if not toResource: self.modelVersReport.error( _("{0} {1} toResource {2} does not reference a resource in toDTS" ).format(conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = toConcept.relationshipToResource( toResource, XbrlConst.conceptLabel) if relationship: if relationship.qname != XbrlConst.qnLinkLabelArc or \ relationship.parentQname != XbrlConst.qnLinkLabelLink or \ toResource.qname != XbrlConst.qnLinkLabel: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements" ).format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptLabelIdentifier") else: relationship = toConcept.relationshipToResource( toResource, XbrlConst.elementLabel) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements" ). format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptLabelIdentifier" ) else: self.modelVersReport.error( _("{0} {1} toResource {2} does not have a label relationship to {3} in toDTS" ).format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidContentResourceIdentifier" ) elif "Reference" in conceptChange.name: if not toResource: self.modelVersReport.error( _("{0} {1} toResource {2} does not reference a resource in toDTS" ).format(conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = toConcept.relationshipToResource( toResource, XbrlConst.conceptReference) if relationship: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ toResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements" ).format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptReferenceIdentifier" ) else: relationship = toConcept.relationshipToResource( toResource, XbrlConst.elementReference) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements" ). format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptReferenceIdentifier" ) else: self.modelVersReport.error( _("{0} {1} toResource {2} does not have a reference relationship to {3} in toDTS" ).format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidContentResourceIdentifier" ) # check concept correspondence if fromConcept and toConcept: if versReport.toDTSqname(fromConcept.qname) != toConcept.qname and \ versReport.equivalentConcepts.get(fromConcept.qname) != toConcept.qname and \ toConcept.qname not in versReport.relatedConcepts.get(fromConcept.qname,[]): self.modelVersReport.error( _("{0} {1} fromConcept {2} and toConcept {3} must be equivalent or related" ).format(conceptChange.actionId, conceptChange.name, conceptChange.fromConceptQname, conceptChange.toConceptQname), "err", "vercee:invalidConceptCorrespondence") # custom attribute events if conceptChange.name.startswith("conceptAttribute"): try: for attr in conceptAttributeEventAttributes[ conceptChange.name]: customAttributeQname = conceptChange.customAttributeQname( attr) if not customAttributeQname or customAttributeQname.namespaceURI is None: self.modelVersReport.error( _("{0} {1} {2} {3} does not have a namespace" ).format(conceptChange.actionId, conceptChange.name, attr, customAttributeQname), "err", "vercee:invalidAttributeChange") elif customAttributeQname.namespaceURI in ( XbrlConst.xbrli, XbrlConst.xsd): self.modelVersReport.error( _("{0} {1} {2} {3} has an invalid namespace" ).format(conceptChange.actionId, conceptChange.name, attr, customAttributeQname), "err", "vercee:illegalCustomAttributeEvent") except KeyError: self.modelVersReport.error( _("{0} {1} event is not recognized").format( conceptChange.actionId, conceptChange.name), "info", "arelle:eventNotRecognized") # check relationship set changes for relSetChange in versReport.relationshipSetChanges: for relationshipSet, name in ( (relSetChange.fromRelationshipSet, "fromRelationshipSet"), (relSetChange.toRelationshipSet, "toRelationshipSet")): if relationshipSet: relationshipSetValid = True if relationshipSet.link and relationshipSet.link not in relationshipSet.dts.qnameConcepts: self.modelVersReport.error( _("{0} link {1} does not reference an element in its DTS" ).format(relSetChange.name, name, relationshipSet.link), "err", "verrelse:invalidLinkElementReference") relationshipSetValid = False if relationshipSet.arc and relationshipSet.arc not in relationshipSet.dts.qnameConcepts: self.modelVersReport.error( _("{0} arc {1} does not reference an element in its DTS" ).format(relSetChange.name, name, relationshipSet.link), "err", "verrelse:invalidArcElementReference") relationshipSetValid = False if relationshipSet.linkrole and not ( XbrlConst.isStandardRole( relationshipSet.linkrole) or relationshipSet.linkrole in relationshipSet.dts.roleTypes): self.modelVersReport.error( _("{0} linkrole {1} does not reference an linkrole in its DTS" ).format(relSetChange.name, name, relationshipSet.linkrole), "err", "verrelse:invalidLinkrole") relationshipSetValid = False if relationshipSet.arcrole and not ( XbrlConst.isStandardArcrole( relationshipSet.arcrole) or relationshipSet.arcrole in relationshipSet.dts.arcroleTypes): self.modelVersReport.error( _("{0} arcrole {1} does not reference an arcrole in its DTS" ).format(relSetChange.name, name, relationshipSet.linkrole), "err", "verrelse:invalidArcrole") relationshipSetValid = False for relationship in relationshipSet.relationships: # fromConcept checks if not relationship.fromConcept: self.modelVersReport.error( _("{0} {1} relationship fromConcept {2} does not reference a concept in its DTS" ).format(relSetChange.name, name, relationship.fromName), "err", "verrelse:invalidConceptReference") relationshipSetValid = False if relationship.toName and not relationship.toConcept: self.modelVersReport.error( _("{0} {1} relationship toConcept {2} does not reference a concept in its DTS" ).format(relSetChange.name, name, relationship.toName), "err", "verrelse:invalidConceptReference") relationshipSetValid = False if relationshipSetValid: # test that relations exist if relationship.fromRelationship is None: if relationship.toName: self.modelVersReport.error( _("{0} {1} no relationship found from toConcept {2} to {3} in its DTS" ).format(relSetChange.name, name, relationship.fromName, relationship.toName), "err", "verrelse:invalidRelationshipReference" ) else: self.modelVersReport.error( _("{0} {1} no relationship found from toConcept {2} in its DTS" ).format(relSetChange.name, name, relationship.fromName), "err", "verrelse:invalidRelationshipReference" ) '''
def init(modelXbrl): # setup modelXbrl for rendering evaluation # dimension defaults required in advance of validation from arelle import ValidateXbrlDimensions, ValidateFormula, ModelDocument ValidateXbrlDimensions.loadDimensionDefaults(modelXbrl) hasXbrlTables = False # validate table linkbase dimensions for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname and XbrlConst.isTableRenderingArcrole(arcrole): ValidateFormula.checkBaseSet(modelXbrl, arcrole, ELR, modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname)) if arcrole in (XbrlConst.tableBreakdown, XbrlConst.tableBreakdownMMDD, XbrlConst.tableBreakdown201305, XbrlConst.tableBreakdown201301, XbrlConst.tableAxis2011): hasXbrlTables = True # provide context for view if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE: instance = None # use instance of the entry pont else: # need dummy instance instance = ModelDocument.create(modelXbrl, ModelDocument.Type.INSTANCE, "dummy.xml", # fake URI and fake schemaRef ("http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd",)) if hasXbrlTables: # formula processor is needed for 2011 XBRL tables but not for 2010 Eurofiling tables modelXbrl.rendrCntx = XPathContext.create(modelXbrl, instance) modelXbrl.profileStat(None) # setup fresh parameters from formula options modelXbrl.parameters = modelXbrl.modelManager.formulaOptions.typedParameters() # validate parameters and custom function signatures ValidateFormula.validate(modelXbrl, xpathContext=modelXbrl.rendrCntx, parametersOnly=True, statusMsg=_("compiling rendering tables")) # deprecated as of 2013-05-17 # check and extract message expressions into compilable programs for msgArcrole in (XbrlConst.tableDefinitionNodeMessage201301, XbrlConst.tableDefinitionNodeSelectionMessage201301, XbrlConst.tableAxisMessage2011, XbrlConst.tableAxisSelectionMessage2011): for msgRel in modelXbrl.relationshipSet(msgArcrole).modelRelationships: ValidateFormula.checkMessageExpressions(modelXbrl, msgRel.toModelObject) # compile and validate tables for modelTable in modelXbrl.modelRenderingTables: modelTable.fromInstanceQnames = None # required if referred to by variables scope chaining modelTable.compile() hasNsWithAspectModel = modelTable.namespaceURI in (XbrlConst.euRend, XbrlConst.table2011, XbrlConst.table201301, XbrlConst.table201305) # check aspectModel (attribute removed 2013-06, now always dimensional) if modelTable.aspectModel not in ("non-dimensional", "dimensional") and hasNsWithAspectModel: modelXbrl.error("xbrlte:unknownAspectModel", _("Table %(xlinkLabel)s, aspect model %(aspectModel)s not recognized"), modelObject=modelTable, xlinkLabel=modelTable.xlinkLabel, aspectModel=modelTable.aspectModel) else: modelTable.priorAspectAxisDisposition = {} # check ordinate aspects against aspectModel oppositeAspectModel = (_DICT_SET({'dimensional','non-dimensional'}) - _DICT_SET({modelTable.aspectModel})).pop() if hasNsWithAspectModel: uncoverableAspects = aspectModels[oppositeAspectModel] - aspectModels[modelTable.aspectModel] else: uncoverableAspects = () aspectsCovered = set() for tblAxisRel in modelXbrl.relationshipSet((XbrlConst.tableBreakdown, XbrlConst.tableBreakdownMMDD, XbrlConst.tableBreakdown201305, XbrlConst.tableBreakdown201301,XbrlConst.tableAxis2011)).fromModelObject(modelTable): breakdownAspectsCovered = set() hasCoveredAspect = checkBreakdownDefinitionNode(modelXbrl, modelTable, tblAxisRel, tblAxisRel.axisDisposition, uncoverableAspects, breakdownAspectsCovered) ''' removed 2013-10 if not hasCoveredAspect: definitionNode = tblAxisRel.toModelObject modelXbrl.error("xbrlte:breakdownDefinesNoAspects", _("Breakdown %(xlinkLabel)s has no participating aspects"), modelObject=(modelTable,definitionNode), xlinkLabel=definitionNode.xlinkLabel, axis=definitionNode.localName) ''' aspectsCovered |= breakdownAspectsCovered checkBreakdownLeafNodeAspects(modelXbrl, modelTable, tblAxisRel, set(), breakdownAspectsCovered) if Aspect.CONCEPT not in aspectsCovered and not hasNsWithAspectModel: modelXbrl.error("xbrlte:tableMissingConceptAspect", _("Table %(xlinkLabel)s does not include the concept aspect as one of its participating aspects"), modelObject=modelTable, xlinkLabel=modelTable.xlinkLabel) del modelTable.priorAspectAxisDisposition # check for table-parameter name clash parameterNames = {} for tblParamRel in modelXbrl.relationshipSet((XbrlConst.tableParameter, XbrlConst.tableParameterMMDD)).fromModelObject(modelTable): parameterName = tblParamRel.variableQname if parameterName in parameterNames: modelXbrl.error("xbrlte:tableParameterNameClash ", _("Table %(xlinkLabel)s has parameter name clash for variable %(name)s"), modelObject=(modelTable,tblParamRel,parameterNames[parameterName]), xlinkLabel=modelTable.xlinkLabel, name=parameterName) else: parameterNames[parameterName] = tblParamRel modelXbrl.profileStat(_("compileTables"))
def validate(self, modelVersReport): self.modelVersReport = modelVersReport versReport = modelVersReport.modelDocument if not hasattr(versReport, "xmlDocument"): # not parsed return for DTSname in ("fromDTS", "toDTS"): DTSmodelXbrl = getattr(versReport, DTSname) if DTSmodelXbrl is None or DTSmodelXbrl.modelDocument is None: self.modelVersReport.error("vere:invalidDTSIdentifier", _("%(dts)s is missing or not loaded"), modelObject=self, dts=DTSname) else: # validate DTS ValidateXbrl.ValidateXbrl(DTSmodelXbrl).validate(DTSmodelXbrl) if len(DTSmodelXbrl.errors) > 0: self.modelVersReport.error("vere:invalidDTSIdentifier", _("%(dts) has errors: %(error)s"), modelObject=DTSmodelXbrl.modelDocument, dts=DTSname, error=DTSmodelXbrl.errors) # validate linkbases ValidateXbrl.ValidateXbrl(self.modelVersReport).validate(modelVersReport) versReportElt = versReport.xmlRootElement # check actions for assignmentRef in versReportElt.iterdescendants(tag="{http://xbrl.org/2010/versioning-base}assignmentRef"): ref = assignmentRef.get("ref") if ref not in versReport.idObjects or \ not isinstance(versReport.idObjects[ref], ModelVersObject.ModelAssignment): self.modelVersReport.error("vere:invalidAssignmentRef", _("AssignmentRef %(assignmentRef)s does not reference an assignment"), modelObject=assignmentRef, assignmentRef=ref) # check namespace renames for NSrename in versReport.namespaceRenameFrom.values(): if NSrename.fromURI not in versReport.fromDTS.namespaceDocs: self.modelVersReport.error("vere:invalidNamespaceMapping", _("NamespaceRename fromURI %(uri)s does not reference a schema in fromDTS"), modelObject=self, uri=NSrename.fromURI) if NSrename.toURI not in versReport.toDTS.namespaceDocs: self.modelVersReport.error("vere:invalidNamespaceMapping", _("NamespaceRename toURI %(uri)s does not reference a schema in toDTS"), modelObject=self, uri=NSrename.toURI) # check role changes for roleChange in versReport.roleChanges.values(): if roleChange.fromURI not in versReport.fromDTS.roleTypes: self.modelVersReport.error("vere:invalidRoleChange", _("RoleChange fromURI %(uri)s does not reference a roleType in fromDTS"), modelObject=self, uri=roleChange.fromURI) if roleChange.toURI not in versReport.toDTS.roleTypes: self.modelVersReport.error("vere:invalidRoleChange", _("RoleChange toURI %(uri)s does not reference a roleType in toDTS"), modelObject=self, uri=roleChange.toURI) # check reportRefs # check actions for reportRef in versReportElt.iterdescendants(tag="{http://xbrl.org/2010/versioning-base}reportRef"): xlinkType = reportRef.get("{http://www.w3.org/1999/xlink}type") if xlinkType != "simple": self.modelVersReport.error("vere:invalidXlinkType", _("ReportRef xlink:type %(xlinkType)s must be \"simple\""), modelObject=reportRef, xlinkType=xlinkType) # if existing it must be valid href = reportRef.get("{http://www.w3.org/1999/xlink}href") # TBD arcrole = reportRef.get("{http://www.w3.org/1999/xlink}arcrole") if arcrole is None: self.modelVersReport.error("vere:missingXlinkArcrole", _("ReportRef xlink:arcrole is missing"), modelObject=reportRef) else: if arcrole != "http://xbrl.org/arcrole/2010/versioning/related-report": self.modelVersReport.error("vere:invalidXlinkArcrole", _("ReportRef xlink:arcrole %(arcrole)s is invalid"), modelObject=reportRef, arcrole=arcrole) if versReport.fromDTS and versReport.toDTS: # check concept changes of concept basic for conceptChange in versReport.conceptBasicChanges: if conceptChange.name != "conceptAdd" and \ (conceptChange.fromConcept is None or \ conceptChange.fromConcept.qname not in versReport.fromDTS.qnameConcepts): self.modelVersReport.error("vercbe:invalidConceptReference", _("%(event)s fromConcept %(concept)s does not reference a concept in fromDTS"), modelObject=conceptChange, event=conceptChange.name, concept=conceptChange.fromConceptQname) if conceptChange.name != "conceptDelete" and \ (conceptChange.toConcept is None or \ conceptChange.toConcept.qname not in versReport.toDTS.qnameConcepts): self.modelVersReport.error("vercbe:invalidConceptReference", _("%(event)s toConcept %(concept)s does not reference a concept in toDTS"), modelObject=conceptChange, event=conceptChange.name, concept=conceptChange.toConceptQname) # check concept changes of concept extended for conceptChange in versReport.conceptExtendedChanges: fromConcept = conceptChange.fromConcept toConcept = conceptChange.toConcept fromResource = conceptChange.fromResource toResource = conceptChange.toResource # fromConcept checks if not conceptChange.name.endswith("Add"): if not fromConcept is not None: self.modelVersReport.error("vercbe:invalidConceptReference", _("%(action)s %(event)s fromConcept %(concept)s does not reference a concept in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.fromConceptQname) # tuple check elif _("Child") in conceptChange.name and \ not versReport.fromDTS.qnameConcepts[fromConcept.qname] \ .isTuple: self.modelVersReport.error("vercbe:invalidConceptReference", _("%(action)s %(event)s fromConcept %(concept)s must be defined as a tuple"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.fromConceptQname) # resource check elif "Label" in conceptChange.name: if fromResource is None: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not reference a resource in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.conceptLabel) if relationship is not None: if relationship.qname != XbrlConst.qnLinkLabelArc or \ relationship.parentQname != XbrlConst.qnLinkLabelLink or \ fromResource.qname != XbrlConst.qnLinkLabel: self.modelVersReport.error("vercee:invalidConceptLabelIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.elementLabel) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error("vercee:invalidConceptLabelIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not have a label relationship to {3} in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue) elif "Reference" in conceptChange.name: if fromResource is None: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not reference a resource in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.conceptReference) if relationship is not None: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ fromResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error("vercee:invalidConceptReferenceIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.elementReference) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error("vercee:invalidConceptReferenceIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not have a reference relationship to %(concept)s in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) # toConcept checks if not conceptChange.name.endswith("Delete"): if not toConcept is not None: self.modelVersReport.error("vercbe:invalidConceptReference", _("%(action)s %(event)s toConcept %(concept)s does not reference a concept in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.toConceptQname) # tuple check elif "Child" in conceptChange.name and \ not versReport.toDTS.qnameConcepts[toConcept.qname] \ .isTuple: self.modelVersReport.error("vercbe:invalidConceptReference", _("%(action)s %(event)s toConcept %(concept)s must be defined as a tuple"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.toConceptQname) # resource check elif "Label" in conceptChange.name: if toResource is None: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s does not reference a resource in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.conceptLabel) if relationship is not None: if relationship.qname != XbrlConst.qnLinkLabelArc or \ relationship.parentQname != XbrlConst.qnLinkLabelLink or \ toResource.qname != XbrlConst.qnLinkLabel: self.modelVersReport.error("vercee:invalidConceptLabelIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.elementLabel) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error("vercee:invalidConceptLabelIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s does not have a label relationship to %(concept)s in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) elif "Reference" in conceptChange.name: if toResource is None: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s does not reference a resource in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.conceptReference) if relationship is not None: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ toResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error("vercee:invalidConceptReferenceIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.elementReference) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error("vercee:invalidConceptReferenceIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: self.modelVersReport.error("vercee:invalidContentResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s does not have a reference relationship to %(concept)s in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) # check concept correspondence if fromConcept is not None and toConcept is not None: if versReport.toDTSqname(fromConcept.qname) != toConcept.qname and \ versReport.equivalentConcepts.get(fromConcept.qname) != toConcept.qname and \ toConcept.qname not in versReport.relatedConcepts.get(fromConcept.qname,[]): self.modelVersReport.error("vercee:invalidConceptCorrespondence", _("%(action)s %(event)s fromConcept %(conceptFrom)s and toConcept %(conceptTo)s must be equivalent or related"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, conceptFrom=conceptChange.fromConceptQname, conceptTo=conceptChange.toConceptQname) # custom attribute events if conceptChange.name.startswith("conceptAttribute"): try: for attr in conceptAttributeEventAttributes[conceptChange.name]: customAttributeQname = conceptChange.customAttributeQname(attr) if not customAttributeQname or customAttributeQname.namespaceURI is None: self.modelVersReport.error("vercee:invalidAttributeChange", _("%(action)s %(event)s %(attr)s $(attrName)s does not have a namespace"), modelObject=conceptChange, action=conceptChange.actionId, attr=attr, attrName=customAttributeQname) elif customAttributeQname.namespaceURI in (XbrlConst.xbrli, XbrlConst.xsd): self.modelVersReport.error("vercee:illegalCustomAttributeEvent", _("%(action)s %(event)s %(attr)s $(attrName)s has an invalid namespace"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, attr=attr, attrName=customAttributeQname) except KeyError: self.modelVersReport.info("arelle:eventNotRecognized", _("%(action)s %(event)s event is not recognized"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name) # check relationship set changes for relSetChange in versReport.relationshipSetChanges: for relationshipSet, name in ((relSetChange.fromRelationshipSet, "fromRelationshipSet"), (relSetChange.toRelationshipSet, "toRelationshipSet")): if relationshipSet is not None: relationshipSetValid = True if relationshipSet.link and relationshipSet.link not in relationshipSet.dts.qnameConcepts: self.modelVersReport.error("verrelse:invalidLinkElementReference", _("%(event)s %(relSet)s link %(link)s does not reference an element in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, link=relationshipSet.link) relationshipSetValid = False if relationshipSet.arc and relationshipSet.arc not in relationshipSet.dts.qnameConcepts: self.modelVersReport.error("verrelse:invalidArcElementReference", _("%(event)s %(relSet)s arc %(arc) does not reference an element in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, arc=relationshipSet.arc) relationshipSetValid = False if relationshipSet.linkrole and not (XbrlConst.isStandardRole(relationshipSet.linkrole) or relationshipSet.linkrole in relationshipSet.dts.roleTypes): self.modelVersReport.error("verrelse:invalidLinkrole", _("%(event)s %(relSet)s linkrole %(linkrole)s does not reference an linkrole in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, linkrole=relationshipSet.linkrole) relationshipSetValid = False if relationshipSet.arcrole and not (XbrlConst.isStandardArcrole(relationshipSet.arcrole) or relationshipSet.arcrole in relationshipSet.dts.arcroleTypes): self.modelVersReport.error("verrelse:invalidArcrole", _("%(event)s %(relSet)s arcrole %(arcrole)s does not reference an arcrole in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, arcrole=relationshipSet.arcrole) relationshipSetValid = False for relationship in relationshipSet.relationships: # fromConcept checks if relationship.fromConcept is None: self.modelVersReport.error("verrelse:invalidConceptReference", _("%(event)s %(relSet)s relationship fromConcept %(conceptFrom)s does not reference a concept in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptFrom=relationship.fromName) relationshipSetValid = False if relationship.toName and relationship.toConcept is None: self.modelVersReport.error("verrelse:invalidConceptReference", _("%(event)s %(relSet)s relationship toConcept %(conceptTo)s does not reference a concept in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptTo=relationship.toName) relationshipSetValid = False if relationshipSetValid: # test that relations exist if relationship.fromRelationship is None: if relationship.toName: self.modelVersReport.error("verrelse:invalidRelationshipReference", _("%(event)s %(relSet)s no relationship found from fromConcept %(conceptFrom)s to toConcept %(conceptTo)s in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptFrom=relationship.fromName, conceptTo=relationship.toName) else: self.modelVersReport.error("verrelse:invalidRelationshipReference", _("%(event)s %(relSet)s no relationship found fromConcept %(conceptFrom)s in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptFrom=relationship.fromName) ''' # check instance aspect changes for iaChange in versReport.instanceAspectChanges: # validate related concepts for aspectName in ("{http://xbrl.org/2010/versioning-instance-aspects}concept", "{http://xbrl.org/2010/versioning-instance-aspects}member"): for aspectElt in iaChange.iterdescendants(aspectName): # check link attribute link = aspectElement.get("link") if link is not None: iaChange.hrefToModelObject(link, dts) ''' self.close()
def linkbaseDiscover(self, linkbaseElement, inInstance=False): for lbElement in linkbaseElement.iterchildren(): if isinstance(lbElement,ModelObject): lbLn = lbElement.localName lbNs = lbElement.namespaceURI if lbNs == XbrlConst.link: if lbLn == "roleRef" or lbLn == "arcroleRef": href = self.discoverHref(lbElement) if href is None: self.modelXbrl.error("xbrl:hrefMissing", _("Linkbase reference for %(linkbaseRefElement)s href attribute missing or malformed"), modelObject=lbElement, linkbaseRefElement=lbLn) else: self.hrefObjects.append(href) continue if lbElement.get("{http://www.w3.org/1999/xlink}type") == "extended": if isinstance(lbElement, ModelLink): self.schemalocateElementNamespace(lbElement) arcrolesFound = set() dimensionArcFound = False formulaArcFound = False tableRenderingArcFound = False linkQn = qname(lbElement) linkrole = lbElement.get("{http://www.w3.org/1999/xlink}role") isStandardExtLink = XbrlConst.isStandardResourceOrExtLinkElement(lbElement) if inInstance: #index footnote links even if no arc children baseSetKeys = (("XBRL-footnotes",None,None,None), ("XBRL-footnotes",linkrole,None,None)) for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[baseSetKey].append(lbElement) for linkElement in lbElement.iterchildren(): if isinstance(linkElement,ModelObject): self.schemalocateElementNamespace(linkElement) xlinkType = linkElement.get("{http://www.w3.org/1999/xlink}type") modelResource = None if xlinkType == "locator": nonDTS = linkElement.namespaceURI != XbrlConst.link or linkElement.localName != "loc" # only link:loc elements are discovered or processed href = self.discoverHref(linkElement, nonDTS=nonDTS) if href is None: if isStandardExtLink: self.modelXbrl.error("xbrl:hrefMissing", _("Locator href attribute missing or malformed in standard extended link"), modelObejct=linkElement) else: self.modelXbrl.warning("arelle:hrefWarning", _("Locator href attribute missing or malformed in non-standard extended link"), modelObejct=linkElement) else: linkElement.modelHref = href modelResource = linkElement elif xlinkType == "arc": arcQn = qname(linkElement) arcrole = linkElement.get("{http://www.w3.org/1999/xlink}arcrole") if arcrole not in arcrolesFound: if linkrole == "": linkrole = XbrlConst.defaultLinkRole #index by both arcrole and linkrole#arcrole and dimensionsions if applicable baseSetKeys = [(arcrole, linkrole, linkQn, arcQn)] baseSetKeys.append((arcrole, linkrole, None, None)) baseSetKeys.append((arcrole, None, None, None)) if XbrlConst.isDimensionArcrole(arcrole) and not dimensionArcFound: baseSetKeys.append(("XBRL-dimensions", None, None, None)) baseSetKeys.append(("XBRL-dimensions", linkrole, None, None)) dimensionArcFound = True if XbrlConst.isFormulaArcrole(arcrole) and not formulaArcFound: baseSetKeys.append(("XBRL-formulae", None, None, None)) baseSetKeys.append(("XBRL-formulae", linkrole, None, None)) formulaArcFound = True if XbrlConst.isTableRenderingArcrole(arcrole) and not tableRenderingArcFound: baseSetKeys.append(("Table-rendering", None, None, None)) baseSetKeys.append(("Table-rendering", linkrole, None, None)) tableRenderingArcFound = True self.modelXbrl.hasTableRendering = True for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[baseSetKey].append(lbElement) arcrolesFound.add(arcrole) elif xlinkType == "resource": # create resource and make accessible by id for document modelResource = linkElement if modelResource is not None: lbElement.labeledResources[linkElement.get("{http://www.w3.org/1999/xlink}label")] \ .append(modelResource) else: self.modelXbrl.error("xbrl:schemaDefinitionMissing", _("Linkbase extended link %(element)s missing schema definition"), modelObject=lbElement, element=lbElement.prefixedName)
def checkFilingDTS(val, modelDocument, isEFM, isGFM, visited): global targetNamespaceDatePattern, efmFilenamePattern, htmlFileNamePattern, roleTypePattern, arcroleTypePattern, \ arcroleDefinitionPattern, namePattern, linkroleDefinitionBalanceIncomeSheet if targetNamespaceDatePattern is None: targetNamespaceDatePattern = re.compile( r"/([12][0-9]{3})-([01][0-9])-([0-3][0-9])|" r"/([12][0-9]{3})([01][0-9])([0-3][0-9])|") efmFilenamePattern = re.compile( r"^[a-z0-9][a-zA-Z0-9_\.\-]*(\.xsd|\.xml|\.htm)$") htmlFileNamePattern = re.compile( r"^[a-zA-Z0-9][._a-zA-Z0-9-]*(\.htm)$") roleTypePattern = re.compile(r"^.*/role/[^/\s]+$") arcroleTypePattern = re.compile(r"^.*/arcrole/[^/\s]+$") arcroleDefinitionPattern = re.compile( r"^.*[^\\s]+.*$") # at least one non-whitespace character namePattern = re.compile( "[][()*+?\\\\/^{}|@#%^=~`\"';:,<>&$\u00a3\u20ac]" ) # u20ac=Euro, u00a3=pound sterling linkroleDefinitionBalanceIncomeSheet = re.compile( r"[^-]+-\s+Statement\s+-\s+.*(income|balance|financial\W+position)", re.IGNORECASE) nonDomainItemNameProblemPattern = re.compile( r"({0})|(FirstQuarter|SecondQuarter|ThirdQuarter|FourthQuarter|[1-4]Qtr|Qtr[1-4]|ytd|YTD|HalfYear)(?:$|[A-Z\W])" .format(re.sub(r"\W", "", (val.entityRegistrantName or "").title()))) visited.append(modelDocument) for referencedDocument, modelDocumentReference in modelDocument.referencesDocument.items( ): #6.07.01 no includes if modelDocumentReference.referenceType == "include": val.modelXbrl.error( ("EFM.6.07.01", "GFM.1.03.01"), _("Taxonomy schema %(schema)s includes %(include)s, only import is allowed" ), modelObject=modelDocumentReference.referringModelObject, schema=modelDocument.basename, include=referencedDocument.basename) if referencedDocument not in visited and ( referencedDocument.inDTS or referencedDocument.type == ModelDocument.Type.INLINEXBRLDOCUMENTSET ): # ignore EdgarRenderer added non-DTS documents checkFilingDTS(val, referencedDocument, isEFM, isGFM, visited) if modelDocument.type == ModelDocument.Type.INLINEXBRLDOCUMENTSET: return # nothing to check in inline document set surrogate parent if val.disclosureSystem.standardTaxonomiesDict is None: pass if isEFM: if modelDocument.uri in val.disclosureSystem.standardTaxonomiesDict: if modelDocument.targetNamespace: # check for duplicates of us-types, dei, and rr taxonomies match = standardNamespacesPattern.match( modelDocument.targetNamespace) if match is not None: conflictClass = match.group(2) or match.group(5) if (conflictClass == 'us-gaap' and match.group(3) < '2018') or conflictClass == 'srt': val.standardNamespaceConflicts['srt+us-gaap'].add( modelDocument ) # ignore non-srt multi-usgaap in Filing.py if conflictClass == 'us-gaap' or conflictClass == 'ifrs-full': val.standardNamespaceConflicts['ifrs+us-gaap'].add( modelDocument) if conflictClass not in ('us-gaap', 'srt'): val.standardNamespaceConflicts[conflictClass].add( modelDocument) else: if len(modelDocument.basename) > 32: val.modelXbrl.error( "EFM.5.01.01.tooManyCharacters", _("Document file name %(filename)s must not exceed 32 characters." ), edgarCode="cp-0501-File-Name-Length", modelObject=modelDocument, filename=modelDocument.basename) if modelDocument.type != ModelDocument.Type.INLINEXBRLDOCUMENTSET: if modelDocument.type == ModelDocument.Type.INLINEXBRL: _pattern = htmlFileNamePattern _suffix = ".htm" else: _pattern = efmFilenamePattern _suffix = ".xsd or .xml" if not _pattern.match(modelDocument.basename): val.modelXbrl.error( "EFM.5.01.01", _("Document file name %(filename)s must start with a-z or 0-9, contain upper or lower case letters, ., -, _, and end with %(suffix)s." ), edgarCode="cp-0501-File-Name", modelObject=modelDocument, filename=modelDocument.basename, suffix=_suffix) if (modelDocument.type == ModelDocument.Type.SCHEMA and modelDocument.targetNamespace not in val.disclosureSystem.baseTaxonomyNamespaces and modelDocument.uri.startswith(val.modelXbrl.uriDir)): val.hasExtensionSchema = True # check schema contents types # 6.7.3 check namespace for standard authority targetNamespaceAuthority = UrlUtil.authority( modelDocument.targetNamespace) if targetNamespaceAuthority in val.disclosureSystem.standardAuthorities: val.modelXbrl.error( ("EFM.6.07.03", "GFM.1.03.03"), _("The target namespace, %(targetNamespace)s cannot have the same authority (%(targetNamespaceAuthority)s) as a standard " "taxonomy, in %(schema)s. Please change your target namespace." ), edgarCode="fs-0703-Extension-Has-Standard-Namespace-Authority", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, targetNamespaceAuthority=UrlUtil.authority( modelDocument.targetNamespace, includeScheme=False)) # 6.7.4 check namespace format if modelDocument.targetNamespace is None or not modelDocument.targetNamespace.startswith( "http://"): match = None else: targetNamespaceDate = modelDocument.targetNamespace[ len(targetNamespaceAuthority):] match = targetNamespaceDatePattern.match(targetNamespaceDate) if match is not None: try: if match.lastindex == 3: date = datetime.date(int(match.group(1)), int(match.group(2)), int(match.group(3))) elif match.lastindex == 6: date = datetime.date(int(match.group(4)), int(match.group(5)), int(match.group(6))) else: match = None except ValueError: match = None if match is None: val.modelXbrl.error( ("EFM.6.07.04", "GFM.1.03.04"), _("You did not adhere to the requirements for target namespace, for %(targetNamespace)s in %(schema)s. " "Please recheck your submission and adhere to the target namespace requirements." ), edgarCode="cp-0704-Taxonomy-Valid-Target-Namespace", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace) elif val.fileNameDate and date > val.fileNameDate: val.modelXbrl.info( ("EFM.6.07.06", "GFM.1.03.06"), _("Warning: Taxonomy schema %(schema)s namespace %(targetNamespace)s has date later than document name date %(docNameDate)s" ), modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, docNameDate=val.fileNameDate) if modelDocument.targetNamespace is not None: # 6.7.5 check prefix for _ authority = UrlUtil.authority(modelDocument.targetNamespace) if not re.match(r"(http://|https://|ftp://|urn:)\w+", authority): val.modelXbrl.error( ("EFM.6.07.05", "GFM.1.03.05"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s must be a valid URI with a valid authority for the namespace." ), edgarCode="du-0705-Namespace-Authority", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace) # may be multiple prefixes for namespace prefixes = [ (prefix if prefix is not None else "") for prefix, NS in modelDocument.xmlRootElement.nsmap.items() if NS == modelDocument.targetNamespace ] if not prefixes: prefix = None val.modelXbrl.error( ("EFM.6.07.07", "GFM.1.03.07"), _("The schema does not supply a prefix for %(targetNamespace)s without an underscore character, in file %(schema)s. " "Please provide or change a prefix"), edgarCode="du-0707-Recommended-Prefix-Disallowed", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace) else: prefix = prefixes[0] if len(prefixes) > 1: val.modelXbrl.error( ("EFM.6.07.07", "GFM.1.03.07"), _("The schema does not supply a prefix for %(targetNamespace)s without an underscore character, in file %(schema)s. " "Please provide or change a prefix"), edgarCode="du-0707-Recommended-Prefix-Disallowed", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, prefix=", ".join(prefixes)) elif "_" in prefix: val.modelXbrl.error( ("EFM.6.07.07", "GFM.1.03.07"), _("The schema does not supply a prefix for %(targetNamespace)s without an underscore character, in file %(schema)s. " "Please provide or change a prefix"), edgarCode="du-0707-Recommended-Prefix-Disallowed", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, prefix=prefix) for modelConcept in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.w3.org/2001/XMLSchema}element"): if isinstance(modelConcept, ModelConcept): # 6.7.16 name not duplicated in standard taxonomies name = modelConcept.get("name") if name is None: name = "" if modelConcept.get("ref") is not None: continue # don't validate ref's here for c in val.modelXbrl.nameConcepts.get(name, []): if c.modelDocument != modelDocument: if not c.modelDocument.uri.startswith( val.modelXbrl.uriDir): val.modelXbrl.error( ("EFM.6.07.16", "GFM.1.03.18"), _("Your extension taxonomy contains an element, %(concept)s, which has the same name as an element in the base taxonomy, " "%(standardConcept)s. Please ensure that this extension is appropriate and if so, please change the extension concept name." ), edgarCode= "cp-0716-Element-Name-Same-As-Base", modelObject=(modelConcept, c), concept=modelConcept.qname, standardSchema=c.modelDocument.basename, standardConcept=c.qname) # 6.7.17 id properly formed _id = modelConcept.id requiredId = (prefix if prefix is not None else "") + "_" + name if _id != requiredId: val.modelXbrl.error( ("EFM.6.07.17", "GFM.1.03.19"), _("You did not adhere to the declarations for concepts by containing an 'id' attribute whose value begins with the recommended " "namespace prefix of the taxonomy, followed by an underscore, followed by an element name, for the concept %(concept)s. " "Please recheck your submission."), edgarCode="cp-0717-Element-Id", modelObject=modelConcept, concept=modelConcept.qname, id=_id, requiredId=requiredId) # 6.7.18 nillable is true nillable = modelConcept.get("nillable") if nillable != "true" and modelConcept.isItem: val.modelXbrl.error( ("EFM.6.07.18", "GFM.1.03.20"), _("Element %(concept)s is declared without a 'true' value for the nillable attribute. Please set the value to 'true'." ), edgarCode="du-0718-Nillable-Not-True", modelObject=modelConcept, schema=modelDocument.basename, concept=name, nillable=nillable) # 6.7.19 not tuple if modelConcept.isTuple: val.modelXbrl.error( ("EFM.6.07.19", "GFM.1.03.21"), _("You provided an extension concept which is a tuple, %(concept)s. Please remove tuples and check your submission." ), edgarCode="cp-0719-No-Tuple-Element", modelObject=modelConcept, concept=modelConcept.qname) # 6.7.20 no typed domain ref if modelConcept.isTypedDimension: val.modelXbrl.error( ("EFM.6.07.20", "GFM.1.03.22"), _("There is an xbrldt:typedDomainRef attribute on %(concept)s (%(typedDomainRef)s). Please remove it." ), edgarCode="du-0720-Typed-Domain-Ref-Disallowed", modelObject=modelConcept, concept=modelConcept.qname, typedDomainRef=modelConcept.typedDomainElement. qname if modelConcept.typedDomainElement is not None else modelConcept.typedDomainRef) # 6.7.21 abstract must be duration isDuration = modelConcept.periodType == "duration" if modelConcept.isAbstract and not isDuration: val.modelXbrl.error( ("EFM.6.07.21", "GFM.1.03.23"), _("Element %(concept)s is declared as an abstract item with period type 'instant'. Please change it to 'duration' or " "make the element not abstract."), edgarCode="du-0721-Abstract-Is-Instant", modelObject=modelConcept, schema=modelDocument.basename, concept=modelConcept.qname) # 6.7.22 abstract must be stringItemType ''' removed SEC EFM v.17, Edgar release 10.4, and GFM 2011-04-08 if modelConcept.abstract == "true" and modelConcept.typeQname != XbrlConst. qnXbrliStringItemType: val.modelXbrl.error(("EFM.6.07.22", "GFM.1.03.24"), _("Concept %(concept)s is abstract but type is not xbrli:stringItemType"), modelObject=modelConcept, concept=modelConcept.qname) ''' substitutionGroupQname = modelConcept.substitutionGroupQname # 6.7.23 Axis must be subs group dimension if name.endswith("Axis") ^ ( substitutionGroupQname == XbrlConst.qnXbrldtDimensionItem): val.modelXbrl.error( ("EFM.6.07.23", "GFM.1.03.25"), _("The substitution group 'xbrldt:dimensionItem' is only consistent with an element name that ends with 'Axis'. " "Please change %(conceptLocalName)s or change the substitutionGroup." ), edgarCode="du-0723-Axis-Dimension-Name-Mismatch", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.24 Table must be subs group hypercube if name.endswith("Table") ^ ( substitutionGroupQname == XbrlConst.qnXbrldtHypercubeItem): val.modelXbrl.error( ("EFM.6.07.24", "GFM.1.03.26"), _("The substitution group 'xbrldt:hypercubeItem' is only allowed with an element name that ends with 'Table'. " "Please change %(conceptLocalName)s or change the substitutionGroup." ), edgarCode="du-0724-Table-Hypercube-Name-Mismatch", modelObject=modelConcept, schema=modelDocument.basename, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.25 if neither hypercube or dimension, substitution group must be item if substitutionGroupQname not in ( None, XbrlConst.qnXbrldtDimensionItem, XbrlConst.qnXbrldtHypercubeItem, XbrlConst.qnXbrliItem): val.modelXbrl.error( ("EFM.6.07.25", "GFM.1.03.27"), _("The substitution group attribute value %(substitutionGroup)s of element %(conceptLocalName)s is not allowed. " "Please change it to one of 'xbrli:item', 'xbrldt:dimensionItem' or 'xbrldt:hypercubeItem'." ), edgarCode="du-0725-Substitution-Group-Custom", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName, substitutionGroup=modelConcept. substitutionGroupQname) # 6.7.26 Table must be subs group hypercube if name.endswith( "LineItems") and modelConcept.abstract != "true": val.modelXbrl.error( ("EFM.6.07.26", "GFM.1.03.28"), _("The element %(conceptLocalName)s ends with 'LineItems' but is not abstract. Please change %(conceptLocalName)s or " "the value of the 'abstract' attribute."), edgarCode= "du-0726-LineItems-Abstract-Name-Mismatch", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.27 type domainMember must end with Domain or Member conceptType = modelConcept.type isDomainItemType = conceptType is not None and conceptType.isDomainItemType endsWithDomainOrMember = name.endswith( "Domain") or name.endswith("Member") if isDomainItemType != endsWithDomainOrMember: val.modelXbrl.error( ("EFM.6.07.27", "GFM.1.03.29"), _("The type 'us-types:domainItemType' is only allowed with an element name that ends with 'Domain' or 'Member'. " "Please change %(conceptLocalName)s or change the type." ), edgarCode="du-0727-Domain-Type-Name-Mismatch", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.28 domainItemType must be duration if isDomainItemType and not isDuration: val.modelXbrl.error( ("EFM.6.07.28", "GFM.1.03.30"), _("Element %(conceptLocalName)s is declared as a us-types:domainItemType with period type 'instant'. " "Please change it to 'duration' or change the item type." ), edgarCode="du-0728-Domain-Member-Is-Instant", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) #6.7.31 (version 27) fractions if modelConcept.isFraction: val.modelXbrl.error( "EFM.6.07.31", _("Element %(concept)s is declared as a fraction item type. Change or remove the declaration." ), edgarCode="du-0731-Fraction-Item-Type", modelObject=modelConcept, concept=modelConcept.qname) #6.7.32 (version 27) instant non numeric if modelConcept.isItem and (not modelConcept.isNumeric and not isDuration and not modelConcept.isAbstract and not isDomainItemType): val.modelXbrl.error( "EFM.6.07.32", _("Declaration of element %(concept)s in %(schema)s must have xbrli:periodType of 'duration' because its base type is not numeric." ), edgarCode="rq-0732-Nonnnumeric-Must-Be-Duration", modelObject=modelConcept, schema=modelDocument.basename, concept=modelConcept.qname) # 6.8.5 semantic check, check LC3 name if name: if not name[0].isupper(): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.05.firstLetter", "GFM.2.03.05.firstLetter"), _("Concept %(concept)s name must start with a capital letter" ), modelObject=modelConcept, concept=modelConcept.qname) if namePattern.search(name): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.05.disallowedCharacter", "GFM.2.03.05.disallowedCharacter"), _("Concept %(concept)s has disallowed name character" ), modelObject=modelConcept, concept=modelConcept.qname) if len(name) > 200: val.modelXbrl.log( "ERROR-SEMANTIC", "EFM.6.08.05.nameLength", _("Concept %(concept)s name length %(namelength)s exceeds 200 characters" ), modelObject=modelConcept, concept=modelConcept.qname, namelength=len(name)) if isEFM: label = modelConcept.label(lang="en-US", fallbackToQname=False) if label: # allow Joe's Bar, N.A. to be JoesBarNA -- remove ', allow A. as not article "a" lc3name = ''.join( re.sub(r"['.-]", "", ( w[0] or w[2] or w[3] or w[4])).title() for w in re.findall( r"((\w+')+\w+)|(A[.-])|([.-]A(?=\W|$))|(\w+)", label ) # EFM implies this should allow - and . re.findall(r"[\w\-\.]+", label) if w[4].lower() not in ("the", "a", "an")) if not (name == lc3name or (name and lc3name and lc3name[0].isdigit() and name[1:] == lc3name and (name[0].isalpha() or name[0] == '_'))): val.modelXbrl.log( "WARNING-SEMANTIC", "EFM.6.08.05.LC3", _("Concept %(concept)s should match expected LC3 composition %(lc3name)s" ), modelObject=modelConcept, concept=modelConcept.qname, lc3name=lc3name) if conceptType is not None: # 6.8.6 semantic check if not isDomainItemType and conceptType.qname != XbrlConst.qnXbrliDurationItemType: nameProblems = nonDomainItemNameProblemPattern.findall( name) if any( any(t) for t in nameProblems ): # list of tuples with possibly nonempty strings val.modelXbrl.log( "WARNING-SEMANTIC", ("EFM.6.08.06", "GFM.2.03.06"), _("Concept %(concept)s should not contain company or period information, found: %(matches)s" ), modelObject=modelConcept, concept=modelConcept.qname, matches=", ".join(''.join(t) for t in nameProblems)) if conceptType.qname == XbrlConst.qnXbrliMonetaryItemType: if not modelConcept.balance: # 6.8.11 may not appear on a income or balance statement if any( linkroleDefinitionBalanceIncomeSheet. match(roleType.definition) for rel in val.modelXbrl. relationshipSet(XbrlConst.parentChild). toModelObject(modelConcept) for roleType in val.modelXbrl. roleTypes.get(rel.linkrole, ())): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.11", "GFM.2.03.11"), _("Concept %(concept)s must have a balance because it appears in a statement of income or balance sheet" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.11.5 semantic check, must have a documentation label stdLabel = modelConcept.label( lang="en-US", fallbackToQname=False) defLabel = modelConcept.label( preferredLabel=XbrlConst. documentationLabel, lang="en-US", fallbackToQname=False) if not defLabel or ( # want different words than std label stdLabel and re.findall(r"\w+", stdLabel) == re.findall(r"\w+", defLabel)): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.11.05", "GFM.2.04.04"), _("Concept %(concept)s is monetary without a balance and must have a documentation label that disambiguates its sign" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.16 semantic check if conceptType.qname == XbrlConst.qnXbrliDateItemType and modelConcept.periodType != "duration": val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.16", "GFM.2.03.16"), _("Concept %(concept)s of type xbrli:dateItemType must have periodType duration" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.17 semantic check if conceptType.qname == XbrlConst.qnXbrliStringItemType and modelConcept.periodType != "duration": val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.17", "GFM.2.03.17"), _("Concept %(concept)s of type xbrli:stringItemType must have periodType duration" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.8 check for embedded linkbase for e in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.xbrl.org/2003/linkbase}linkbase"): if isinstance(e, ModelObject): val.modelXbrl.error( ("EFM.6.07.08", "GFM.1.03.08"), _("Your filing contained embedded linkbases in %(schema)s. Please recheck your submission and remove all embedded linkbases." ), edgarCode="cp-0708-No-Embedded-Linkbases", modelObject=e, schema=modelDocument.basename) break requiredUsedOns = { XbrlConst.qnLinkPresentationLink, XbrlConst.qnLinkCalculationLink, XbrlConst.qnLinkDefinitionLink } standardUsedOns = { XbrlConst.qnLinkLabel, XbrlConst.qnLinkReference, XbrlConst.qnLinkDefinitionArc, XbrlConst.qnLinkCalculationArc, XbrlConst.qnLinkPresentationArc, XbrlConst.qnLinkLabelArc, XbrlConst.qnLinkReferenceArc, # per WH, private footnote arc and footnore resource roles are not allowed XbrlConst.qnLinkFootnoteArc, XbrlConst.qnLinkFootnote, } # 6.7.9 role types authority for e in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.xbrl.org/2003/linkbase}roleType"): if isinstance(e, ModelObject): roleURI = e.get("roleURI") if targetNamespaceAuthority != UrlUtil.authority(roleURI): val.modelXbrl.error( ("EFM.6.07.09", "GFM.1.03.09"), _("Role %(roleType)s does not begin with %(targetNamespace)s's scheme and authority. " "Please change the role URI or target namespace URI." ), edgarCode="du-0709-Role-Namespace-Mismatch", modelObject=e, roleType=roleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.9 end with .../role/lc3 name if not roleTypePattern.match(roleURI): val.modelXbrl.warning( ("EFM.6.07.09.roleEnding", "GFM.1.03.09"), "RoleType %(roleType)s should end with /role/{LC3name}", modelObject=e, roleType=roleURI) # 6.7.10 only one role type declaration in DTS modelRoleTypes = val.modelXbrl.roleTypes.get(roleURI) if modelRoleTypes is not None: modelRoleType = modelRoleTypes[0] definition = modelRoleType.definitionNotStripped usedOns = modelRoleType.usedOns if len(modelRoleTypes) == 1: # 6.7.11 used on's for pre, cal, def if any has a used on if not usedOns.isdisjoint(requiredUsedOns) and len( requiredUsedOns - usedOns) > 0: val.modelXbrl.error( ("EFM.6.07.11", "GFM.1.03.11"), _("The role %(roleType)s did not provide a usedOn element for all three link types (presentation, " "calculation and definition), missing %(usedOn)s. Change the declaration to be for all three types of link, and resubmit." ), edgarCode= "du-0711-Role-Type-Declaration-Incomplete", modelObject=e, roleType=roleURI, usedOn=requiredUsedOns - usedOns) # 6.7.12 definition match pattern if (val.disclosureSystem.roleDefinitionPattern is not None and (definition is None or not val.disclosureSystem. roleDefinitionPattern.match(definition))): val.modelXbrl.error( ("EFM.6.07.12", "GFM.1.03.12-14"), _("The definition '%(definition)s' of role %(roleType)s does not match the expected format. " "Please check that the definition matches {number} - {type} - {text}." ), edgarCode="rq-0712-Role-Definition-Mismatch", modelObject=e, roleType=roleURI, definition=(definition or "")) if usedOns & standardUsedOns: # semantics check val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("RoleType %(roleuri)s is defined using role types already defined by standard roles for: %(qnames)s" ), modelObject=e, roleuri=roleURI, qnames=', '.join( str(qn) for qn in usedOns & standardUsedOns)) # 6.7.13 arcrole types authority for e in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.xbrl.org/2003/linkbase}arcroleType"): if isinstance(e, ModelObject): arcroleURI = e.get("arcroleURI") if targetNamespaceAuthority != UrlUtil.authority(arcroleURI): val.modelXbrl.error( ("EFM.6.07.13", "GFM.1.03.15"), _("Relationship role %(arcroleType)s does not begin with %(targetNamespace)s's scheme and authority. " "Please change the relationship role URI or target namespace URI." ), edgarCode="du-0713-Arcrole-Namespace-Mismatch", modelObject=e, arcroleType=arcroleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.13 end with .../arcrole/lc3 name if not arcroleTypePattern.match(arcroleURI): val.modelXbrl.warning( ("EFM.6.07.13.arcroleEnding", "GFM.1.03.15"), _("ArcroleType %(arcroleType)s should end with /arcrole/{LC3name}" ), modelObject=e, arcroleType=arcroleURI) # 6.7.15 definition match pattern modelRoleTypes = val.modelXbrl.arcroleTypes[arcroleURI] definition = modelRoleTypes[0].definition if definition is None or not arcroleDefinitionPattern.match( definition): val.modelXbrl.error( ("EFM.6.07.15", "GFM.1.03.17"), _("Relationship role declaration %(arcroleType)s is missing a definition. Please provide a definition." ), edgarCode="du-0715-Arcrole-Definition-Missing", modelObject=e, arcroleType=arcroleURI) # semantic checks usedOns = modelRoleTypes[0].usedOns if usedOns & standardUsedOns: # semantics check val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("ArcroleType %(arcroleuri)s is defined using role types already defined by standard arcroles for: %(qnames)s" ), modelObject=e, arcroleuri=arcroleURI, qnames=', '.join( str(qn) for qn in usedOns & standardUsedOns)) #6.3.3 filename check m = re.match(r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9]).xsd$", modelDocument.basename) if m: try: # check date value datetime.datetime.strptime(m.group(1), "%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}.xsd".format( val.fileNameBasePart, val.fileNameDatePart) if modelDocument.basename != expectedFilename: val.modelXbrl.log( "WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Schema file name warning: %(filename)s, should match %(expectedFilename)s' ), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid schema file base name part (date) in "{base}-{yyyymmdd}.xsd": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid schema file name, must match "{base}-{yyyymmdd}.xsd": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) elif modelDocument.type == ModelDocument.Type.LINKBASE: # if it is part of the submission (in same directory) check name labelRels = None if modelDocument.filepath.startswith( val.modelXbrl.modelDocument.filepathdir): #6.3.3 filename check extLinkElt = XmlUtil.descendant( modelDocument.xmlRootElement, XbrlConst.link, "*", "{http://www.w3.org/1999/xlink}type", "extended") if extLinkElt is None: # no ext link element val.modelXbrl.error( (val.EFM60303 + ".noLinkElement", "GFM.1.01.01.noLinkElement"), _('Invalid linkbase file name: %(filename)s, has no extended link element, cannot determine link type.' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03.noLinkElement", "EFM.6.23.01.noLinkElement", "GFM.1.01.01.noLinkElement")) elif extLinkElt.localName not in extLinkEltFileNameEnding: val.modelXbrl.error( "EFM.6.03.02", _('Invalid linkbase link element %(linkElement)s in %(filename)s' ), modelObject=modelDocument, linkElement=extLinkElt.localName, filename=modelDocument.basename) else: m = re.match( r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9])(_[a-z]{3}).xml$", modelDocument.basename) expectedSuffix = extLinkEltFileNameEnding[extLinkElt.localName] if m and m.group(2) == expectedSuffix: try: # check date value datetime.datetime.strptime(m.group(1), "%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}{2}.xml".format( val.fileNameBasePart, val.fileNameDatePart, expectedSuffix) if modelDocument.basename != expectedFilename: val.modelXbrl.log( "WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Linkbase name warning: %(filename)s should match %(expectedFilename)s' ), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase base file name part (date) in "{base}-{yyyymmdd}_{suffix}.xml": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase name, must match "{base}-{yyyymmdd}%(expectedSuffix)s.xml": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, expectedSuffix=expectedSuffix, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) if extLinkElt.localName == "labelLink": if labelRels is None: labelRels = val.modelXbrl.relationshipSet( XbrlConst.conceptLabel) for labelElt in XmlUtil.children(extLinkElt, XbrlConst.link, "label"): # 6.10.9 if XbrlConst.isNumericRole(labelElt.role): for rel in labelRels.toModelObject(labelElt): if rel.fromModelObject is not None and not rel.fromModelObject.isNumeric: val.modelXbrl.error( "EFM.6.10.09", _("Non-numeric element %(concept)s has a label role for numeric elements: %(role)s. " "Please change the role attribute."), edgarCode="du-1009-Numeric-Label-Role", modelObject=(labelElt, rel.fromModelObject), concept=rel.fromModelObject.qname, role=labelElt.role)
def init(modelXbrl): # setup modelXbrl for rendering evaluation # dimension defaults required in advance of validation from arelle import ValidateXbrlDimensions, ValidateFormula, ModelDocument ValidateXbrlDimensions.loadDimensionDefaults(modelXbrl) hasXbrlTables = False # validate table linkbase dimensions for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname and XbrlConst.isTableRenderingArcrole(arcrole): ValidateFormula.checkBaseSet(modelXbrl, arcrole, ELR, modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname)) if arcrole in (XbrlConst.tableBreakdown, XbrlConst.tableAxis2011): hasXbrlTables = True # provide context for view if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE: instance = None # use instance of the entry pont else: # need dummy instance instance = ModelDocument.create(modelXbrl, ModelDocument.Type.INSTANCE, "dummy.xml", # fake URI and fake schemaRef ("http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd",)) if hasXbrlTables: # formula processor is needed for 2011 XBRL tables but not for 2010 Eurofiling tables modelXbrl.rendrCntx = XPathContext.create(modelXbrl, instance) modelXbrl.profileStat(None) # setup fresh parameters from formula optoins modelXbrl.parameters = modelXbrl.modelManager.formulaOptions.typedParameters() # validate parameters and custom function signatures ValidateFormula.validate(modelXbrl, xpathContext=modelXbrl.rendrCntx, parametersOnly=True, statusMsg=_("compiling rendering tables")) # check and extract message expressions into compilable programs for msgArcrole in (XbrlConst.tableDefinitionNodeMessage, XbrlConst.tableDefinitionNodeSelectionMessage, XbrlConst.tableAxisMessage2011, XbrlConst.tableAxisSelectionMessage2011): for msgRel in modelXbrl.relationshipSet(msgArcrole).modelRelationships: ValidateFormula.checkMessageExpressions(modelXbrl, msgRel.toModelObject) # compile and validate tables for modelTable in modelXbrl.modelRenderingTables: modelTable.fromInstanceQnames = None # required if referred to by variables scope chaining modelTable.compile() # check aspectModel if modelTable.aspectModel not in ("non-dimensional", "dimensional"): modelXbrl.error("xbrlte:unknownAspectModel", _("Table %(xlinkLabel)s, aspect model %(aspectModel)s not recognized"), modelObject=modelTable, xlinkLabel=modelTable.xlinkLabel, aspectModel=modelTable.aspectModel) else: modelTable.priorAspectAxisDisposition = {} # check ordinate aspects against aspectModel oppositeAspectModel = (_DICT_SET({'dimensional','non-dimensional'}) - _DICT_SET({modelTable.aspectModel})).pop() uncoverableAspects = aspectModels[oppositeAspectModel] - aspectModels[modelTable.aspectModel] for tblAxisRel in modelXbrl.relationshipSet((XbrlConst.tableBreakdown,XbrlConst.tableAxis2011)).fromModelObject(modelTable): checkDefinitionNodeAspectModel(modelXbrl, modelTable, tblAxisRel, uncoverableAspects) del modelTable.priorAspectAxisDisposition modelXbrl.profileStat(_("compileTables"))
def linkbaseDiscover(self, linkbaseElement, inInstance=False): for lbElement in linkbaseElement.childNodes: if lbElement.nodeType == 1: #element lbLn = lbElement.localName lbNs = lbElement.namespaceURI if lbNs == XbrlConst.link: if lbLn == "roleRef" or lbLn == "arcroleRef": href = self.discoverHref(lbElement) if href is None: self.modelXbrl.error( "Linkbase in {0} {1} href attribute missing or malformed".format( os.path.basename(self.uri), lbLn), "err", "xbrl:hrefMissing") else: self.hrefObjects.append(href) continue if lbElement.getAttributeNS(XbrlConst.xlink, "type") == "extended": self.schemalocateElementNamespace(lbElement) arcrolesFound = set() dimensionArcFound = False formulaArcFound = False euRenderingArcFound = False linkQn = qname(lbElement) linkrole = lbElement.getAttributeNS(XbrlConst.xlink, "role") modelLink = ModelObject.createLink(self, lbElement) if inInstance: #index footnote links even if no arc children baseSetKeys = (("XBRL-footnotes",None,None,None), ("XBRL-footnotes",linkrole,None,None)) for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[baseSetKey].append(modelLink) for linkElement in lbElement.childNodes: if linkElement.nodeType == 1: #element self.schemalocateElementNamespace(linkElement) xlinkType = linkElement.getAttributeNS(XbrlConst.xlink, "type") modelResource = None if xlinkType == "locator": nonDTS = linkElement.namespaceURI != XbrlConst.link or linkElement.localName != "loc" # only link:loc elements are discovered or processed href = self.discoverHref(linkElement, nonDTS=nonDTS) if href is None: self.modelXbrl.error( "Linkbase in {0} {1} href attribute missing or malformed".format( os.path.basename(self.uri), lbLn), "err", "xbrl:hrefMissing") else: modelResource = ModelObject.createLocator(self, linkElement, href) elif xlinkType == "arc": arcQn = qname(linkElement) arcrole = linkElement.getAttributeNS(XbrlConst.xlink, "arcrole") if arcrole not in arcrolesFound: if linkrole == "": linkrole = XbrlConst.defaultLinkRole #index by both arcrole and linkrole#arcrole and dimensionsions if applicable baseSetKeys = [(arcrole, linkrole, linkQn, arcQn)] baseSetKeys.append((arcrole, linkrole, None, None)) baseSetKeys.append((arcrole, None, None, None)) if XbrlConst.isDimensionArcrole(arcrole) and not dimensionArcFound: baseSetKeys.append(("XBRL-dimensions", None, None, None)) baseSetKeys.append(("XBRL-dimensions", linkrole, None, None)) dimensionArcFound = True if XbrlConst.isFormulaArcrole(arcrole) and not formulaArcFound: baseSetKeys.append(("XBRL-formulae", None, None, None)) baseSetKeys.append(("XBRL-formulae", linkrole, None, None)) formulaArcFound = True if XbrlConst.isEuRenderingArcrole(arcrole) and not euRenderingArcFound: baseSetKeys.append(("EU-rendering", None, None, None)) baseSetKeys.append(("EU-rendering", linkrole, None, None)) euRenderingArcFound = True self.modelXbrl.hasEuRendering = True for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[baseSetKey].append(modelLink) arcrolesFound.add(arcrole) elif xlinkType == "resource": # create resource and make accessible by id for document modelResource = ModelObject.createResource(self, linkElement) if modelResource is not None: if linkElement.hasAttribute("id"): self.idObjects[linkElement.getAttribute("id")] = modelResource modelLink.labeledResources[linkElement.getAttributeNS(XbrlConst.xlink, "label")] \ .append(modelResource) else: XmlUtil.markIdAttributes(linkElement)
def checkDTS(val, modelDocument, visited): global targetNamespaceDatePattern, efmFilenamePattern, roleTypePattern, arcroleTypePattern, \ arcroleDefinitionPattern, namePattern, linkroleDefinitionBalanceIncomeSheet, \ namespacesConflictPattern if targetNamespaceDatePattern is None: targetNamespaceDatePattern = re.compile(r"/([12][0-9]{3})-([01][0-9])-([0-3][0-9])|" r"/([12][0-9]{3})([01][0-9])([0-3][0-9])|") efmFilenamePattern = re.compile(r"^[a-z0-9][a-zA-Z0-9_\.\-]*(\.xsd|\.xml)$") roleTypePattern = re.compile(r"^.*/role/[^/\s]+$") arcroleTypePattern = re.compile(r"^.*/arcrole/[^/\s]+$") arcroleDefinitionPattern = re.compile(r"^.*[^\\s]+.*$") # at least one non-whitespace character namePattern = re.compile("[][()*+?\\\\/^{}|@#%^=~`\"';:,<>&$\u00a3\u20ac]") # u20ac=Euro, u00a3=pound sterling linkroleDefinitionBalanceIncomeSheet = re.compile(r"[^-]+-\s+Statement\s+-\s+.*(income|balance|financial\W+position)", re.IGNORECASE) namespacesConflictPattern = re.compile(r"http://(xbrl\.us|fasb\.org|xbrl\.sec\.gov)/(dei|us-types|us-roles|rr)/([0-9]{4}-[0-9]{2}-[0-9]{2})$") nonDomainItemNameProblemPattern = re.compile( r"({0})|(FirstQuarter|SecondQuarter|ThirdQuarter|FourthQuarter|[1-4]Qtr|Qtr[1-4]|ytd|YTD|HalfYear)(?:$|[A-Z\W])" .format(re.sub(r"\W", "", (val.entityRegistrantName or "").title()))) visited.append(modelDocument) definesLabelLinkbase = False for referencedDocument, modelDocumentReference in modelDocument.referencesDocument.items(): #6.07.01 no includes if modelDocumentReference.referenceType == "include": val.modelXbrl.error(("EFM.6.07.01", "GFM.1.03.01", "SBR.NL.2.2.0.18"), _("Taxonomy schema %(schema)s includes %(include)s, only import is allowed"), modelObject=modelDocumentReference.referringModelObject, schema=os.path.basename(modelDocument.uri), include=os.path.basename(referencedDocument.uri)) if referencedDocument not in visited: checkDTS(val, referencedDocument, visited) if val.disclosureSystem.standardTaxonomiesDict is None: pass if val.validateEFM: if modelDocument.uri in val.disclosureSystem.standardTaxonomiesDict: if modelDocument.targetNamespace: # check for duplicates of us-types, dei, and rr taxonomies match = namespacesConflictPattern.match(modelDocument.targetNamespace) if match is not None: val.standardNamespaceConflicts[match.group(2)].add(modelDocument) else: if len(modelDocument.basename) > 32: val.modelXbrl.error("EFM.5.01.01.tooManyCharacters", _("Document file name %(filename)s must not exceed 32 characters."), modelObject=modelDocument, filename=modelDocument.basename) if not efmFilenamePattern.match(modelDocument.basename): val.modelXbrl.error("EFM.5.01.01", _("Document file name %(filename)s must start with a-z or 0-9, contain upper or lower case letters, ., -, _, and end with .xsd or .xml."), modelObject=modelDocument, filename=modelDocument.basename) if (modelDocument.type == ModelDocument.Type.SCHEMA and modelDocument.targetNamespace not in val.disclosureSystem.baseTaxonomyNamespaces and modelDocument.uri.startswith(val.modelXbrl.uriDir)): # check schema contents types if val.validateSBRNL: definesLinkroles = False definesArcroles = False definesLinkParts = False definesAbstractItems = False definesNonabstractItems = False definesConcepts = False definesTuples = False definesPresentationTuples = False definesSpecificationTuples = False definesTypes = False definesEnumerations = False definesDimensions = False definesDomains = False definesHypercubes = False # 6.7.3 check namespace for standard authority targetNamespaceAuthority = UrlUtil.authority(modelDocument.targetNamespace) if targetNamespaceAuthority in val.disclosureSystem.standardAuthorities: val.modelXbrl.error(("EFM.6.07.03", "GFM.1.03.03"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s is a disallowed authority"), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace, targetNamespaceAuthority=UrlUtil.authority(modelDocument.targetNamespace, includeScheme=False)) # 6.7.4 check namespace format if modelDocument.targetNamespace is None or not modelDocument.targetNamespace.startswith("http://"): match = None elif val.validateEFMorGFM: targetNamespaceDate = modelDocument.targetNamespace[len(targetNamespaceAuthority):] match = targetNamespaceDatePattern.match(targetNamespaceDate) else: match = None if match is not None: try: if match.lastindex == 3: date = datetime.date(int(match.group(1)),int(match.group(2)),int(match.group(3))) elif match.lastindex == 6: date = datetime.date(int(match.group(4)),int(match.group(5)),int(match.group(6))) else: match = None except ValueError: match = None if match is None: val.modelXbrl.error(("EFM.6.07.04", "GFM.1.03.04"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s must have format http://{authority}/{versionDate}"), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace) elif val.fileNameDate and date > val.fileNameDate: val.modelXbrl.info(("EFM.6.07.06", "GFM.1.03.06"), _("Warning: Taxonomy schema %(schema)s namespace %(targetNamespace)s has date later than document name date %(docNameDate)s"), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace, docNameDate=val.fileNameDate) if modelDocument.targetNamespace is not None: # 6.7.5 check prefix for _ authority = UrlUtil.authority(modelDocument.targetNamespace) if not re.match(r"(http://|https://|ftp://|urn:)\w+",authority): val.modelXbrl.error(("EFM.6.07.05", "GFM.1.03.05"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s must be a valid URL with a valid authority for the namespace."), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace) prefix = XmlUtil.xmlnsprefix(modelDocument.xmlRootElement,modelDocument.targetNamespace) if not prefix: val.modelXbrl.error(("EFM.6.07.07", "GFM.1.03.07"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s missing prefix for the namespace."), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace) elif "_" in prefix: val.modelXbrl.error(("EFM.6.07.07", "GFM.1.03.07"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s prefix %(prefix)s must not have an '_'"), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace, prefix=prefix) if val.validateSBRNL: genrlSpeclRelSet = val.modelXbrl.relationshipSet(XbrlConst.generalSpecial) for modelConcept in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.w3.org/2001/XMLSchema}element"): if isinstance(modelConcept,ModelConcept): # 6.7.16 name not duplicated in standard taxonomies name = modelConcept.get("name") if name is None: name = "" if modelConcept.get("ref") is not None: continue # don't validate ref's here for c in val.modelXbrl.nameConcepts.get(name, []): if c.modelDocument != modelDocument: if (val.validateEFMorGFM and not c.modelDocument.uri.startswith(val.modelXbrl.uriDir)): val.modelXbrl.error(("EFM.6.07.16", "GFM.1.03.18"), _("Concept %(concept)s is also defined in standard taxonomy schema schema %(standardSchema)s"), modelObject=(modelConcept,c), concept=modelConcept.qname, standardSchema=os.path.basename(c.modelDocument.uri), standardConcept=c.qname) elif val.validateSBRNL: if not (genrlSpeclRelSet.isRelated(modelConcept, "child", c) or genrlSpeclRelSet.isRelated(c, "child", modelConcept)): val.modelXbrl.error("SBR.NL.2.2.2.02", _("Concept %(concept)s is also defined in standard taxonomy schema %(standardSchema)s without a general-special relationship"), modelObject=c, concept=modelConcept.qname, standardSchema=os.path.basename(c.modelDocument.uri)) ''' removed RH 2011-12-23 corresponding set up of table in ValidateFiling if val.validateSBRNL and name in val.nameWordsTable: if not any( any( genrlSpeclRelSet.isRelated(c, "child", modelConcept) for c in val.modelXbrl.nameConcepts.get(partialWordName, [])) for partialWordName in val.nameWordsTable[name]): val.modelXbrl.error("SBR.NL.2.3.2.01", _("Concept %(specialName)s is appears to be missing a general-special relationship to %(generalNames)s"), modelObject=c, specialName=modelConcept.qname, generalNames=', or to '.join(val.nameWordsTable[name])) ''' # 6.7.17 id properly formed id = modelConcept.id requiredId = (prefix if prefix is not None else "") + "_" + name if val.validateEFMorGFM and id != requiredId: val.modelXbrl.error(("EFM.6.07.17", "GFM.1.03.19"), _("Concept %(concept)s id %(id)s should be %(requiredId)s"), modelObject=modelConcept, concept=modelConcept.qname, id=id, requiredId=requiredId) # 6.7.18 nillable is true nillable = modelConcept.get("nillable") if nillable != "true" and modelConcept.isItem: val.modelXbrl.error(("EFM.6.07.18", "GFM.1.03.20"), _("Taxonomy schema %(schema)s element %(concept)s nillable %(nillable)s should be 'true'"), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=name, nillable=nillable) # 6.7.19 not tuple if modelConcept.isTuple: if val.validateEFMorGFM: val.modelXbrl.error(("EFM.6.07.19", "GFM.1.03.21"), _("Concept %(concept)s is a tuple"), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.20 no typed domain ref if modelConcept.isTypedDimension: val.modelXbrl.error(("EFM.6.07.20", "GFM.1.03.22"), _("Concept %(concept)s has typedDomainRef %(typedDomainRef)s"), modelObject=modelConcept, concept=modelConcept.qname, typedDomainRef=modelConcept.typedDomainElement.qname if modelConcept.typedDomainElement is not None else modelConcept.typedDomainRef) # 6.7.21 abstract must be duration isDuration = modelConcept.periodType == "duration" if modelConcept.isAbstract and not isDuration: val.modelXbrl.error(("EFM.6.07.21", "GFM.1.03.23"), _("Taxonomy schema %(schema)s element %(concept)s is abstract but period type is not duration"), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=modelConcept.qname) # 6.7.22 abstract must be stringItemType ''' removed SEC EFM v.17, Edgar release 10.4, and GFM 2011-04-08 if modelConcept.abstract == "true" and modelConcept.typeQname != XbrlConst. qnXbrliStringItemType: val.modelXbrl.error(("EFM.6.07.22", "GFM.1.03.24"), _("Concept %(concept)s is abstract but type is not xbrli:stringItemType"), modelObject=modelConcept, concept=modelConcept.qname) ''' substititutionGroupQname = modelConcept.substitutionGroupQname # 6.7.23 Axis must be subs group dimension if name.endswith("Axis") ^ (substititutionGroupQname == XbrlConst.qnXbrldtDimensionItem): val.modelXbrl.error(("EFM.6.07.23", "GFM.1.03.25"), _("Concept %(concept)s must end in Axis to be in xbrldt:dimensionItem substitution group"), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.24 Table must be subs group hypercube if name.endswith("Table") ^ (substititutionGroupQname == XbrlConst.qnXbrldtHypercubeItem): val.modelXbrl.error(("EFM.6.07.24", "GFM.1.03.26"), _("Concept %(concept)s must end in Table to be in xbrldt:hypercubeItem substitution group"), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=modelConcept.qname) # 6.7.25 if neither hypercube or dimension, substitution group must be item if substititutionGroupQname not in (None, XbrlConst.qnXbrldtDimensionItem, XbrlConst.qnXbrldtHypercubeItem, XbrlConst.qnXbrliItem): val.modelXbrl.error(("EFM.6.07.25", "GFM.1.03.27"), _("Concept %(concept)s has disallowed substitution group %(substitutionGroup)s"), modelObject=modelConcept, concept=modelConcept.qname, substitutionGroup=modelConcept.substitutionGroupQname) # 6.7.26 Table must be subs group hypercube if name.endswith("LineItems") and modelConcept.abstract != "true": val.modelXbrl.error(("EFM.6.07.26", "GFM.1.03.28"), _("Concept %(concept)s is a LineItems but not abstract"), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.27 type domainMember must end with Domain or Member conceptType = modelConcept.type isDomainItemType = conceptType is not None and conceptType.isDomainItemType endsWithDomainOrMember = name.endswith("Domain") or name.endswith("Member") if isDomainItemType != endsWithDomainOrMember: val.modelXbrl.error(("EFM.6.07.27", "GFM.1.03.29"), _("Concept %(concept)s must end with Domain or Member for type of domainItemType"), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.28 domainItemType must be duration if isDomainItemType and not isDuration: val.modelXbrl.error(("EFM.6.07.28", "GFM.1.03.30"), _("Concept %(concept)s is a domainItemType and must be periodType duration"), modelObject=modelConcept, concept=modelConcept.qname) #6.7.31 (version 27) fractions if modelConcept.isFraction: val.modelXbrl.error("EFM.6.07.31", _("Concept %(concept)s is a fraction"), modelObject=modelConcept, concept=modelConcept.qname) #6.7.32 (version 27) instant non numeric if modelConcept.isItem and (not modelConcept.isNumeric and not isDuration and not modelConcept.isAbstract and not isDomainItemType): val.modelXbrl.error("EFM.6.07.32", _("Taxonomy schema %(schema)s element %(concept)s is non-numeric but period type is not duration"), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=modelConcept.qname) # 6.8.5 semantic check, check LC3 name if name: if not name[0].isupper(): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.05.firstLetter", "GFM.2.03.05.firstLetter"), _("Concept %(concept)s name must start with a capital letter"), modelObject=modelConcept, concept=modelConcept.qname) if namePattern.search(name): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.05.disallowedCharacter", "GFM.2.03.05.disallowedCharacter"), _("Concept %(concept)s has disallowed name character"), modelObject=modelConcept, concept=modelConcept.qname) if len(name) > 200: val.modelXbrl.log("ERROR-SEMANTIC", "EFM.6.08.05.nameLength", _("Concept %(concept)s name length %(namelength)s exceeds 200 characters"), modelObject=modelConcept, concept=modelConcept.qname, namelength=len(name)) if val.validateEFM: label = modelConcept.label(lang="en-US", fallbackToQname=False) if label: # allow Joe's Bar, N.A. to be JoesBarNA -- remove ', allow A. as not article "a" lc3name = ''.join(re.sub(r"['.-]", "", (w[0] or w[2] or w[3] or w[4])).title() for w in re.findall(r"((\w+')+\w+)|(A[.-])|([.-]A(?=\W|$))|(\w+)", label) # EFM implies this should allow - and . re.findall(r"[\w\-\.]+", label) if w[4].lower() not in ("the", "a", "an")) if not(name == lc3name or (name and lc3name and lc3name[0].isdigit() and name[1:] == lc3name and (name[0].isalpha() or name[0] == '_'))): val.modelXbrl.log("WARNING-SEMANTIC", "EFM.6.08.05.LC3", _("Concept %(concept)s should match expected LC3 composition %(lc3name)s"), modelObject=modelConcept, concept=modelConcept.qname, lc3name=lc3name) if conceptType is not None: # 6.8.6 semantic check if not isDomainItemType and conceptType.qname != XbrlConst.qnXbrliDurationItemType: nameProblems = nonDomainItemNameProblemPattern.findall(name) if any(any(t) for t in nameProblems): # list of tuples with possibly nonempty strings val.modelXbrl.log("WARNING-SEMANTIC", ("EFM.6.08.06", "GFM.2.03.06"), _("Concept %(concept)s should not contain company or period information, found: %(matches)s"), modelObject=modelConcept, concept=modelConcept.qname, matches=", ".join(''.join(t) for t in nameProblems)) if conceptType.qname == XbrlConst.qnXbrliMonetaryItemType: if not modelConcept.balance: # 6.8.11 may not appear on a income or balance statement if any(linkroleDefinitionBalanceIncomeSheet.match(roleType.definition) for rel in val.modelXbrl.relationshipSet(XbrlConst.parentChild).toModelObject(modelConcept) for roleType in val.modelXbrl.roleTypes.get(rel.linkrole,())): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.11", "GFM.2.03.11"), _("Concept %(concept)s must have a balance because it appears in a statement of income or balance sheet"), modelObject=modelConcept, concept=modelConcept.qname) # 6.11.5 semantic check, must have a documentation label stdLabel = modelConcept.label(lang="en-US", fallbackToQname=False) defLabel = modelConcept.label(preferredLabel=XbrlConst.documentationLabel, lang="en-US", fallbackToQname=False) if not defLabel or ( # want different words than std label stdLabel and re.findall(r"\w+", stdLabel) == re.findall(r"\w+", defLabel)): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.11.05", "GFM.2.04.04"), _("Concept %(concept)s is monetary without a balance and must have a documentation label that disambiguates its sign"), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.16 semantic check if conceptType.qname == XbrlConst.qnXbrliDateItemType and modelConcept.periodType != "duration": val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.16", "GFM.2.03.16"), _("Concept %(concept)s of type xbrli:dateItemType must have periodType duration"), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.17 semantic check if conceptType.qname == XbrlConst.qnXbrliStringItemType and modelConcept.periodType != "duration": val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.17", "GFM.2.03.17"), _("Concept %(concept)s of type xbrli:stringItemType must have periodType duration"), modelObject=modelConcept, concept=modelConcept.qname) if val.validateSBRNL: if modelConcept.isTuple: if modelConcept.substitutionGroupQname.localName == "presentationTuple" and modelConcept.substitutionGroupQname.namespaceURI.endswith("/basis/sbr/xbrl/xbrl-syntax-extension"): # namespace may change each year definesPresentationTuples = True elif modelConcept.substitutionGroupQname.localName == "specificationTuple" and modelConcept.substitutionGroupQname.namespaceURI.endswith("/basis/sbr/xbrl/xbrl-syntax-extension"): # namespace may change each year definesSpecificationTuples = True else: definesTuples = True definesConcepts = True if modelConcept.isAbstract: val.modelXbrl.error("SBR.NL.2.2.2.03", _("Concept %(concept)s is an abstract tuple"), modelObject=modelConcept, concept=modelConcept.qname) if tupleCycle(val,modelConcept): val.modelXbrl.error("SBR.NL.2.2.2.07", _("Tuple %(concept)s has a tuple cycle"), modelObject=modelConcept, concept=modelConcept.qname) if modelConcept.get("nillable") != "false" and modelConcept.isRoot: val.modelXbrl.error("SBR.NL.2.2.2.17", #don't want default, just what was really there _("Tuple %(concept)s must have nillable='false'"), modelObject=modelConcept, concept=modelConcept.qname) elif modelConcept.isItem: definesConcepts = True if modelConcept.abstract == "true": if modelConcept.isRoot: if modelConcept.get("nillable") != "false": #don't want default, just what was really there val.modelXbrl.error("SBR.NL.2.2.2.16", _("Abstract root concept %(concept)s must have nillable='false'"), modelObject=modelConcept, concept=modelConcept.qname) if modelConcept.typeQname != XbrlConst.qnXbrliStringItemType: val.modelXbrl.error("SBR.NL.2.2.2.21", _("Abstract root concept %(concept)s must have type='xbrli:stringItemType'"), modelObject=modelConcept, concept=modelConcept.qname) if modelConcept.balance: val.modelXbrl.error("SBR.NL.2.2.2.22", _("Abstract concept %(concept)s must not have a balance attribute"), modelObject=modelConcept, concept=modelConcept.qname) if modelConcept.isHypercubeItem: definesHypercubes = True elif modelConcept.isDimensionItem: definesDimensions = True elif substititutionGroupQname and substititutionGroupQname.localName in ("domainItem","domainMemberItem"): definesDomains = True elif modelConcept.isItem: definesAbstractItems = True else: # not abstract if modelConcept.isItem: definesNonabstractItems = True if not (modelConcept.label(preferredLabel=XbrlConst.documentationLabel,fallbackToQname=False,lang="nl") or val.modelXbrl.relationshipSet(XbrlConst.conceptReference).fromModelObject(c) or modelConcept.genLabel(role=XbrlConst.genDocumentationLabel,lang="nl") or val.modelXbrl.relationshipSet(XbrlConst.elementReference).fromModelObject(c)): val.modelXbrl.error("SBR.NL.2.2.2.28", _("Concept %(concept)s must have a documentation label or reference"), modelObject=modelConcept, concept=modelConcept.qname) if modelConcept.balance and not modelConcept.instanceOfType(XbrlConst.qnXbrliMonetaryItemType): val.modelXbrl.error("SBR.NL.2.2.2.24", _("Non-monetary concept %(concept)s must not have a balance attribute"), modelObject=modelConcept, concept=modelConcept.qname) if modelConcept.isLinkPart: definesLinkParts = True val.modelXbrl.error("SBR.NL.2.2.5.01", _("Link:part concept %(concept)s is not allowed"), modelObject=modelConcept, concept=modelConcept.qname) if not modelConcept.genLabel(fallbackToQname=False,lang="nl"): val.modelXbrl.error("SBR.NL.2.2.5.02", _("Link part definition %(concept)s must have a generic label in language 'nl'"), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.8 check for embedded linkbase for e in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}linkbase"): if isinstance(e,ModelObject): val.modelXbrl.error(("EFM.6.07.08", "GFM.1.03.08"), _("Taxonomy schema %(schema)s contains an embedded linkbase"), modelObject=e, schema=modelDocument.basename) break requiredUsedOns = {XbrlConst.qnLinkPresentationLink, XbrlConst.qnLinkCalculationLink, XbrlConst.qnLinkDefinitionLink} standardUsedOns = {XbrlConst.qnLinkLabel, XbrlConst.qnLinkReference, XbrlConst.qnLinkDefinitionArc, XbrlConst.qnLinkCalculationArc, XbrlConst.qnLinkPresentationArc, XbrlConst.qnLinkLabelArc, XbrlConst.qnLinkReferenceArc, # per WH, private footnote arc and footnore resource roles are not allowed XbrlConst.qnLinkFootnoteArc, XbrlConst.qnLinkFootnote, } # 6.7.9 role types authority for e in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}roleType"): if isinstance(e,ModelObject): roleURI = e.get("roleURI") if targetNamespaceAuthority != UrlUtil.authority(roleURI): val.modelXbrl.error(("EFM.6.07.09", "GFM.1.03.09"), _("RoleType %(roleType)s does not match authority %(targetNamespaceAuthority)s"), modelObject=e, roleType=roleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.9 end with .../role/lc3 name if not roleTypePattern.match(roleURI): val.modelXbrl.warning(("EFM.6.07.09.roleEnding", "GFM.1.03.09"), "RoleType %(roleType)s should end with /role/{LC3name}", modelObject=e, roleType=roleURI) # 6.7.10 only one role type declaration in DTS modelRoleTypes = val.modelXbrl.roleTypes.get(roleURI) if modelRoleTypes is not None: modelRoleType = modelRoleTypes[0] definition = modelRoleType.definitionNotStripped usedOns = modelRoleType.usedOns if len(modelRoleTypes) == 1: # 6.7.11 used on's for pre, cal, def if any has a used on if not usedOns.isdisjoint(requiredUsedOns) and len(requiredUsedOns - usedOns) > 0: val.modelXbrl.error(("EFM.6.07.11", "GFM.1.03.11"), _("RoleType %(roleType)s missing used on %(usedOn)s"), modelObject=e, roleType=roleURI, usedOn=requiredUsedOns - usedOns) # 6.7.12 definition match pattern if (val.disclosureSystem.roleDefinitionPattern is not None and (definition is None or not val.disclosureSystem.roleDefinitionPattern.match(definition))): val.modelXbrl.error(("EFM.6.07.12", "GFM.1.03.12-14"), _("RoleType %(roleType)s definition \"%(definition)s\" must match {Sortcode} - {Type} - {Title}"), modelObject=e, roleType=roleURI, definition=(definition or "")) if usedOns & standardUsedOns: # semantics check val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("RoleType %(roleuri)s is defined using role types already defined by standard roles for: %(qnames)s"), modelObject=e, roleuri=roleURI, qnames=', '.join(str(qn) for qn in usedOns & standardUsedOns)) if val.validateSBRNL: if usedOns & XbrlConst.standardExtLinkQnames or XbrlConst.qnGenLink in usedOns: definesLinkroles = True if not e.genLabel(): val.modelXbrl.error("SBR.NL.2.2.3.03", _("Link RoleType %(roleType)s missing a generic standard label"), modelObject=e, roleType=roleURI) nlLabel = e.genLabel(lang="nl") if definition != nlLabel: val.modelXbrl.error("SBR.NL.2.2.3.04", _("Link RoleType %(roleType)s definition does not match NL standard generic label, \ndefinition: %(definition)s \nNL label: %(label)s"), modelObject=e, roleType=roleURI, definition=definition, label=nlLabel) if definition and (definition[0].isspace() or definition[-1].isspace()): val.modelXbrl.error("SBR.NL.2.2.3.07", _('Link RoleType %(roleType)s definition has leading or trailing spaces: "%(definition)s"'), modelObject=e, roleType=roleURI, definition=definition) # 6.7.13 arcrole types authority for e in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}arcroleType"): if isinstance(e,ModelObject): arcroleURI = e.get("arcroleURI") if targetNamespaceAuthority != UrlUtil.authority(arcroleURI): val.modelXbrl.error(("EFM.6.07.13", "GFM.1.03.15"), _("ArcroleType %(arcroleType)s does not match authority %(targetNamespaceAuthority)s"), modelObject=e, arcroleType=arcroleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.13 end with .../arcrole/lc3 name if not arcroleTypePattern.match(arcroleURI): val.modelXbrl.warning(("EFM.6.07.13.arcroleEnding", "GFM.1.03.15"), _("ArcroleType %(arcroleType)s should end with /arcrole/{LC3name}"), modelObject=e, arcroleType=arcroleURI) # 6.7.15 definition match pattern modelRoleTypes = val.modelXbrl.arcroleTypes[arcroleURI] definition = modelRoleTypes[0].definition if definition is None or not arcroleDefinitionPattern.match(definition): val.modelXbrl.error(("EFM.6.07.15", "GFM.1.03.17"), _("ArcroleType %(arcroleType)s definition must be non-empty"), modelObject=e, arcroleType=arcroleURI) # semantic checks usedOns = modelRoleTypes[0].usedOns if usedOns & standardUsedOns: # semantics check val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("ArcroleType %(arcroleuri)s is defined using role types already defined by standard arcroles for: %(qnames)s"), modelObject=e, arcroleuri=arcroleURI, qnames=', '.join(str(qn) for qn in usedOns & standardUsedOns)) if val.validateSBRNL: definesArcroles = True val.modelXbrl.error("SBR.NL.2.2.4.01", _("Arcrole type definition is not allowed: %(arcroleURI)s"), modelObject=e, arcroleURI=arcroleURI) if val.validateSBRNL: for appinfoElt in modelDocument.xmlRootElement.iter(tag="{http://www.w3.org/2001/XMLSchema}appinfo"): for nonLinkElt in appinfoElt.iterdescendants(): if isinstance(nonLinkElt, ModelObject) and nonLinkElt.namespaceURI != XbrlConst.link: val.modelXbrl.error("SBR.NL.2.2.11.05", _("Appinfo contains disallowed non-link element %(element)s"), modelObject=nonLinkElt, element=nonLinkElt.qname) for cplxTypeElt in modelDocument.xmlRootElement.iter(tag="{http://www.w3.org/2001/XMLSchema}complexType"): choiceElt = cplxTypeElt.find("{http://www.w3.org/2001/XMLSchema}choice") if choiceElt is not None: val.modelXbrl.error("SBR.NL.2.2.11.09", _("ComplexType contains disallowed xs:choice element"), modelObject=choiceElt) for cplxContentElt in modelDocument.xmlRootElement.iter(tag="{http://www.w3.org/2001/XMLSchema}complexContent"): if XmlUtil.descendantAttr(cplxContentElt, "http://www.w3.org/2001/XMLSchema", ("extension","restriction"), "base") != "sbr:placeholder": val.modelXbrl.error("SBR.NL.2.2.11.10", _("ComplexContent is disallowed"), modelObject=cplxContentElt) definesTypes = (modelDocument.xmlRootElement.find("{http://www.w3.org/2001/XMLSchema}complexType") is not None or modelDocument.xmlRootElement.find("{http://www.w3.org/2001/XMLSchema}simpleType") is not None) for enumElt in modelDocument.xmlRootElement.iter(tag="{http://www.w3.org/2001/XMLSchema}enumeration"): definesEnumerations = True if any(not valueElt.genLabel(lang="nl") for valueElt in enumElt.iter(tag="{http://www.w3.org/2001/XMLSchema}value")): val.modelXbrl.error("SBR.NL.2.2.7.05", _("Enumeration element has value(s) without generic label."), modelObject=enumElt) if (definesLinkroles + definesArcroles + definesLinkParts + definesAbstractItems + definesNonabstractItems + definesTuples + definesPresentationTuples + definesSpecificationTuples + definesTypes + definesEnumerations + definesDimensions + definesDomains + definesHypercubes) != 1: schemaContents = [] if definesLinkroles: schemaContents.append(_("linkroles")) if definesArcroles: schemaContents.append(_("arcroles")) if definesLinkParts: schemaContents.append(_("link parts")) if definesAbstractItems: schemaContents.append(_("abstract items")) if definesNonabstractItems: schemaContents.append(_("nonabstract items")) if definesTuples: schemaContents.append(_("tuples")) if definesPresentationTuples: schemaContents.append(_("sbrPresentationTuples")) if definesSpecificationTuples: schemaContents.append(_("sbrSpecificationTuples")) if definesTypes: schemaContents.append(_("types")) if definesEnumerations: schemaContents.append(_("enumerations")) if definesDimensions: schemaContents.append(_("dimensions")) if definesDomains: schemaContents.append(_("domains")) if definesHypercubes: schemaContents.append(_("hypercubes")) if schemaContents: if not ((definesTuples or definesPresentationTuples or definesSpecificationTuples) and not (definesLinkroles or definesArcroles or definesLinkParts or definesAbstractItems or definesTypes or definesDimensions or definesDomains or definesHypercubes)): val.modelXbrl.error("SBR.NL.2.2.1.01", _("Taxonomy schema may only define one of these: %(contents)s"), modelObject=modelDocument, contents=', '.join(schemaContents)) elif not any(refDoc.inDTS and refDoc.targetNamespace not in val.disclosureSystem.baseTaxonomyNamespaces for refDoc in modelDocument.referencesDocument.keys()): # no linkbase ref or includes val.modelXbrl.error("SBR.NL.2.2.1.01", _("Taxonomy schema must be a DTS entrypoint OR define linkroles OR arcroles OR link:parts OR context fragments OR abstract items OR tuples OR non-abstract elements OR types OR enumerations OR dimensions OR domains OR hypercubes"), modelObject=modelDocument) if definesConcepts ^ any( # xor so either concepts and no label LB or no concepts and has label LB (refDoc.type == ModelDocument.Type.LINKBASE and XmlUtil.descendant(refDoc.xmlRootElement, XbrlConst.link, "labelLink") is not None) for refDoc in modelDocument.referencesDocument.keys()): # no label linkbase val.modelXbrl.error("SBR.NL.2.2.1.02", _("A schema that defines concepts MUST have a linked 2.1 label linkbase"), modelObject=modelDocument) if (definesNonabstractItems or definesTuples) and not any( # was xor but changed to and not per RH 1/11/12 (refDoc.type == ModelDocument.Type.LINKBASE and (XmlUtil.descendant(refDoc.xmlRootElement, XbrlConst.link, "referenceLink") is not None or XmlUtil.descendant(refDoc.xmlRootElement, XbrlConst.link, "label", "{http://www.w3.org/1999/xlink}role", "http://www.xbrl.org/2003/role/documentation" ) is not None)) for refDoc in modelDocument.referencesDocument.keys()): val.modelXbrl.error("SBR.NL.2.2.1.03", _("A schema that defines non-abstract items MUST have a linked (2.1) reference linkbase AND/OR a label linkbase with @xlink:role=documentation"), modelObject=modelDocument) #6.3.3 filename check m = re.match(r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9]).xsd$", modelDocument.basename) if m: try: # check date value datetime.datetime.strptime(m.group(1),"%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}.xsd".format(val.fileNameBasePart, val.fileNameDatePart) if modelDocument.basename != expectedFilename: val.modelXbrl.log("WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Schema file name warning: %(filename)s, should match %(expectedFilename)s'), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid schema file base name part (date) in "{base}-{yyyymmdd}.xsd": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid schema file name, must match "{base}-{yyyymmdd}.xsd": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) elif modelDocument.type == ModelDocument.Type.LINKBASE: # if it is part of the submission (in same directory) check name labelRels = None if modelDocument.filepath.startswith(val.modelXbrl.modelDocument.filepathdir): #6.3.3 filename check extLinkElt = XmlUtil.descendant(modelDocument.xmlRootElement, XbrlConst.link, "*", "{http://www.w3.org/1999/xlink}type", "extended") if extLinkElt is None:# no ext link element val.modelXbrl.error((val.EFM60303 + ".noLinkElement", "GFM.1.01.01.noLinkElement"), _('Invalid linkbase file name: %(filename)s, has no extended link element, cannot determine link type.'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03.noLinkElement", "EFM.6.23.01.noLinkElement", "GFM.1.01.01.noLinkElement")) elif extLinkElt.localName not in extLinkEltFileNameEnding: val.modelXbrl.error("EFM.6.03.02", _('Invalid linkbase link element %(linkElement)s in %(filename)s'), modelObject=modelDocument, linkElement=extLinkElt.localName, filename=modelDocument.basename) else: m = re.match(r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9])(_[a-z]{3}).xml$", modelDocument.basename) expectedSuffix = extLinkEltFileNameEnding[extLinkElt.localName] if m and m.group(2) == expectedSuffix: try: # check date value datetime.datetime.strptime(m.group(1),"%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}{2}.xml".format(val.fileNameBasePart, val.fileNameDatePart, expectedSuffix) if modelDocument.basename != expectedFilename: val.modelXbrl.log("WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Linkbase name warning: %(filename)s should match %(expectedFilename)s'), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase base file name part (date) in "{base}-{yyyymmdd}_{suffix}.xml": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase name, must match "{base}-{yyyymmdd}%(expectedSuffix)s.xml": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, expectedSuffix=expectedSuffix, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) if extLinkElt.localName == "labelLink": if labelRels is None: labelRels = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel) for labelElt in XmlUtil.children(extLinkElt, XbrlConst.link, "label"): # 6.10.9 if XbrlConst.isNumericRole(labelElt.role): for rel in labelRels.toModelObject(labelElt): if rel.fromModelObject is not None and not rel.fromModelObject.isNumeric: val.modelXbrl.error("EFM.6.10.09", _("Label of non-numeric concept %(concept)s has a numeric role: %(role)s"), modelObject=(labelElt, rel.fromModelObject), concept=rel.fromModelObject.qname, role=labelElt.role) visited.remove(modelDocument)
def view(self, firstTime=False): self.blockSelectEvent = 1 self.blockViewModelObject = 0 self.tag_has = defaultdict(list) # temporary until Tk 8.6 # relationship set based on linkrole parameter, to determine applicable linkroles relationshipSet = self.modelXbrl.relationshipSet(self.arcrole, self.linkrole, self.linkqname, self.arcqname) if not relationshipSet: self.modelXbrl.modelManager.addToLog(_("no relationships for {0}").format(self.arcrole)) return False if firstTime: # set up treeView widget and tabbed pane hdr = self.treeColHdr if self.treeColHdr else _("{0} Relationships").format(XbrlConst.baseSetArcroleLabel(self.arcrole)[1:]) self.treeView.heading("#0", text=hdr) if self.arcrole == XbrlConst.parentChild: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("type", "references") self.treeView.column("type", width=100, anchor="w", stretch=False) self.treeView.heading("type", text=_("Type")) self.treeView.column("references", width=200, anchor="w", stretch=False) self.treeView.heading("references", text=_("References")) elif self.arcrole == XbrlConst.summationItem: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("weight", "balance") self.treeView.column("weight", width=48, anchor="w", stretch=False) self.treeView.heading("weight", text=_("Weight")) self.treeView.column("balance", width=70, anchor="w", stretch=False) self.treeView.heading("balance", text=_("Balance")) elif self.arcrole == "XBRL-dimensions": # add columns for dimensional information self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("arcrole", "contextElement", "closed", "usable") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("contextElement", width=50, anchor="center", stretch=False) self.treeView.heading("contextElement", text="Context") self.treeView.column("closed", width=40, anchor="center", stretch=False) self.treeView.heading("closed", text="Closed") self.treeView.column("usable", width=40, anchor="center", stretch=False) self.treeView.heading("usable", text="Usable") elif self.arcrole == "Table-rendering": # add columns for dimensional information self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("axis", "abstract", "header", "priItem", "dims") self.treeView.column("axis", width=50, anchor="center", stretch=False) self.treeView.heading("axis", text="Axis") self.treeView.column("abstract", width=24, anchor="center", stretch=False) self.treeView.heading("abstract", text="Abs") self.treeView.column("header", width=160, anchor="w", stretch=False) self.treeView.heading("header", text="Header") self.treeView.column("priItem", width=100, anchor="w", stretch=False) self.treeView.heading("priItem", text="Primary Item") self.treeView.column("dims", width=150, anchor="w", stretch=False) self.treeView.heading("dims", text=_("Dimensions")) self.id = 1 for previousNode in self.treeView.get_children(""): self.treeView.delete(previousNode) # sort URIs by definition linkroleUris = [] for linkroleUri in relationshipSet.linkRoleUris: modelRoleTypes = self.modelXbrl.roleTypes.get(linkroleUri) if modelRoleTypes: roledefinition = modelRoleTypes[0].definition if not roledefinition: roledefinition = linkroleUri roleId = modelRoleTypes[0].objectId(self.id) else: roledefinition = linkroleUri roleId = "node{0}".format(self.id) self.id += 1 linkroleUris.append((roledefinition, linkroleUri, roleId)) linkroleUris.sort() # for each URI in definition order for linkroleUriTuple in linkroleUris: linknode = self.treeView.insert("", "end", linkroleUriTuple[2], text=linkroleUriTuple[0], tags=("ELR",)) linkRelationshipSet = self.modelXbrl.relationshipSet(self.arcrole, linkroleUriTuple[1], self.linkqname, self.arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.viewConcept(rootConcept, rootConcept, "", self.labelrole, linknode, 1, linkRelationshipSet, set()) self.tag_has[linkroleUriTuple[1]].append(linknode)
def view(self, firstTime=False): self.blockSelectEvent = 1 self.blockViewModelObject = 0 self.tag_has = defaultdict(list) # temporary until Tk 8.6 # relationship set based on linkrole parameter, to determine applicable linkroles relationshipSet = self.modelXbrl.relationshipSet( self.arcrole, self.linkrole, self.linkqname, self.arcqname) if not relationshipSet: self.modelXbrl.modelManager.addToLog( _("no relationships for {0}").format(self.arcrole)) return False if firstTime: # set up treeView widget and tabbed pane hdr = self.treeColHdr if self.treeColHdr else _( "{0} Relationships").format( XbrlConst.baseSetArcroleLabel(self.arcrole)[1:]) self.treeView.heading("#0", text=hdr) if self.arcrole == XbrlConst.parentChild: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("type", "references") self.treeView.column("type", width=100, anchor="w", stretch=False) self.treeView.heading("type", text=_("Type")) self.treeView.column("references", width=200, anchor="w", stretch=False) self.treeView.heading("references", text=_("References")) elif self.arcrole == XbrlConst.summationItem: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("weight", "balance") self.treeView.column("weight", width=48, anchor="w", stretch=False) self.treeView.heading("weight", text=_("Weight")) self.treeView.column("balance", width=70, anchor="w", stretch=False) self.treeView.heading("balance", text=_("Balance")) elif self.arcrole == "XBRL-dimensions": # add columns for dimensional information self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("arcrole", "contextElement", "closed", "usable") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("contextElement", width=50, anchor="center", stretch=False) self.treeView.heading("contextElement", text="Context") self.treeView.column("closed", width=40, anchor="center", stretch=False) self.treeView.heading("closed", text="Closed") self.treeView.column("usable", width=40, anchor="center", stretch=False) self.treeView.heading("usable", text="Usable") elif self.arcrole == "Table-rendering": # add columns for dimensional information self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("axis", "abstract", "header", "priItem", "dims") self.treeView.column("axis", width=50, anchor="center", stretch=False) self.treeView.heading("axis", text="Axis") self.treeView.column("abstract", width=24, anchor="center", stretch=False) self.treeView.heading("abstract", text="Abs") self.treeView.column("header", width=160, anchor="w", stretch=False) self.treeView.heading("header", text="Header") self.treeView.column("priItem", width=100, anchor="w", stretch=False) self.treeView.heading("priItem", text="Primary Item") self.treeView.column("dims", width=150, anchor="w", stretch=False) self.treeView.heading("dims", text=_("Dimensions")) self.id = 1 for previousNode in self.treeView.get_children(""): self.treeView.delete(previousNode) # sort URIs by definition linkroleUris = [] for linkroleUri in relationshipSet.linkRoleUris: modelRoleTypes = self.modelXbrl.roleTypes.get(linkroleUri) if modelRoleTypes: roledefinition = (modelRoleTypes[0].definition or linkroleUri) roleId = modelRoleTypes[0].objectId(self.id) else: roledefinition = linkroleUri roleId = "node{0}".format(self.id) self.id += 1 linkroleUris.append((roledefinition, linkroleUri, roleId)) linkroleUris.sort() # for each URI in definition order for roledefinition, linkroleUri, roleId in linkroleUris: linknode = self.treeView.insert("", "end", roleId, text=roledefinition, tags=("ELR", )) linkRelationshipSet = self.modelXbrl.relationshipSet( self.arcrole, linkroleUri, self.linkqname, self.arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.viewConcept(rootConcept, rootConcept, "", self.labelrole, linknode, 1, linkRelationshipSet, set()) self.tag_has[linkroleUri].append(linknode)
def validate(self, modelVersReport): self.modelVersReport = modelVersReport versReport = modelVersReport.modelDocument if not hasattr(versReport, "xmlDocument"): # not parsed return for DTSname in ("fromDTS", "toDTS"): DTSmodelXbrl = getattr(versReport, DTSname) if DTSmodelXbrl is None or DTSmodelXbrl.modelDocument is None: self.modelVersReport.error( _("{0} is missing or not loaded").format(DTSname), "err", "vere:invalidDTSIdentifier") else: # validate DTS ValidateXbrl.ValidateXbrl(DTSmodelXbrl).validate(DTSmodelXbrl) if len(DTSmodelXbrl.errors) > 0: self.modelVersReport.error( _("{0} {1} has errors: {2}").format(DTSname, DTSmodelXbrl.modelDocument.basename, DTSmodelXbrl.errors), "err", "vere:invalidDTSIdentifier") # validate linkbases ValidateXbrl.ValidateXbrl(self.modelVersReport).validate(modelVersReport) versReportElt = versReport.xmlRootElement # check actions for assignmentRef in versReportElt.getElementsByTagNameNS(XbrlConst.ver,"assignmentRef"): ref = assignmentRef.getAttribute("ref") if ref not in versReport.idObjects or \ not isinstance(versReport.idObjects[ref], ModelVersObject.ModelAssignment): self.modelVersReport.error( _("AssignmentRef {0} does not reference an assignment").format(ref), "err", "vere:invalidAssignmentRef") # check namespace renames for NSrename in versReport.namespaceRenameFrom.values(): if NSrename.fromURI not in versReport.fromDTS.namespaceDocs: self.modelVersReport.error( _("NamespaceRename fromURI {0} does not reference a schema in fromDTS").format( NSrename.fromURI), "err", "vere:invalidNamespaceMapping") if NSrename.toURI not in versReport.toDTS.namespaceDocs: self.modelVersReport.error( _("NamespaceRename toURI {0} does not reference a schema in toDTS").format( NSrename.toURI), "err", "vere:invalidNamespaceMapping") # check role changes for roleChange in versReport.roleChanges.values(): if roleChange.fromURI not in versReport.fromDTS.roleTypes: self.modelVersReport.error( _("RoleChange fromURI {0} does not reference a roleType in fromDTS").format( roleChange.fromURI), "err", "vere:invalidRoleChange") if roleChange.toURI not in versReport.toDTS.roleTypes: self.modelVersReport.error( _("RoleChange toURI {0} does not reference a roleType in toDTS").format( roleChange.toURI), "err", "vere:invalidRoleChange") # check reportRefs # check actions for reportRef in versReportElt.getElementsByTagNameNS(XbrlConst.ver,"reportRef"): xlinkType = reportRef.getAttributeNS(XbrlConst.xlink, "type") if xlinkType != "simple": self.modelVersReport.error( _("ReportRef xlink:type {0} must be \"simple\"").format(xlinkType), "err", "vere:invalidXlinkType") # if existing it must be valid href = reportRef.getAttributeNS(XbrlConst.xlink, "href") # TBD if not reportRef.hasAttributeNS(XbrlConst.xlink, "arcrole"): self.modelVersReport.error( _("ReportRef xlink:arcrole is missing"), "err", "vere:missingXlinkArcrole") else: arcrole = reportRef.getAttributeNS(XbrlConst.xlink, "arcrole") if arcrole != "http://xbrl.org/arcrole/2010/versioning/related-report": self.modelVersReport.error( _("ReportRef xlink:arcrole {0} is invalid").format(arcrole), "err", "vere:invalidXlinkArcrole") if versReport.fromDTS and versReport.toDTS: # check concept changes of concept basic for conceptChange in versReport.conceptBasicChanges: if conceptChange.name != "conceptAdd" and \ (conceptChange.fromConcept is None or \ conceptChange.fromConcept.qname not in versReport.fromDTS.qnameConcepts): self.modelVersReport.error( _("{0} fromConcept {1} does not reference a concept in fromDTS").format( conceptChange.name, conceptChange.fromConceptQname), "err", "vercbe:invalidConceptReference") if conceptChange.name != "conceptDelete" and \ (conceptChange.toConcept is None or \ conceptChange.toConcept.qname not in versReport.toDTS.qnameConcepts): self.modelVersReport.error( _("{0} toConcept {1} does not reference a concept in toDTS").format( conceptChange.name, conceptChange.toConceptQname), "err", "vercbe:invalidConceptReference") # check concept changes of concept extended for conceptChange in versReport.conceptExtendedChanges: fromConcept = conceptChange.fromConcept toConcept = conceptChange.toConcept fromResource = conceptChange.fromResource toResource = conceptChange.toResource # fromConcept checks if not conceptChange.name.endswith("Add"): if not fromConcept: self.modelVersReport.error( _("{0} {1} fromConcept {2} does not reference a concept in fromDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.fromConceptQname), "err", "vercbe:invalidConceptReference") # tuple check elif "Child" in conceptChange.name and \ not versReport.fromDTS.qnameConcepts[fromConcept.qname] \ .isTuple: self.modelVersReport.error( _("{0} {1} fromConcept {2} must be defined as a tuple").format( conceptChange.actionId, conceptChange.name, conceptChange.fromConceptQname), "err", "vercbe:invalidConceptReference") # resource check elif "Label" in conceptChange.name: if not fromResource: self.modelVersReport.error( _("{0} {1} fromResource {2} does not reference a resource in fromDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.conceptLabel) if relationship: if relationship.qname != XbrlConst.qnLinkLabelArc or \ relationship.parentQname != XbrlConst.qnLinkLabelLink or \ fromResource.qname != XbrlConst.qnLinkLabel: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidConceptLabelIdentifier") else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.elementLabel) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidConceptLabelIdentifier") else: self.modelVersReport.error( _("{0} {1} fromResource {2} does not have a label relationship to {3} in fromDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidContentResourceIdentifier") elif "Reference" in conceptChange.name: if not fromResource: self.modelVersReport.error( _("{0} {1} fromResource {2} does not reference a resource in fromDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.conceptReference) if relationship: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ fromResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidConceptReferenceIdentifier") else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.elementReference) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error( _("{0} {1} fromResource {2} for {3} in fromDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidConceptReferenceIdentifier") else: self.modelVersReport.error( _("{0} {1} fromResource {2} does not have a reference relationship to {3} in fromDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.fromResourceValue, conceptChange.fromConceptQname), "err", "vercee:invalidContentResourceIdentifier") # toConcept checks if not conceptChange.name.endswith("Delete"): if not toConcept: self.modelVersReport.error( _("{0} {1} toConcept {2} does not reference a concept in toDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.toConceptQname), "err", "vercbe:invalidConceptReference") # tuple check elif "Child" in conceptChange.name and \ not versReport.toDTS.qnameConcepts[toConcept.qname] \ .isTuple: self.modelVersReport.error( _("{0} {1} toConcept {2} must be defined as a tuple").format( conceptChange.actionId, conceptChange.name, conceptChange.toConceptQname), "err", "vercbe:invalidConceptReference") # resource check elif "Label" in conceptChange.name: if not toResource: self.modelVersReport.error( _("{0} {1} toResource {2} does not reference a resource in toDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.conceptLabel) if relationship: if relationship.qname != XbrlConst.qnLinkLabelArc or \ relationship.parentQname != XbrlConst.qnLinkLabelLink or \ toResource.qname != XbrlConst.qnLinkLabel: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptLabelIdentifier") else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.elementLabel) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptLabelIdentifier") else: self.modelVersReport.error( _("{0} {1} toResource {2} does not have a label relationship to {3} in toDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidContentResourceIdentifier") elif "Reference" in conceptChange.name: if not toResource: self.modelVersReport.error( _("{0} {1} toResource {2} does not reference a resource in toDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue), "err", "vercee:invalidContentResourceIdentifier") else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.conceptReference) if relationship: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ toResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptReferenceIdentifier") else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.elementReference) if relationship: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error( _("{0} {1} toResource {2} for {3} in toDTS does not have expected link, arc, or label elements").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidConceptReferenceIdentifier") else: self.modelVersReport.error( _("{0} {1} toResource {2} does not have a reference relationship to {3} in toDTS").format( conceptChange.actionId, conceptChange.name, conceptChange.toResourceValue, conceptChange.toConceptQname), "err", "vercee:invalidContentResourceIdentifier") # check concept correspondence if fromConcept and toConcept: if versReport.toDTSqname(fromConcept.qname) != toConcept.qname and \ versReport.equivalentConcepts.get(fromConcept.qname) != toConcept.qname and \ toConcept.qname not in versReport.relatedConcepts.get(fromConcept.qname,[]): self.modelVersReport.error( _("{0} {1} fromConcept {2} and toConcept {3} must be equivalent or related").format( conceptChange.actionId, conceptChange.name, conceptChange.fromConceptQname, conceptChange.toConceptQname), "err", "vercee:invalidConceptCorrespondence") # custom attribute events if conceptChange.name.startswith("conceptAttribute"): try: for attr in conceptAttributeEventAttributes[conceptChange.name]: customAttributeQname = conceptChange.customAttributeQname(attr) if not customAttributeQname or customAttributeQname.namespaceURI is None: self.modelVersReport.error( _("{0} {1} {2} {3} does not have a namespace").format( conceptChange.actionId, conceptChange.name, attr, customAttributeQname), "err", "vercee:invalidAttributeChange") elif customAttributeQname.namespaceURI in (XbrlConst.xbrli, XbrlConst.xsd): self.modelVersReport.error( _("{0} {1} {2} {3} has an invalid namespace").format( conceptChange.actionId, conceptChange.name, attr, customAttributeQname), "err", "vercee:illegalCustomAttributeEvent") except KeyError: self.modelVersReport.error( _("{0} {1} event is not recognized").format(conceptChange.actionId, conceptChange.name), "info", "arelle:eventNotRecognized") # check relationship set changes for relSetChange in versReport.relationshipSetChanges: for relationshipSet, name in ((relSetChange.fromRelationshipSet, "fromRelationshipSet"), (relSetChange.toRelationshipSet, "toRelationshipSet")): if relationshipSet: relationshipSetValid = True if relationshipSet.link and relationshipSet.link not in relationshipSet.dts.qnameConcepts: self.modelVersReport.error( _("{0} link {1} does not reference an element in its DTS").format( relSetChange.name, name, relationshipSet.link), "err", "verrelse:invalidLinkElementReference") relationshipSetValid = False if relationshipSet.arc and relationshipSet.arc not in relationshipSet.dts.qnameConcepts: self.modelVersReport.error( _("{0} arc {1} does not reference an element in its DTS").format( relSetChange.name, name, relationshipSet.link), "err", "verrelse:invalidArcElementReference") relationshipSetValid = False if relationshipSet.linkrole and not (XbrlConst.isStandardRole(relationshipSet.linkrole) or relationshipSet.linkrole in relationshipSet.dts.roleTypes): self.modelVersReport.error( _("{0} linkrole {1} does not reference an linkrole in its DTS").format( relSetChange.name, name, relationshipSet.linkrole), "err", "verrelse:invalidLinkrole") relationshipSetValid = False if relationshipSet.arcrole and not (XbrlConst.isStandardArcrole(relationshipSet.arcrole) or relationshipSet.arcrole in relationshipSet.dts.arcroleTypes): self.modelVersReport.error( _("{0} arcrole {1} does not reference an arcrole in its DTS").format( relSetChange.name, name, relationshipSet.linkrole), "err", "verrelse:invalidArcrole") relationshipSetValid = False for relationship in relationshipSet.relationships: # fromConcept checks if not relationship.fromConcept: self.modelVersReport.error( _("{0} {1} relationship fromConcept {2} does not reference a concept in its DTS").format( relSetChange.name, name, relationship.fromName), "err", "verrelse:invalidConceptReference") relationshipSetValid = False if relationship.toName and not relationship.toConcept: self.modelVersReport.error( _("{0} {1} relationship toConcept {2} does not reference a concept in its DTS").format( relSetChange.name, name, relationship.toName), "err", "verrelse:invalidConceptReference") relationshipSetValid = False if relationshipSetValid: # test that relations exist if relationship.fromRelationship is None: if relationship.toName: self.modelVersReport.error( _("{0} {1} no relationship found from toConcept {2} to {3} in its DTS").format( relSetChange.name, name, relationship.fromName, relationship.toName), "err", "verrelse:invalidRelationshipReference") else: self.modelVersReport.error( _("{0} {1} no relationship found from toConcept {2} in its DTS").format( relSetChange.name, name, relationship.fromName), "err", "verrelse:invalidRelationshipReference") '''
def checkFilingDTS(val, modelDocument, isEFM, isGFM, visited): global targetNamespaceDatePattern, efmFilenamePattern, htmlFileNamePattern, roleTypePattern, arcroleTypePattern, \ arcroleDefinitionPattern, namePattern, linkroleDefinitionBalanceIncomeSheet if targetNamespaceDatePattern is None: targetNamespaceDatePattern = re.compile(r"/([12][0-9]{3})-([01][0-9])-([0-3][0-9])|" r"/([12][0-9]{3})([01][0-9])([0-3][0-9])|") efmFilenamePattern = re.compile(r"^[a-z0-9][a-zA-Z0-9_\.\-]*(\.xsd|\.xml|\.htm)$") htmlFileNamePattern = re.compile(r"^[a-zA-Z0-9][._a-zA-Z0-9-]*(\.htm)$") roleTypePattern = re.compile(r"^.*/role/[^/\s]+$") arcroleTypePattern = re.compile(r"^.*/arcrole/[^/\s]+$") arcroleDefinitionPattern = re.compile(r"^.*[^\\s]+.*$") # at least one non-whitespace character namePattern = re.compile("[][()*+?\\\\/^{}|@#%^=~`\"';:,<>&$\u00a3\u20ac]") # u20ac=Euro, u00a3=pound sterling linkroleDefinitionBalanceIncomeSheet = re.compile(r"[^-]+-\s+Statement\s+-\s+.*(income|balance|financial\W+position)", re.IGNORECASE) nonDomainItemNameProblemPattern = re.compile( r"({0})|(FirstQuarter|SecondQuarter|ThirdQuarter|FourthQuarter|[1-4]Qtr|Qtr[1-4]|ytd|YTD|HalfYear)(?:$|[A-Z\W])" .format(re.sub(r"\W", "", (val.entityRegistrantName or "").title()))) visited.append(modelDocument) for referencedDocument, modelDocumentReference in modelDocument.referencesDocument.items(): #6.07.01 no includes if modelDocumentReference.referenceType == "include": val.modelXbrl.error(("EFM.6.07.01", "GFM.1.03.01"), _("Taxonomy schema %(schema)s includes %(include)s, only import is allowed"), modelObject=modelDocumentReference.referringModelObject, schema=modelDocument.basename, include=referencedDocument.basename) if referencedDocument not in visited and (referencedDocument.inDTS or referencedDocument.type == ModelDocument.Type.INLINEXBRLDOCUMENTSET): # ignore EdgarRenderer added non-DTS documents checkFilingDTS(val, referencedDocument, isEFM, isGFM, visited) if modelDocument.type == ModelDocument.Type.INLINEXBRLDOCUMENTSET: return # nothing to check in inline document set surrogate parent if val.disclosureSystem.standardTaxonomiesDict is None: pass if isEFM: if modelDocument.uri in val.disclosureSystem.standardTaxonomiesDict: if modelDocument.targetNamespace: # check for duplicates of us-types, dei, and rr taxonomies match = standardNamespacesPattern.match(modelDocument.targetNamespace) if match is not None: conflictClass = match.group(2) or match.group(5) if (conflictClass == 'us-gaap' and match.group(3) < '2018') or conflictClass == 'srt': val.standardNamespaceConflicts['srt+us-gaap'].add(modelDocument) # ignore non-srt multi-usgaap in Filing.py if conflictClass == 'us-gaap' or conflictClass == 'ifrs-full': val.standardNamespaceConflicts['ifrs+us-gaap'].add(modelDocument) if conflictClass not in ('us-gaap', 'srt'): val.standardNamespaceConflicts[conflictClass].add(modelDocument) else: if len(modelDocument.basename) > 32: val.modelXbrl.error("EFM.5.01.01.tooManyCharacters", _("Document file name %(filename)s must not exceed 32 characters."), edgarCode="cp-0501-File-Name-Length", modelObject=modelDocument, filename=modelDocument.basename) if modelDocument.type != ModelDocument.Type.INLINEXBRLDOCUMENTSET: if modelDocument.type == ModelDocument.Type.INLINEXBRL: _pattern = htmlFileNamePattern _suffix = ".htm" else: _pattern = efmFilenamePattern _suffix = ".xsd or .xml" if not _pattern.match(modelDocument.basename): val.modelXbrl.error("EFM.5.01.01", _("Document file name %(filename)s must start with a-z or 0-9, contain upper or lower case letters, ., -, _, and end with %(suffix)s."), edgarCode="cp-0501-File-Name", modelObject=modelDocument, filename=modelDocument.basename, suffix=_suffix) if (modelDocument.type == ModelDocument.Type.SCHEMA and modelDocument.targetNamespace not in val.disclosureSystem.baseTaxonomyNamespaces and modelDocument.uri.startswith(val.modelXbrl.uriDir)): val.hasExtensionSchema = True # check schema contents types # 6.7.3 check namespace for standard authority targetNamespaceAuthority = UrlUtil.authority(modelDocument.targetNamespace) if targetNamespaceAuthority in val.disclosureSystem.standardAuthorities: val.modelXbrl.error(("EFM.6.07.03", "GFM.1.03.03"), _("The target namespace, %(targetNamespace)s cannot have the same authority (%(targetNamespaceAuthority)s) as a standard " "taxonomy, in %(schema)s. Please change your target namespace."), edgarCode="fs-0703-Extension-Has-Standard-Namespace-Authority", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, targetNamespaceAuthority=UrlUtil.authority(modelDocument.targetNamespace, includeScheme=False)) # 6.7.4 check namespace format if modelDocument.targetNamespace is None or not modelDocument.targetNamespace.startswith("http://"): match = None else: targetNamespaceDate = modelDocument.targetNamespace[len(targetNamespaceAuthority):] match = targetNamespaceDatePattern.match(targetNamespaceDate) if match is not None: try: if match.lastindex == 3: date = datetime.date(int(match.group(1)),int(match.group(2)),int(match.group(3))) elif match.lastindex == 6: date = datetime.date(int(match.group(4)),int(match.group(5)),int(match.group(6))) else: match = None except ValueError: match = None if match is None: val.modelXbrl.error(("EFM.6.07.04", "GFM.1.03.04"), _("You did not adhere to the requirements for target namespace, for %(targetNamespace)s in %(schema)s. " "Please recheck your submission and adhere to the target namespace requirements."), edgarCode="cp-0704-Taxonomy-Valid-Target-Namespace", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace) elif val.fileNameDate and date > val.fileNameDate: val.modelXbrl.info(("EFM.6.07.06", "GFM.1.03.06"), _("Warning: Taxonomy schema %(schema)s namespace %(targetNamespace)s has date later than document name date %(docNameDate)s"), modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, docNameDate=val.fileNameDate) if modelDocument.targetNamespace is not None: # 6.7.5 check prefix for _ authority = UrlUtil.authority(modelDocument.targetNamespace) if not re.match(r"(http://|https://|ftp://|urn:)\w+",authority): val.modelXbrl.error(("EFM.6.07.05", "GFM.1.03.05"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s must be a valid URI with a valid authority for the namespace."), edgarCode="du-0705-Namespace-Authority", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace) # may be multiple prefixes for namespace prefixes = [(prefix if prefix is not None else "") for prefix, NS in modelDocument.xmlRootElement.nsmap.items() if NS == modelDocument.targetNamespace] if not prefixes: prefix = None val.modelXbrl.error(("EFM.6.07.07", "GFM.1.03.07"), _("The schema does not supply a prefix for %(targetNamespace)s without an underscore character, in file %(schema)s. " "Please provide or change a prefix"), edgarCode="du-0707-Recommended-Prefix-Disallowed", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace) else: prefix = prefixes[0] if len(prefixes) > 1: val.modelXbrl.error(("EFM.6.07.07", "GFM.1.03.07"), _("The schema does not supply a prefix for %(targetNamespace)s without an underscore character, in file %(schema)s. " "Please provide or change a prefix"), edgarCode="du-0707-Recommended-Prefix-Disallowed", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, prefix=", ".join(prefixes)) elif "_" in prefix: val.modelXbrl.error(("EFM.6.07.07", "GFM.1.03.07"), _("The schema does not supply a prefix for %(targetNamespace)s without an underscore character, in file %(schema)s. " "Please provide or change a prefix"), edgarCode="du-0707-Recommended-Prefix-Disallowed", modelObject=modelDocument, schema=modelDocument.basename, targetNamespace=modelDocument.targetNamespace, prefix=prefix) for modelConcept in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.w3.org/2001/XMLSchema}element"): if isinstance(modelConcept,ModelConcept): # 6.7.16 name not duplicated in standard taxonomies name = modelConcept.get("name") if name is None: name = "" if modelConcept.get("ref") is not None: continue # don't validate ref's here for c in val.modelXbrl.nameConcepts.get(name, []): if c.modelDocument != modelDocument: if not c.modelDocument.uri.startswith(val.modelXbrl.uriDir): val.modelXbrl.error(("EFM.6.07.16", "GFM.1.03.18"), _("Your extension taxonomy contains an element, %(concept)s, which has the same name as an element in the base taxonomy, " "%(standardConcept)s. Please ensure that this extension is appropriate and if so, please change the extension concept name."), edgarCode="cp-0716-Element-Name-Same-As-Base", modelObject=(modelConcept,c), concept=modelConcept.qname, standardSchema=c.modelDocument.basename, standardConcept=c.qname) # 6.7.17 id properly formed _id = modelConcept.id requiredId = (prefix if prefix is not None else "") + "_" + name if _id != requiredId: val.modelXbrl.error(("EFM.6.07.17", "GFM.1.03.19"), _("You did not adhere to the declarations for concepts by containing an 'id' attribute whose value begins with the recommended " "namespace prefix of the taxonomy, followed by an underscore, followed by an element name, for the concept %(concept)s. " "Please recheck your submission."), edgarCode="cp-0717-Element-Id", modelObject=modelConcept, concept=modelConcept.qname, id=_id, requiredId=requiredId) # 6.7.18 nillable is true nillable = modelConcept.get("nillable") if nillable != "true" and modelConcept.isItem: val.modelXbrl.error(("EFM.6.07.18", "GFM.1.03.20"), _("Element %(concept)s is declared without a 'true' value for the nillable attribute. Please set the value to 'true'."), edgarCode="du-0718-Nillable-Not-True", modelObject=modelConcept, schema=modelDocument.basename, concept=name, nillable=nillable) # 6.7.19 not tuple if modelConcept.isTuple: val.modelXbrl.error(("EFM.6.07.19", "GFM.1.03.21"), _("You provided an extension concept which is a tuple, %(concept)s. Please remove tuples and check your submission."), edgarCode="cp-0719-No-Tuple-Element", modelObject=modelConcept, concept=modelConcept.qname) # 6.7.20 no typed domain ref if modelConcept.isTypedDimension: val.modelXbrl.error(("EFM.6.07.20", "GFM.1.03.22"), _("There is an xbrldt:typedDomainRef attribute on %(concept)s (%(typedDomainRef)s). Please remove it."), edgarCode="du-0720-Typed-Domain-Ref-Disallowed", modelObject=modelConcept, concept=modelConcept.qname, typedDomainRef=modelConcept.typedDomainElement.qname if modelConcept.typedDomainElement is not None else modelConcept.typedDomainRef) # 6.7.21 abstract must be duration isDuration = modelConcept.periodType == "duration" if modelConcept.isAbstract and not isDuration: val.modelXbrl.error(("EFM.6.07.21", "GFM.1.03.23"), _("Element %(concept)s is declared as an abstract item with period type 'instant'. Please change it to 'duration' or " "make the element not abstract."), edgarCode="du-0721-Abstract-Is-Instant", modelObject=modelConcept, schema=modelDocument.basename, concept=modelConcept.qname) # 6.7.22 abstract must be stringItemType ''' removed SEC EFM v.17, Edgar release 10.4, and GFM 2011-04-08 if modelConcept.abstract == "true" and modelConcept.typeQname != XbrlConst. qnXbrliStringItemType: val.modelXbrl.error(("EFM.6.07.22", "GFM.1.03.24"), _("Concept %(concept)s is abstract but type is not xbrli:stringItemType"), modelObject=modelConcept, concept=modelConcept.qname) ''' substitutionGroupQname = modelConcept.substitutionGroupQname # 6.7.23 Axis must be subs group dimension if name.endswith("Axis") ^ (substitutionGroupQname == XbrlConst.qnXbrldtDimensionItem): val.modelXbrl.error(("EFM.6.07.23", "GFM.1.03.25"), _("The substitution group 'xbrldt:dimensionItem' is only consistent with an element name that ends with 'Axis'. " "Please change %(conceptLocalName)s or change the substitutionGroup."), edgarCode="du-0723-Axis-Dimension-Name-Mismatch", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.24 Table must be subs group hypercube if name.endswith("Table") ^ (substitutionGroupQname == XbrlConst.qnXbrldtHypercubeItem): val.modelXbrl.error(("EFM.6.07.24", "GFM.1.03.26"), _("The substitution group 'xbrldt:hypercubeItem' is only allowed with an element name that ends with 'Table'. " "Please change %(conceptLocalName)s or change the substitutionGroup."), edgarCode="du-0724-Table-Hypercube-Name-Mismatch", modelObject=modelConcept, schema=modelDocument.basename, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.25 if neither hypercube or dimension, substitution group must be item if substitutionGroupQname not in (None, XbrlConst.qnXbrldtDimensionItem, XbrlConst.qnXbrldtHypercubeItem, XbrlConst.qnXbrliItem): val.modelXbrl.error(("EFM.6.07.25", "GFM.1.03.27"), _("The substitution group attribute value %(substitutionGroup)s of element %(conceptLocalName)s is not allowed. " "Please change it to one of 'xbrli:item', 'xbrldt:dimensionItem' or 'xbrldt:hypercubeItem'."), edgarCode="du-0725-Substitution-Group-Custom", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName, substitutionGroup=modelConcept.substitutionGroupQname) # 6.7.26 Table must be subs group hypercube if name.endswith("LineItems") and modelConcept.abstract != "true": val.modelXbrl.error(("EFM.6.07.26", "GFM.1.03.28"), _("The element %(conceptLocalName)s ends with 'LineItems' but is not abstract. Please change %(conceptLocalName)s or " "the value of the 'abstract' attribute."), edgarCode="du-0726-LineItems-Abstract-Name-Mismatch", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.27 type domainMember must end with Domain or Member conceptType = modelConcept.type isDomainItemType = conceptType is not None and conceptType.isDomainItemType endsWithDomainOrMember = name.endswith("Domain") or name.endswith("Member") if isDomainItemType != endsWithDomainOrMember: val.modelXbrl.error(("EFM.6.07.27", "GFM.1.03.29"), _("The type 'us-types:domainItemType' is only allowed with an element name that ends with 'Domain' or 'Member'. " "Please change %(conceptLocalName)s or change the type."), edgarCode="du-0727-Domain-Type-Name-Mismatch", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) # 6.7.28 domainItemType must be duration if isDomainItemType and not isDuration: val.modelXbrl.error(("EFM.6.07.28", "GFM.1.03.30"), _("Element %(conceptLocalName)s is declared as a us-types:domainItemType with period type 'instant'. " "Please change it to 'duration' or change the item type."), edgarCode="du-0728-Domain-Member-Is-Instant", modelObject=modelConcept, concept=modelConcept.qname, conceptLocalName=modelConcept.qname.localName) #6.7.31 (version 27) fractions if modelConcept.isFraction: val.modelXbrl.error("EFM.6.07.31", _("Element %(concept)s is declared as a fraction item type. Change or remove the declaration."), edgarCode="du-0731-Fraction-Item-Type", modelObject=modelConcept, concept=modelConcept.qname) #6.7.32 (version 27) instant non numeric if modelConcept.isItem and (not modelConcept.isNumeric and not isDuration and not modelConcept.isAbstract and not isDomainItemType): val.modelXbrl.error("EFM.6.07.32", _("Declaration of element %(concept)s in %(schema)s must have xbrli:periodType of 'duration' because its base type is not numeric."), edgarCode="rq-0732-Nonnnumeric-Must-Be-Duration", modelObject=modelConcept, schema=modelDocument.basename, concept=modelConcept.qname) # 6.8.5 semantic check, check LC3 name if name: if not name[0].isupper(): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.05.firstLetter", "GFM.2.03.05.firstLetter"), _("Concept %(concept)s name must start with a capital letter"), modelObject=modelConcept, concept=modelConcept.qname) if namePattern.search(name): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.05.disallowedCharacter", "GFM.2.03.05.disallowedCharacter"), _("Concept %(concept)s has disallowed name character"), modelObject=modelConcept, concept=modelConcept.qname) if len(name) > 200: val.modelXbrl.log("ERROR-SEMANTIC", "EFM.6.08.05.nameLength", _("Concept %(concept)s name length %(namelength)s exceeds 200 characters"), modelObject=modelConcept, concept=modelConcept.qname, namelength=len(name)) if isEFM: label = modelConcept.label(lang="en-US", fallbackToQname=False) if label: # allow Joe's Bar, N.A. to be JoesBarNA -- remove ', allow A. as not article "a" lc3name = ''.join(re.sub(r"['.-]", "", (w[0] or w[2] or w[3] or w[4])).title() for w in re.findall(r"((\w+')+\w+)|(A[.-])|([.-]A(?=\W|$))|(\w+)", label) # EFM implies this should allow - and . re.findall(r"[\w\-\.]+", label) if w[4].lower() not in ("the", "a", "an")) if not(name == lc3name or (name and lc3name and lc3name[0].isdigit() and name[1:] == lc3name and (name[0].isalpha() or name[0] == '_'))): val.modelXbrl.log("WARNING-SEMANTIC", "EFM.6.08.05.LC3", _("Concept %(concept)s should match expected LC3 composition %(lc3name)s"), modelObject=modelConcept, concept=modelConcept.qname, lc3name=lc3name) if conceptType is not None: # 6.8.6 semantic check if not isDomainItemType and conceptType.qname != XbrlConst.qnXbrliDurationItemType: nameProblems = nonDomainItemNameProblemPattern.findall(name) if any(any(t) for t in nameProblems): # list of tuples with possibly nonempty strings val.modelXbrl.log("WARNING-SEMANTIC", ("EFM.6.08.06", "GFM.2.03.06"), _("Concept %(concept)s should not contain company or period information, found: %(matches)s"), modelObject=modelConcept, concept=modelConcept.qname, matches=", ".join(''.join(t) for t in nameProblems)) if conceptType.qname == XbrlConst.qnXbrliMonetaryItemType: if not modelConcept.balance: # 6.8.11 may not appear on a income or balance statement if any(linkroleDefinitionBalanceIncomeSheet.match(roleType.definition) for rel in val.modelXbrl.relationshipSet(XbrlConst.parentChild).toModelObject(modelConcept) for roleType in val.modelXbrl.roleTypes.get(rel.linkrole,())): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.11", "GFM.2.03.11"), _("Concept %(concept)s must have a balance because it appears in a statement of income or balance sheet"), modelObject=modelConcept, concept=modelConcept.qname) # 6.11.5 semantic check, must have a documentation label stdLabel = modelConcept.label(lang="en-US", fallbackToQname=False) defLabel = modelConcept.label(preferredLabel=XbrlConst.documentationLabel, lang="en-US", fallbackToQname=False) if not defLabel or ( # want different words than std label stdLabel and re.findall(r"\w+", stdLabel) == re.findall(r"\w+", defLabel)): val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.11.05", "GFM.2.04.04"), _("Concept %(concept)s is monetary without a balance and must have a documentation label that disambiguates its sign"), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.16 semantic check if conceptType.qname == XbrlConst.qnXbrliDateItemType and modelConcept.periodType != "duration": val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.16", "GFM.2.03.16"), _("Concept %(concept)s of type xbrli:dateItemType must have periodType duration"), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.17 semantic check if conceptType.qname == XbrlConst.qnXbrliStringItemType and modelConcept.periodType != "duration": val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.17", "GFM.2.03.17"), _("Concept %(concept)s of type xbrli:stringItemType must have periodType duration"), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.8 check for embedded linkbase for e in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}linkbase"): if isinstance(e,ModelObject): val.modelXbrl.error(("EFM.6.07.08", "GFM.1.03.08"), _("Your filing contained embedded linkbases in %(schema)s. Please recheck your submission and remove all embedded linkbases."), edgarCode="cp-0708-No-Embedded-Linkbases", modelObject=e, schema=modelDocument.basename) break requiredUsedOns = {XbrlConst.qnLinkPresentationLink, XbrlConst.qnLinkCalculationLink, XbrlConst.qnLinkDefinitionLink} standardUsedOns = {XbrlConst.qnLinkLabel, XbrlConst.qnLinkReference, XbrlConst.qnLinkDefinitionArc, XbrlConst.qnLinkCalculationArc, XbrlConst.qnLinkPresentationArc, XbrlConst.qnLinkLabelArc, XbrlConst.qnLinkReferenceArc, # per WH, private footnote arc and footnore resource roles are not allowed XbrlConst.qnLinkFootnoteArc, XbrlConst.qnLinkFootnote, } # 6.7.9 role types authority for e in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}roleType"): if isinstance(e,ModelObject): roleURI = e.get("roleURI") if targetNamespaceAuthority != UrlUtil.authority(roleURI): val.modelXbrl.error(("EFM.6.07.09", "GFM.1.03.09"), _("Role %(roleType)s does not begin with %(targetNamespace)s's scheme and authority. " "Please change the role URI or target namespace URI."), edgarCode="du-0709-Role-Namespace-Mismatch", modelObject=e, roleType=roleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.9 end with .../role/lc3 name if not roleTypePattern.match(roleURI): val.modelXbrl.warning(("EFM.6.07.09.roleEnding", "GFM.1.03.09"), "RoleType %(roleType)s should end with /role/{LC3name}", modelObject=e, roleType=roleURI) # 6.7.10 only one role type declaration in DTS modelRoleTypes = val.modelXbrl.roleTypes.get(roleURI) if modelRoleTypes is not None: modelRoleType = modelRoleTypes[0] definition = modelRoleType.definitionNotStripped usedOns = modelRoleType.usedOns if len(modelRoleTypes) == 1: # 6.7.11 used on's for pre, cal, def if any has a used on if not usedOns.isdisjoint(requiredUsedOns) and len(requiredUsedOns - usedOns) > 0: val.modelXbrl.error(("EFM.6.07.11", "GFM.1.03.11"), _("The role %(roleType)s did not provide a usedOn element for all three link types (presentation, " "calculation and definition), missing %(usedOn)s. Change the declaration to be for all three types of link, and resubmit."), edgarCode="du-0711-Role-Type-Declaration-Incomplete", modelObject=e, roleType=roleURI, usedOn=requiredUsedOns - usedOns) # 6.7.12 definition match pattern if (val.disclosureSystem.roleDefinitionPattern is not None and (definition is None or not val.disclosureSystem.roleDefinitionPattern.match(definition))): val.modelXbrl.error(("EFM.6.07.12", "GFM.1.03.12-14"), _("The definition '%(definition)s' of role %(roleType)s does not match the expected format. " "Please check that the definition matches {number} - {type} - {text}."), edgarCode="rq-0712-Role-Definition-Mismatch", modelObject=e, roleType=roleURI, definition=(definition or "")) if usedOns & standardUsedOns: # semantics check val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("RoleType %(roleuri)s is defined using role types already defined by standard roles for: %(qnames)s"), modelObject=e, roleuri=roleURI, qnames=', '.join(str(qn) for qn in usedOns & standardUsedOns)) # 6.7.13 arcrole types authority for e in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}arcroleType"): if isinstance(e,ModelObject): arcroleURI = e.get("arcroleURI") if targetNamespaceAuthority != UrlUtil.authority(arcroleURI): val.modelXbrl.error(("EFM.6.07.13", "GFM.1.03.15"), _("Relationship role %(arcroleType)s does not begin with %(targetNamespace)s's scheme and authority. " "Please change the relationship role URI or target namespace URI."), edgarCode="du-0713-Arcrole-Namespace-Mismatch", modelObject=e, arcroleType=arcroleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.13 end with .../arcrole/lc3 name if not arcroleTypePattern.match(arcroleURI): val.modelXbrl.warning(("EFM.6.07.13.arcroleEnding", "GFM.1.03.15"), _("ArcroleType %(arcroleType)s should end with /arcrole/{LC3name}"), modelObject=e, arcroleType=arcroleURI) # 6.7.15 definition match pattern modelRoleTypes = val.modelXbrl.arcroleTypes[arcroleURI] definition = modelRoleTypes[0].definition if definition is None or not arcroleDefinitionPattern.match(definition): val.modelXbrl.error(("EFM.6.07.15", "GFM.1.03.17"), _("Relationship role declaration %(arcroleType)s is missing a definition. Please provide a definition."), edgarCode="du-0715-Arcrole-Definition-Missing", modelObject=e, arcroleType=arcroleURI) # semantic checks usedOns = modelRoleTypes[0].usedOns if usedOns & standardUsedOns: # semantics check val.modelXbrl.log("ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("ArcroleType %(arcroleuri)s is defined using role types already defined by standard arcroles for: %(qnames)s"), modelObject=e, arcroleuri=arcroleURI, qnames=', '.join(str(qn) for qn in usedOns & standardUsedOns)) #6.3.3 filename check m = re.match(r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9]).xsd$", modelDocument.basename) if m: try: # check date value datetime.datetime.strptime(m.group(1),"%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}.xsd".format(val.fileNameBasePart, val.fileNameDatePart) if modelDocument.basename != expectedFilename: val.modelXbrl.log("WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Schema file name warning: %(filename)s, should match %(expectedFilename)s'), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid schema file base name part (date) in "{base}-{yyyymmdd}.xsd": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid schema file name, must match "{base}-{yyyymmdd}.xsd": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) elif modelDocument.type == ModelDocument.Type.LINKBASE: # if it is part of the submission (in same directory) check name labelRels = None if modelDocument.filepath.startswith(val.modelXbrl.modelDocument.filepathdir): #6.3.3 filename check extLinkElt = XmlUtil.descendant(modelDocument.xmlRootElement, XbrlConst.link, "*", "{http://www.w3.org/1999/xlink}type", "extended") if extLinkElt is None:# no ext link element val.modelXbrl.error((val.EFM60303 + ".noLinkElement", "GFM.1.01.01.noLinkElement"), _('Invalid linkbase file name: %(filename)s, has no extended link element, cannot determine link type.'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03.noLinkElement", "EFM.6.23.01.noLinkElement", "GFM.1.01.01.noLinkElement")) elif extLinkElt.localName not in extLinkEltFileNameEnding: val.modelXbrl.error("EFM.6.03.02", _('Invalid linkbase link element %(linkElement)s in %(filename)s'), modelObject=modelDocument, linkElement=extLinkElt.localName, filename=modelDocument.basename) else: m = re.match(r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9])(_[a-z]{3}).xml$", modelDocument.basename) expectedSuffix = extLinkEltFileNameEnding[extLinkElt.localName] if m and m.group(2) == expectedSuffix: try: # check date value datetime.datetime.strptime(m.group(1),"%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}{2}.xml".format(val.fileNameBasePart, val.fileNameDatePart, expectedSuffix) if modelDocument.basename != expectedFilename: val.modelXbrl.log("WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Linkbase name warning: %(filename)s should match %(expectedFilename)s'), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase base file name part (date) in "{base}-{yyyymmdd}_{suffix}.xml": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error((val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase name, must match "{base}-{yyyymmdd}%(expectedSuffix)s.xml": %(filename)s'), modelObject=modelDocument, filename=modelDocument.basename, expectedSuffix=expectedSuffix, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) if extLinkElt.localName == "labelLink": if labelRels is None: labelRels = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel) for labelElt in XmlUtil.children(extLinkElt, XbrlConst.link, "label"): # 6.10.9 if XbrlConst.isNumericRole(labelElt.role): for rel in labelRels.toModelObject(labelElt): if rel.fromModelObject is not None and not rel.fromModelObject.isNumeric: val.modelXbrl.error("EFM.6.10.09", _("Non-numeric element %(concept)s has a label role for numeric elements: %(role)s. " "Please change the role attribute."), edgarCode="du-1009-Numeric-Label-Role", modelObject=(labelElt, rel.fromModelObject), concept=rel.fromModelObject.qname, role=labelElt.role)
def checkDTSdocument(val, modelDocument, isFilingDocument): for hrefElt, _hrefedDoc, hrefId in modelDocument.hrefObjects: if hrefId: #check scheme regardless of whether document loaded # check all xpointer schemes for scheme, _path in XmlUtil.xpointerSchemes(hrefId): if scheme == "element" and val.validateDisclosureSystem: val.modelXbrl.error( ("EFM.6.03.06", "GFM.1.01.03"), _("Href %(elementHref)s may only have shorthand xpointers" ), modelObject=hrefElt, elementHref=hrefElt.get( "{http://www.w3.org/1999/xlink}href")) if not isFilingDocument: return # not a filing's extension document if modelDocument.type == ModelDocument.Type.SCHEMA: if modelDocument.targetNamespace is not None and len( modelDocument.targetNamespace) > 85: l = len(modelDocument.targetNamespace.encode("utf-8")) if l > 255: val.modelXbrl.error( "EFM.6.07.30", _("Schema targetNamespace length (%(length)s) is over 255 bytes long in utf-8 %(targetNamespace)s" ), modelObject=modelDocument.xmlRootElement, length=l, targetNamespace=modelDocument.targetNamespace, value=modelDocument.targetNamespace) if modelDocument.type in (ModelDocument.Type.SCHEMA, ModelDocument.Type.LINKBASE): isSchema = modelDocument.type == ModelDocument.Type.SCHEMA if isSchema: for elt in modelDocument.xmlRootElement.iter( tag="{http://www.w3.org/2001/XMLSchema}*"): if elt.namespaceURI == XbrlConst.xsd: localName = elt.localName if localName in {"element", "complexType", "simpleType"}: name = elt.get("name") if name and len(name) > 64: l = len(name.encode("utf-8")) if l > 200: val.modelXbrl.error( "EFM.6.07.29", _("Schema %(element)s has a name length (%(length)s) over 200 bytes long in utf-8, %(name)s." ), modelObject=elt, element=localName, name=name, length=l) for elt in modelDocument.xmlRootElement.iter(): if isinstance(elt, ModelObject): xlinkType = elt.get("{http://www.w3.org/1999/xlink}type") xlinkRole = elt.get("{http://www.w3.org/1999/xlink}role") if elt.namespaceURI == XbrlConst.link: # check schema roleTypes if elt.localName in ("roleType", "arcroleType"): uriAttr = { "roleType": "roleURI", "arcroleType": "arcroleURI" }[elt.localName] roleURI = elt.get(uriAttr) if len(roleURI) > 85: l = len(roleURI.encode("utf-8")) if l > 255: val.modelXbrl.error( "EFM.6.07.30", _("Schema %(element)s %(attribute)s length (%(length)s) is over 255 bytes long in utf-8 %(roleURI)s" ), modelObject=elt, element=elt.qname, attribute=uriAttr, length=l, roleURI=roleURI, value=roleURI) if elt.localName == "arcroleRef": refUri = elt.get("arcroleURI") hrefAttr = elt.get("{http://www.w3.org/1999/xlink}href") hrefUri, hrefId = UrlUtil.splitDecodeFragment(hrefAttr) if hrefUri not in val.disclosureSystem.standardTaxonomiesDict: val.modelXbrl.error( ("EFM.6.09.06", "GFM.1.04.06"), _("Arcrole %(refURI)s arcroleRef %(xlinkHref)s must be a standard taxonomy" ), modelObject=elt, refURI=refUri, xlinkHref=hrefUri) if modelDocument.type == ModelDocument.Type.INLINEXBRL and elt.namespaceURI in XbrlConst.ixbrlAll: if elt.localName == "footnote": if val.validateGFM: if elt.get("{http://www.w3.org/1999/xlink}arcrole" ) != XbrlConst.factFootnote: # must be in a nonDisplay div if not any( inlineDisplayNonePattern.search( e.get("style") or "") for e in XmlUtil.ancestors( elt, XbrlConst.xhtml, "div")): val.modelXbrl.error( ("EFM.N/A", "GFM:1.10.16"), _("Inline XBRL footnote %(footnoteID)s must be in non-displayable div due to arcrole %(arcrole)s" ), modelObject=elt, footnoteID=elt.get("footnoteID"), arcrole=elt.get( "{http://www.w3.org/1999/xlink}arcrole" )) if not elt.get( "{http://www.w3.org/XML/1998/namespace}lang"): val.modelXbrl.error( ("EFM.N/A", "GFM:1.10.13"), _("Inline XBRL footnote %(footnoteID)s is missing an xml:lang attribute" ), modelObject=elt, footnoteID=id) if xlinkType == "extended": if not xlinkRole or xlinkRole == "": val.modelXbrl.error(("EFM.6.09.04", "GFM.1.04.04"), "%(element)s is missing an xlink:role", modelObject=elt, element=elt.qname) if not val.extendedElementName: val.extendedElementName = elt.qname elif val.extendedElementName != elt.qname: val.modelXbrl.error( ("EFM.6.09.07", "GFM:1.04.07"), _("Extended element %(element)s must be the same as %(element2)s" ), modelObject=elt, element=elt.qname, element2=val.extendedElementName) elif xlinkType == "resource": if not xlinkRole: val.modelXbrl.error( ("EFM.6.09.04", "GFM.1.04.04"), _("%(element)s is missing an xlink:role"), modelObject=elt, element=elt.qname) elif not XbrlConst.isStandardRole(xlinkRole): modelsRole = val.modelXbrl.roleTypes.get(xlinkRole) if (modelsRole is None or len(modelsRole) == 0 or modelsRole[0].modelDocument.targetNamespace not in val.disclosureSystem.standardTaxonomiesDict ): val.modelXbrl.error( ("EFM.6.09.05", "GFM.1.04.05"), _("Resource %(xlinkLabel)s role %(role)s is not a standard taxonomy role" ), modelObject=elt, xlinkLabel=elt.get( "{http://www.w3.org/1999/xlink}label"), role=xlinkRole, element=elt.qname, roleDefinition=val.modelXbrl.roleTypeDefinition( xlinkRole)) elif xlinkType == "arc": if elt.get("priority") is not None: priority = elt.get("priority") try: if int(priority) >= 10: val.modelXbrl.error( ("EFM.6.09.09", "GFM.1.04.08"), _("Arc from %(xlinkFrom)s to %(xlinkTo)s priority %(priority)s must be less than 10" ), modelObject=elt, arcElement=elt.qname, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), priority=priority) except (ValueError): val.modelXbrl.error( ("EFM.6.09.09", "GFM.1.04.08"), _("Arc from %(xlinkFrom)s to %(xlinkTo)s priority %(priority)s is not an integer" ), modelObject=elt, arcElement=elt.qname, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), priority=priority) if elt.namespaceURI == XbrlConst.link: if elt.localName == "presentationArc" and not elt.get( "order"): val.modelXbrl.error( ("EFM.6.12.01", "GFM.1.06.01"), _("PresentationArc from %(xlinkFrom)s to %(xlinkTo)s must have an order" ), modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt)) elif elt.localName == "calculationArc": if not elt.get("order"): val.modelXbrl.error( ("EFM.6.14.01", "GFM.1.07.01"), _("CalculationArc from %(xlinkFrom)s to %(xlinkTo)s must have an order" ), modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt)) try: weightAttr = elt.get("weight") weight = float(weightAttr) if not weight in (1, -1): val.modelXbrl.error( ("EFM.6.14.02", "GFM.1.07.02"), _("CalculationArc from %(xlinkFrom)s to %(xlinkTo)s weight %(weight)s must be 1 or -1" ), modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt), weight=weightAttr) except ValueError: val.modelXbrl.error( ("EFM.6.14.02", "GFM.1.07.02"), _("CalculationArc from %(xlinkFrom)s to %(xlinkTo)s must have an weight (value error in \"%(weight)s\")" ), modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt), weight=weightAttr) elif elt.localName == "definitionArc": if not elt.get("order"): val.modelXbrl.error( ("EFM.6.16.01", "GFM.1.08.01"), _("DefinitionArc from %(xlinkFrom)s to %(xlinkTo)s must have an order" ), modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt))
def presumptionOfTotal(val, rel, siblingRels, iSibling, isStatementSheet, nestedInTotal, checkLabelRoleOnly): """ A numeric concept target of a parent-child relationship is presumed total if: (i) its preferredLabel role is a total role (pre XbrlConst static function of current such total roles) or (ii) if not in a nested total (abstract child relationship to a known total's contributing siblings): the parent is not SupplementalCashFlowInformationAbstract and the preceding sibling relationship is monetary and it's on a statement sheet and it's the last of more than one monetary item (a) Last monetary parented by an abstract or non-monetary and not in a nested (breakdown) total, or (b) effective label (en-US of preferred role) has "Total" in its wording. (c) (commented out for now due to false positives: Concept name has "Total" in its name) (d) last monetary (may be sub level) whose immediate sibling is a calc LB child """ concept = rel.toModelObject if isinstance(concept, ModelConcept) and concept.isNumeric: preferredLabel = rel.preferredLabel if XbrlConst.isTotalRole(preferredLabel): return _("preferredLabel {0}").format(os.path.basename(preferredLabel)) if concept.isMonetary and not checkLabelRoleOnly: effectiveLabel = concept.label(lang="en-US", fallbackToQname=False, preferredLabel=preferredLabel) ''' word total in label/name does not seem to be a good indicator, e.g., Google Total in label for ShareBasedCompensationArrangementByShareBasedPaymentAwardGrantDateFairValueOfOptionsVested followed by label with Aggregate but name has Total ... so only perform this test on last monetary in a Note if 'Total' in effectiveLabel: # also check for Net ??? return _("word 'Total' in effective label {0}").format(effectiveLabel) if 'Total' in concept.name: # also check for Net ??? return _("word 'Total' in concept name {0}").format(concept.name) ''' parent = rel.fromModelObject if (len(siblingRels) > 1 and iSibling == len(siblingRels) - 1 and parent is not None and parent.name not in { "SupplementalCashFlowInformationAbstract" }): preceedingSibling = siblingRels[iSibling - 1].toModelObject if preceedingSibling is not None and preceedingSibling.isMonetary: # last fact, may be total if isStatementSheet: # check if facts add up?? if (parent.isAbstract or not parent.isMonetary) and not nestedInTotal: return _("last monetary item in statement sheet monetary line items parented by nonMonetary concept") elif effectiveLabel and 'Total' in effectiveLabel: return _("last monetary item in statement sheet monetary line items with word 'Total' in effective label {0}").format(effectiveLabel) elif 'Total' in concept.name: return _("last monetary item in statement sheet monetary line items with word 'Total' in concept name {0}").format(concept.name) elif val.summationItemRelsSetAllELRs.isRelated(concept, "child", preceedingSibling): return _("last monetary item in statement sheet monetary line items is calc sum of previous line item") ''' for now unreliable to use total words for notes else: if 'Total' in effectiveLabel: # also check for Net ??? return _("last monetary item in note with word 'Total' in effective label {0}").format(effectiveLabel) if 'Total' in concept.name: # also check for Net ??? return _("last monetary item in note with word 'Total' in concept name {0}").format(concept.name) ''' return None
def labelroles(modelXbrl, includeConceptName=False): # returns sorted list of tuples of arcrole basename and uri return sorted( set((XbrlConst.labelroleLabel(r), r) for r in (modelXbrl.labelroles | ( {XbrlConst.conceptNameLabelRole} if includeConceptName else set())) if r is not None))
def isNumeric(self): try: return self._isNumeric except AttributeError: self._isNumeric = XbrlConst.isNumericXsdType(self.baseXsdType) return self._isNumeric
def validate(self, modelVersReport): self.modelVersReport = modelVersReport versReport = modelVersReport.modelDocument if not hasattr(versReport, "xmlDocument"): # not parsed return for DTSname in ("fromDTS", "toDTS"): DTSmodelXbrl = getattr(versReport, DTSname) if DTSmodelXbrl is None or DTSmodelXbrl.modelDocument is None: self.modelVersReport.error("vere:invalidDTSIdentifier", _("%(dts)s is missing or not loaded"), modelObject=self, dts=DTSname) else: # validate DTS ValidateXbrl.ValidateXbrl(DTSmodelXbrl).validate(DTSmodelXbrl) if len(DTSmodelXbrl.errors) > 0: self.modelVersReport.error("vere:invalidDTSIdentifier", _("%(dts) has errors: %(error)s"), modelObject=DTSmodelXbrl.modelDocument, dts=DTSname, error=DTSmodelXbrl.errors) # validate linkbases ValidateXbrl.ValidateXbrl(self.modelVersReport).validate(modelVersReport) versReportElt = versReport.xmlRootElement # check actions for assignmentRef in versReportElt.iterdescendants(tag="{http://xbrl.org/2010/versioning-base}assignmentRef"): ref = assignmentRef.get("ref") if ref not in versReport.idObjects or \ not isinstance(versReport.idObjects[ref], ModelVersObject.ModelAssignment): self.modelVersReport.error("vere:invalidAssignmentRef", _("AssignmentRef %(assignmentRef)s does not reference an assignment"), modelObject=assignmentRef, assignmentRef=ref) # check namespace renames for NSrename in versReport.namespaceRenameFrom.values(): if NSrename.fromURI not in versReport.fromDTS.namespaceDocs: self.modelVersReport.error("vere:invalidNamespaceMapping", _("NamespaceRename fromURI %(uri)s does not reference a schema in fromDTS"), modelObject=self, uri=NSrename.fromURI) if NSrename.toURI not in versReport.toDTS.namespaceDocs: self.modelVersReport.error("vere:invalidNamespaceMapping", _("NamespaceRename toURI %(uri)s does not reference a schema in toDTS"), modelObject=self, uri=NSrename.toURI) # check role changes for roleChange in versReport.roleChanges.values(): if roleChange.fromURI not in versReport.fromDTS.roleTypes: self.modelVersReport.error("vere:invalidRoleChange", _("RoleChange fromURI %(uri)s does not reference a roleType in fromDTS"), modelObject=self, uri=roleChange.fromURI) if roleChange.toURI not in versReport.toDTS.roleTypes: self.modelVersReport.error("vere:invalidRoleChange", _("RoleChange toURI %(uri)s does not reference a roleType in toDTS"), modelObject=self, uri=roleChange.toURI) # check reportRefs # check actions for reportRef in versReportElt.iterdescendants(tag="{http://xbrl.org/2010/versioning-base}reportRef"): # if existing it must be valid href = reportRef.get("{http://www.w3.org/1999/xlink}href") # TBD if versReport.fromDTS and versReport.toDTS: # check concept changes of concept basic for conceptChange in versReport.conceptUseChanges: fromConceptQn = conceptChange.fromConceptQname toConceptQn = conceptChange.toConceptQname if (conceptChange.name != "conceptAdd" and (fromConceptQn is None or fromConceptQn not in versReport.fromDTS.qnameConcepts)): self.modelVersReport.error("vercue:invalidConceptReference", _("%(event)s fromConcept %(concept)s does not reference a concept in fromDTS"), modelObject=conceptChange, event=conceptChange.name, concept=conceptChange.fromConceptQname) if (conceptChange.name != "conceptDelete" and (toConceptQn is None or toConceptQn not in versReport.toDTS.qnameConcepts)): self.modelVersReport.error("vercue:invalidConceptReference", _("%(event)s toConcept %(concept)s does not reference a concept in toDTS"), modelObject=conceptChange, event=conceptChange.name, concept=conceptChange.toConceptQname) if (conceptChange.name == "conceptAdd" and toConceptQn is not None and conceptChange.isPhysical ^ (qname(versReport.namespaceRenameTo.get(toConceptQn.namespaceURI, toConceptQn.namespaceURI), toConceptQn.localName) not in versReport.fromDTS.qnameConcepts)): self.modelVersReport.error("vercue:inconsistentPhysicalAttribute", _("%(event)s toConcept %(concept)s physical attribute conflicts with presence in fromDTS"), modelObject=conceptChange, event=conceptChange.name, concept=conceptChange.toConceptQname) if (conceptChange.name == "conceptDelete" and toConceptQn is not None and conceptChange.isPhysical ^ (qname(versReport.namespaceRenameFrom.get(fromConceptQn.namespaceURI, fromConceptQn.namespaceURI), fromConceptQn.localName) in versReport.toDTS.qnameConcepts)): self.modelVersReport.error("vercue:inconsistentPhysicalAttribute", _("%(event)s toConcept %(concept)s physical attribute conflicts with presence in toDTS"), modelObject=conceptChange, event=conceptChange.name, concept=conceptChange.toConceptQname) # check concept changes of concept extended equivalentAttributes = {} for conceptChange in versReport.conceptDetailsChanges: fromConcept = conceptChange.fromConcept toConcept = conceptChange.toConcept fromResource = conceptChange.fromResource toResource = conceptChange.toResource # fromConcept checks if not conceptChange.name.endswith("Add"): if not fromConcept is not None: self.modelVersReport.error("vercue:invalidConceptReference", _("%(action)s %(event)s fromConcept %(concept)s does not reference a concept in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.fromConceptQname) # tuple check elif _("Child") in conceptChange.name and \ not versReport.fromDTS.qnameConcepts[fromConcept.qname] \ .isTuple: self.modelVersReport.error("vercue:invalidConceptReference", _("%(action)s %(event)s fromConcept %(concept)s must be defined as a tuple"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.fromConceptQname) # resource check elif "Label" in conceptChange.name: if fromResource is None: self.modelVersReport.error("vercde:invalidResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not reference a resource in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.conceptLabel) if relationship is not None: if (relationship.qname != XbrlConst.qnLinkLabelArc or relationship.parentQname != XbrlConst.qnLinkLabelLink or fromResource.qname != XbrlConst.qnLinkLabel): self.modelVersReport.error("vercde:invalidConceptLabelIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.elementLabel) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error("vercde:invalidConceptLabelIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: self.modelVersReport.error("vercde:invalidResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not have a label relationship to {3} in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue) elif "Reference" in conceptChange.name: if fromResource is None: self.modelVersReport.error("vercde:invalidResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not reference a resource in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.conceptReference) if relationship is not None: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ fromResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error("vercde:invalidConceptReferenceIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: relationship = fromConcept.relationshipToResource(fromResource, XbrlConst.elementReference) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ fromResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error("vercde:invalidConceptReferenceIdentifier", _("%(action)s %(event)s fromResource %(resource)s for %(concept)s in fromDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) else: self.modelVersReport.error("vercde:invalidResourceIdentifier", _("%(action)s %(event)s fromResource %(resource)s does not have a reference relationship to %(concept)s in fromDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.fromResourceValue, concept=conceptChange.fromConceptQname) # toConcept checks if not conceptChange.name.endswith("Delete"): if not toConcept is not None: self.modelVersReport.error("vercue:invalidConceptReference", _("%(action)s %(event)s toConcept %(concept)s does not reference a concept in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.toConceptQname) # tuple check elif "Child" in conceptChange.name and \ not versReport.toDTS.qnameConcepts[toConcept.qname] \ .isTuple: self.modelVersReport.error("vercue:invalidConceptReference", _("%(action)s %(event)s toConcept %(concept)s must be defined as a tuple"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=conceptChange.toConceptQname) # resource check elif "Label" in conceptChange.name: if toResource is None: self.modelVersReport.error("vercde:invalidResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s does not reference a resource in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) elif toResource.qname not in (XbrlConst.qnLinkLabel, XbrlConst.qnGenLabel): self.modelVersReport.error("vercde:invalidConceptLabelIdentifier", _("%(action)s %(event)s toResource %(resource)s is not a label in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.conceptLabel) if relationship is not None: if relationship.qname != XbrlConst.qnLinkLabelArc or \ relationship.parentQname != XbrlConst.qnLinkLabelLink or \ toResource.qname != XbrlConst.qnLinkLabel: self.modelVersReport.error("vercde:invalidConceptLabelIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.elementLabel) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenLabel: self.modelVersReport.error("vercde:invalidConceptLabelIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: self.modelVersReport.error("vercde:invalidConceptResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s does not have a label relationship to %(concept)s in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) elif "Reference" in conceptChange.name: if toResource is None: self.modelVersReport.error("vercde:invalidResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s does not reference a resource in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue) elif toResource.qname not in (XbrlConst.qnLinkReference, XbrlConst.qnGenReference): self.modelVersReport.error("vercde:invalidConceptReferenceIdentifier", _("%(action)s %(event)s toResource %(resource)s is not a reference in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.conceptReference) if relationship is not None: if relationship.qname != XbrlConst.qnLinkReferenceArc or \ relationship.parentQname != XbrlConst.qnLinkReferenceLink or \ toResource.qname != XbrlConst.qnLinkReference: self.modelVersReport.error("vercde:invalidConceptReferenceIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: relationship = toConcept.relationshipToResource(toResource, XbrlConst.elementReference) if relationship is not None: if relationship.qname != XbrlConst.qnGenArc or \ toResource.qname != XbrlConst.qnGenReference: self.modelVersReport.error("vercde:invalidConceptReferenceIdentifier", _("%(action)s %(event)s toResource %(resource)s for %(concept)s in toDTS does not have expected link, arc, or label elements"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) else: self.modelVersReport.error("vercde:invalidConceptResourceIdentifier", _("%(action)s %(event)s toResource %(resource)s does not have a reference relationship to %(concept)s in toDTS"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, resource=conceptChange.toResourceValue, concept=conceptChange.toConceptQname) # check concept correspondence if fromConcept is not None and toConcept is not None: if (versReport.toDTSqname(fromConcept.qname) != toConcept.qname and versReport.equivalentConcepts.get(fromConcept.qname) != toConcept.qname and toConcept.qname not in versReport.relatedConcepts.get(fromConcept.qname,[])): self.modelVersReport.error("vercde:invalidConceptCorrespondence", _("%(action)s %(event)s fromConcept %(conceptFrom)s and toConcept %(conceptTo)s must be equivalent or related"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, conceptFrom=conceptChange.fromConceptQname, conceptTo=conceptChange.toConceptQname) # custom attribute events if conceptChange.name.startswith("conceptAttribute") or conceptChange.name == "attributeDefinitionChange": try: for attr in conceptAttributeEventAttributes[conceptChange.name]: customAttributeQname = conceptChange.customAttributeQname(attr) if not customAttributeQname: self.modelVersReport.info("arelle:invalidAttributeChange", _("%(action)s %(event)s %(attr)s $(attrName)s does not have a name"), modelObject=conceptChange, action=conceptChange.actionId, attr=attr, attrName=customAttributeQname) elif customAttributeQname.namespaceURI in (None, XbrlConst.xbrli, XbrlConst.xsd): self.modelVersReport.error("vercde:illegalCustomAttributeEvent", _("%(action)s %(event)s %(attr)s $(attrName)s has an invalid namespace"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, attr=attr, attrName=customAttributeQname) except KeyError: self.modelVersReport.info("arelle:eventNotRecognized", _("%(action)s %(event)s event is not recognized"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name) if conceptChange.name == "attributeDefinitionChange": fromAttr = conceptChange.customAttributeQname("fromCustomAttribute") toAttr = conceptChange.customAttributeQname("toCustomAttribute") equivalentAttributes[fromAttr] = toAttr equivalentAttributes[toAttr] = fromAttr # check item concept identifiers if conceptChange.name in ("conceptPeriodTypeChange", "conceptPeriodTypeChange"): for concept in (fromConcept, toConcept): if concept is not None and not concept.isItem: self.modelVersReport.error("vercde:invalidItemConceptIdentifier", _("%(action)s %(event)s concept %(concept)s does not reference an item concept."), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=concept.qname) # check tuple concept identifiers if conceptChange.name in ("tupleContentModelChange", ): for concept in (fromConcept, toConcept): if concept is not None and not concept.isItem: self.modelVersReport.error("vercde:invalidTupleConceptIdentifier", _("%(action)s %(event)s concept %(concept)s does not reference a tuple concept."), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, concept=concept.qname) if conceptChange.name in schemaAttributeEventAttributes: attr = schemaAttributeEventAttributes[conceptChange.name] if (fromConcept is not None and not fromConcept.get(attr) and toConcept is not None and not toConcept.get(attr)): self.modelVersReport.error("vercde:illegalSchemaAttributeChangeEvent", _("%(action)s %(event)s neither concepts have a %(attribute)s attribute: %(fromConcept)s, %(toConcept)s."), modelObject=conceptChange, action=conceptChange.actionId, attribute=attr, event=conceptChange.name, fromConcept=fromConcept.qname, toConcept=toConcept.qname) # check concept changes for equivalent attributes for conceptChange in versReport.conceptDetailsChanges: if conceptChange.name == "conceptAttributeChange": fromAttr = conceptChange.customAttributeQname("fromCustomAttribute") toAttr = conceptChange.customAttributeQname("toCustomAttribute") if (equivalentAttributes.get(fromAttr) != toAttr and (fromAttr.localName != toAttr.localName or (fromAttr.namespaceURI != toAttr.namespaceURI and versReport.namespaceRenameFrom.get(fromAttr.namespaceURI, fromAttr.namespaceURI) != toAttr.namespaceURI))): self.modelVersReport.error("vercde:invalidAttributeCorrespondence", _("%(action)s %(event)s has non-equivalent attributes %(fromQname)s and %(toQname)s"), modelObject=conceptChange, action=conceptChange.actionId, event=conceptChange.name, fromQname=fromAttr, toQname=toAttr) del equivalentAttributes # dereference # check relationship set changes for relSetChange in versReport.relationshipSetChanges: for relationshipSet, name in ((relSetChange.fromRelationshipSet, "fromRelationshipSet"), (relSetChange.toRelationshipSet, "toRelationshipSet")): if relationshipSet is not None: dts = relationshipSet.dts relationshipSetValid = True if relationshipSet.link: if (relationshipSet.link not in dts.qnameConcepts or (dts.qnameConcepts[relationshipSet.link].type is not None and not dts.qnameConcepts[relationshipSet.link].type.isDerivedFrom(XbrlConst.qnXlExtendedType))): self.modelVersReport.error("verrelse:invalidLinkElementReferenceEvent", _("%(event)s %(relSet)s link %(link)s does not reference an element in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, link=relationshipSet.link) relationshipSetValid = False if relationshipSet.arc: if (relationshipSet.arc not in dts.qnameConcepts or (dts.qnameConcepts[relationshipSet.arc].type is not None and not dts.qnameConcepts[relationshipSet.arc].type.isDerivedFrom(XbrlConst.qnXlArcType))): self.modelVersReport.error("verrelse:invalidArcElementReferenceEvent", _("%(event)s %(relSet)s arc %(arc) does not reference an element in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, arc=relationshipSet.arc) relationshipSetValid = False if relationshipSet.linkrole: if not (XbrlConst.isStandardRole(relationshipSet.linkrole) or relationshipSet.linkrole in relationshipSet.dts.roleTypes): self.modelVersReport.error("verrelse:invalidLinkrole", _("%(event)s %(relSet)s linkrole %(linkrole)s does not reference an linkrole in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, linkrole=relationshipSet.linkrole) relationshipSetValid = False elif not any(linkrole == relationshipSet.linkrole for arcrole, linkrole, linkqname, arcqname in dts.baseSets.keys()): self.modelVersReport.error("verrelse:invalidLinkrole", _("%(event)s %(relSet)s linkrole %(linkrole)s is not used in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, linkrole=relationshipSet.linkrole) relationshipSetValid = False if relationshipSet.arcrole: if not (XbrlConst.isStandardArcrole(relationshipSet.arcrole) or relationshipSet.arcrole in relationshipSet.dts.arcroleTypes): self.modelVersReport.error("verrelse:invalidArcrole", _("%(event)s %(relSet)s arcrole %(arcrole)s does not reference an arcrole in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, arcrole=relationshipSet.arcrole) relationshipSetValid = False elif not any(arcrole == relationshipSet.arcrole for arcrole, linkrole, linkqname, arcqname in dts.baseSets.keys()): self.modelVersReport.error("verrelse:invalidArcrole", _("%(event)s %(relSet)s arcrole %(arcrole)s is not used in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, arcrole=relationshipSet.arcrole) relationshipSetValid = False for relationship in relationshipSet.relationships: # fromConcept checks if relationship.fromConcept is None: self.modelVersReport.error("vercue:invalidConceptReference", _("%(event)s %(relSet)s relationship fromConcept %(conceptFrom)s does not reference a concept in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptFrom=relationship.fromName) relationshipSetValid = False if relationship.toName and relationship.toConcept is None: self.modelVersReport.error("vercue:invalidConceptReference", _("%(event)s %(relSet)s relationship toConcept %(conceptTo)s does not reference a concept in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptTo=relationship.toName) relationshipSetValid = False if relationshipSetValid: # test that relations exist if relationship.fromRelationship is None: if relationship.toName: self.modelVersReport.error("verrelse:invalidRelationshipReference", _("%(event)s %(relSet)s no relationship found from fromConcept %(conceptFrom)s to toConcept %(conceptTo)s in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptFrom=relationship.fromName, conceptTo=relationship.toName) else: self.modelVersReport.error("verrelse:invalidRelationshipReference", _("%(event)s %(relSet)s no relationship found fromConcept %(conceptFrom)s in its DTS"), modelObject=relSetChange, event=relSetChange.name, relSet=name, conceptFrom=relationship.fromName) # check instance aspect changes for iaChange in versReport.instanceAspectChanges: for instAspects in (iaChange.fromAspects, iaChange.toAspects): if instAspects is not None and instAspects.aspects: dimAspectElts = {} for aspect in instAspects.aspects: dts = aspect.modelAspects.dts if (aspect.localName in ("explicitDimension", "typedDimension") and aspect.concept is None): self.modelVersReport.error("vercue:invalidConceptReference", _("%(event)s dimension %(dimension)s is not a concept in its DTS"), modelObject=aspect, event=iaChange.name, dimension=aspect.conceptName) elif aspect.localName == "explicitDimension": dimConcept = aspect.concept if not dimConcept.isExplicitDimension: self.modelVersReport.error("verdime:invalidExplicitDimensionIdentifier", _("%(event)s dimension %(dimension)s is not an explicit dimension in its DTS"), modelObject=aspect, event=iaChange.name, dimension=aspect.conceptName) if dimConcept in dimAspectElts: self.modelVersReport.error("verdime:duplicateExplicitDimensionAspect", _("%(event)s dimension %(dimension)s is duplicated in a single explicitDimension element"), modelObject=(aspect, dimAspectElts[dimConcept]), event=iaChange.name, dimension=aspect.conceptName) else: dimAspectElts[dimConcept] = aspect elif aspect.localName == "typedDimension": dimConcept = aspect.concept if not dimConcept.isTypedDimension: self.modelVersReport.error("verdime:invalidTypedDimensionIdentifier", _("%(event)s dimension %(dimension)s is not a typed dimension in its DTS"), modelObject=aspect, event=iaChange.name, dimension=aspect.conceptName) if dimConcept in dimAspectElts: self.modelVersReport.error("verdime:duplicateTypedDimensionAspect", _("%(event)s dimension %(dimension)s is duplicated in a single explicitDimension element"), modelObject=(aspect, dimAspectElts[dimConcept]), event=iaChange.name, dimension=aspect.conceptName) else: dimAspectElts[dimConcept] = aspect if aspect.localName in ("explicitDimension", "concepts"): for relatedConcept in aspect.relatedConcepts: conceptMdlObj = relatedConcept.concept if conceptMdlObj is None or not conceptMdlObj.isItem: self.modelVersReport.error("vercue:invalidConceptReference", _("%(event)s concept %(concept)s is not an item in its DTS"), modelObject=aspect, event=iaChange.name, concept=relatedConcept.conceptName) if relatedConcept.arcrole is not None: if (not XbrlConst.isStandardArcrole(relatedConcept.arcrole) and relatedConcept.arcrole not in dts.arcroleTypes): self.modelVersReport.error("verdime:invalidURI", _("%(event)s arcrole %(arcrole)s is not defined in its DTS"), modelObject=aspect, event=iaChange.name, arcrole=relatedConcept.arcrole) elif not any(arcrole == relatedConcept.arcrole for arcrole, linkrole, linkqname, arcqname in dts.baseSets.keys()): self.modelVersReport.error("verdime:invalidURI", _("%(event)s arcrole %(arcrole)s is not used in its DTS"), modelObject=aspect, event=iaChange.name, linkrole=relatedConcept.arcrole) if relatedConcept.linkrole is not None: if (relatedConcept.linkrole != "http://www.xbrl.org/2003/role/link" and relatedConcept.linkrole not in dts.roleTypes): self.modelVersReport.error("verdime:invalidURI", _("%(event)s linkrole %(linkrole)s is not defined in its DTS"), modelObject=aspect, event=iaChange.name, linkrole=relatedConcept.linkrole) elif not any(linkrole == relatedConcept.linkrole for arcrole, linkrole, linkqname, arcqname in dts.baseSets.keys()): self.modelVersReport.error("verdime:invalidURI", _("%(event)s linkrole %(linkrole)s is not used in its DTS"), modelObject=aspect, event=iaChange.name, linkrole=relatedConcept.linkrole) if (relatedConcept.arc is not None and (relatedConcept.arc not in dts.qnameConcepts or (dts.qnameConcepts[relatedConcept.arc].type is not None and not dts.qnameConcepts[relatedConcept.arc].type.isDerivedFrom(XbrlConst.qnXlArcType)))): self.modelVersReport.error("verdime:invalidArcElement", _("%(event)s arc %(arc)s is not defined as an arc in its DTS"), modelObject=aspect, event=iaChange.name, arc=relatedConcept.arc) if (relatedConcept.link is not None and (relatedConcept.link not in dts.qnameConcepts or (dts.qnameConcepts[relatedConcept.link].type is not None and not dts.qnameConcepts[relatedConcept.link].type.isDerivedFrom(XbrlConst.qnXlExtendedType)))): self.modelVersReport.error("verdime:invalidLinkElement", _("%(event)s link %(link)s is not defined in its DTS"), modelObject=aspect, event=iaChange.name, link=relatedConcept.link) self.close()
def groupRelationshipLabel(arcrole): if isinstance(arcrole, (list,tuple)): # (group-name, [arcroles]) arcroleName = arcrole[0] else: arcroleName = XbrlConst.baseSetArcroleLabel(arcrole)[1:] return arcroleName
def baseSetArcroles(modelXbrl): # returns sorted list of tuples of arcrole basename and uri return sorted(set((XbrlConst.baseSetArcroleLabel(b[0]),b[0]) for b in modelXbrl.baseSets.keys()))
def checkFilingDTS(val, modelDocument, isEFM, isGFM, visited): global targetNamespaceDatePattern, efmFilenamePattern, htmlFileNamePattern, roleTypePattern, arcroleTypePattern, \ arcroleDefinitionPattern, namePattern, linkroleDefinitionBalanceIncomeSheet, \ namespacesConflictPattern if targetNamespaceDatePattern is None: targetNamespaceDatePattern = re.compile( r"/([12][0-9]{3})-([01][0-9])-([0-3][0-9])|" r"/([12][0-9]{3})([01][0-9])([0-3][0-9])|") efmFilenamePattern = re.compile( r"^[a-z0-9][a-zA-Z0-9_\.\-]*(\.xsd|\.xml|\.htm)$") htmlFileNamePattern = re.compile( r"^[a-zA-Z0-9][._a-zA-Z0-9-]*(\.htm)$") roleTypePattern = re.compile(r"^.*/role/[^/\s]+$") arcroleTypePattern = re.compile(r"^.*/arcrole/[^/\s]+$") arcroleDefinitionPattern = re.compile( r"^.*[^\\s]+.*$") # at least one non-whitespace character namePattern = re.compile( "[][()*+?\\\\/^{}|@#%^=~`\"';:,<>&$\u00a3\u20ac]" ) # u20ac=Euro, u00a3=pound sterling linkroleDefinitionBalanceIncomeSheet = re.compile( r"[^-]+-\s+Statement\s+-\s+.*(income|balance|financial\W+position)", re.IGNORECASE) namespacesConflictPattern = re.compile( r"http://(xbrl\.us|fasb\.org|xbrl\.sec\.gov)/(dei|us-types|us-roles|rr)/([0-9]{4}-[0-9]{2}-[0-9]{2})$" ) nonDomainItemNameProblemPattern = re.compile( r"({0})|(FirstQuarter|SecondQuarter|ThirdQuarter|FourthQuarter|[1-4]Qtr|Qtr[1-4]|ytd|YTD|HalfYear)(?:$|[A-Z\W])" .format(re.sub(r"\W", "", (val.entityRegistrantName or "").title()))) visited.append(modelDocument) for referencedDocument, modelDocumentReference in modelDocument.referencesDocument.items( ): #6.07.01 no includes if modelDocumentReference.referenceType == "include": val.modelXbrl.error( ("EFM.6.07.01", "GFM.1.03.01"), _("Taxonomy schema %(schema)s includes %(include)s, only import is allowed" ), modelObject=modelDocumentReference.referringModelObject, schema=os.path.basename(modelDocument.uri), include=os.path.basename(referencedDocument.uri)) if referencedDocument not in visited and referencedDocument.inDTS: # ignore EdgarRenderer added non-DTS documents checkFilingDTS(val, referencedDocument, isEFM, isGFM, visited) if val.disclosureSystem.standardTaxonomiesDict is None: pass if isEFM: if modelDocument.uri in val.disclosureSystem.standardTaxonomiesDict: if modelDocument.targetNamespace: # check for duplicates of us-types, dei, and rr taxonomies match = namespacesConflictPattern.match( modelDocument.targetNamespace) if match is not None: val.standardNamespaceConflicts[match.group(2)].add( modelDocument) else: if len(modelDocument.basename) > 32: val.modelXbrl.error( "EFM.5.01.01.tooManyCharacters", _("Document file name %(filename)s must not exceed 32 characters." ), modelObject=modelDocument, filename=modelDocument.basename) if modelDocument.type == ModelDocument.Type.INLINEXBRL: if not htmlFileNamePattern.match(modelDocument.basename): val.modelXbrl.error( "EFM.5.01.01", _("Document file name %(filename)s must start with a-z or 0-9, contain upper or lower case letters, ., -, _, and end with .htm." ), modelObject=modelDocument, filename=modelDocument.basename) elif not efmFilenamePattern.match(modelDocument.basename): val.modelXbrl.error( "EFM.5.01.01", _("Document file name %(filename)s must start with a-z or 0-9, contain upper or lower case letters, ., -, _, and end with .xsd or .xml." ), modelObject=modelDocument, filename=modelDocument.basename) if (modelDocument.type == ModelDocument.Type.SCHEMA and modelDocument.targetNamespace not in val.disclosureSystem.baseTaxonomyNamespaces and modelDocument.uri.startswith(val.modelXbrl.uriDir)): val.hasExtensionSchema = True # check schema contents types # 6.7.3 check namespace for standard authority targetNamespaceAuthority = UrlUtil.authority( modelDocument.targetNamespace) if targetNamespaceAuthority in val.disclosureSystem.standardAuthorities: val.modelXbrl.error( ("EFM.6.07.03", "GFM.1.03.03"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s is a disallowed authority" ), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace, targetNamespaceAuthority=UrlUtil.authority( modelDocument.targetNamespace, includeScheme=False)) # 6.7.4 check namespace format if modelDocument.targetNamespace is None or not modelDocument.targetNamespace.startswith( "http://"): match = None else: targetNamespaceDate = modelDocument.targetNamespace[ len(targetNamespaceAuthority):] match = targetNamespaceDatePattern.match(targetNamespaceDate) if match is not None: try: if match.lastindex == 3: date = datetime.date(int(match.group(1)), int(match.group(2)), int(match.group(3))) elif match.lastindex == 6: date = datetime.date(int(match.group(4)), int(match.group(5)), int(match.group(6))) else: match = None except ValueError: match = None if match is None: val.modelXbrl.error( ("EFM.6.07.04", "GFM.1.03.04"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s must have format http://{authority}/{versionDate}" ), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace) elif val.fileNameDate and date > val.fileNameDate: val.modelXbrl.info( ("EFM.6.07.06", "GFM.1.03.06"), _("Warning: Taxonomy schema %(schema)s namespace %(targetNamespace)s has date later than document name date %(docNameDate)s" ), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace, docNameDate=val.fileNameDate) if modelDocument.targetNamespace is not None: # 6.7.5 check prefix for _ authority = UrlUtil.authority(modelDocument.targetNamespace) if not re.match(r"(http://|https://|ftp://|urn:)\w+", authority): val.modelXbrl.error( ("EFM.6.07.05", "GFM.1.03.05"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s must be a valid URL with a valid authority for the namespace." ), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace) prefix = XmlUtil.xmlnsprefix(modelDocument.xmlRootElement, modelDocument.targetNamespace) if not prefix: val.modelXbrl.error( ("EFM.6.07.07", "GFM.1.03.07"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s missing prefix for the namespace." ), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace) elif "_" in prefix: val.modelXbrl.error( ("EFM.6.07.07", "GFM.1.03.07"), _("Taxonomy schema %(schema)s namespace %(targetNamespace)s prefix %(prefix)s must not have an '_'" ), modelObject=modelDocument, schema=os.path.basename(modelDocument.uri), targetNamespace=modelDocument.targetNamespace, prefix=prefix) for modelConcept in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.w3.org/2001/XMLSchema}element"): if isinstance(modelConcept, ModelConcept): # 6.7.16 name not duplicated in standard taxonomies name = modelConcept.get("name") if name is None: name = "" if modelConcept.get("ref") is not None: continue # don't validate ref's here for c in val.modelXbrl.nameConcepts.get(name, []): if c.modelDocument != modelDocument: if not c.modelDocument.uri.startswith( val.modelXbrl.uriDir): val.modelXbrl.error( ("EFM.6.07.16", "GFM.1.03.18"), _("Concept %(concept)s is also defined in standard taxonomy schema schema %(standardSchema)s" ), modelObject=(modelConcept, c), concept=modelConcept.qname, standardSchema=os.path.basename( c.modelDocument.uri), standardConcept=c.qname) # 6.7.17 id properly formed _id = modelConcept.id requiredId = (prefix if prefix is not None else "") + "_" + name if _id != requiredId: val.modelXbrl.error( ("EFM.6.07.17", "GFM.1.03.19"), _("Concept %(concept)s id %(id)s should be %(requiredId)s" ), modelObject=modelConcept, concept=modelConcept.qname, id=_id, requiredId=requiredId) # 6.7.18 nillable is true nillable = modelConcept.get("nillable") if nillable != "true" and modelConcept.isItem: val.modelXbrl.error( ("EFM.6.07.18", "GFM.1.03.20"), _("Taxonomy schema %(schema)s element %(concept)s nillable %(nillable)s should be 'true'" ), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=name, nillable=nillable) # 6.7.19 not tuple if modelConcept.isTuple: val.modelXbrl.error( ("EFM.6.07.19", "GFM.1.03.21"), _("Concept %(concept)s is a tuple"), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.20 no typed domain ref if modelConcept.isTypedDimension: val.modelXbrl.error( ("EFM.6.07.20", "GFM.1.03.22"), _("Concept %(concept)s has typedDomainRef %(typedDomainRef)s" ), modelObject=modelConcept, concept=modelConcept.qname, typedDomainRef=modelConcept.typedDomainElement. qname if modelConcept.typedDomainElement is not None else modelConcept.typedDomainRef) # 6.7.21 abstract must be duration isDuration = modelConcept.periodType == "duration" if modelConcept.isAbstract and not isDuration: val.modelXbrl.error( ("EFM.6.07.21", "GFM.1.03.23"), _("Taxonomy schema %(schema)s element %(concept)s is abstract but period type is not duration" ), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=modelConcept.qname) # 6.7.22 abstract must be stringItemType ''' removed SEC EFM v.17, Edgar release 10.4, and GFM 2011-04-08 if modelConcept.abstract == "true" and modelConcept.typeQname != XbrlConst. qnXbrliStringItemType: val.modelXbrl.error(("EFM.6.07.22", "GFM.1.03.24"), _("Concept %(concept)s is abstract but type is not xbrli:stringItemType"), modelObject=modelConcept, concept=modelConcept.qname) ''' substitutionGroupQname = modelConcept.substitutionGroupQname # 6.7.23 Axis must be subs group dimension if name.endswith("Axis") ^ ( substitutionGroupQname == XbrlConst.qnXbrldtDimensionItem): val.modelXbrl.error( ("EFM.6.07.23", "GFM.1.03.25"), _("Concept %(concept)s must end in Axis to be in xbrldt:dimensionItem substitution group" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.24 Table must be subs group hypercube if name.endswith("Table") ^ ( substitutionGroupQname == XbrlConst.qnXbrldtHypercubeItem): val.modelXbrl.error( ("EFM.6.07.24", "GFM.1.03.26"), _("Concept %(concept)s must end in Table to be in xbrldt:hypercubeItem substitution group" ), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=modelConcept.qname) # 6.7.25 if neither hypercube or dimension, substitution group must be item if substitutionGroupQname not in ( None, XbrlConst.qnXbrldtDimensionItem, XbrlConst.qnXbrldtHypercubeItem, XbrlConst.qnXbrliItem): val.modelXbrl.error( ("EFM.6.07.25", "GFM.1.03.27"), _("Concept %(concept)s has disallowed substitution group %(substitutionGroup)s" ), modelObject=modelConcept, concept=modelConcept.qname, substitutionGroup=modelConcept. substitutionGroupQname) # 6.7.26 Table must be subs group hypercube if name.endswith( "LineItems") and modelConcept.abstract != "true": val.modelXbrl.error( ("EFM.6.07.26", "GFM.1.03.28"), _("Concept %(concept)s is a LineItems but not abstract" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.27 type domainMember must end with Domain or Member conceptType = modelConcept.type isDomainItemType = conceptType is not None and conceptType.isDomainItemType endsWithDomainOrMember = name.endswith( "Domain") or name.endswith("Member") if isDomainItemType != endsWithDomainOrMember: val.modelXbrl.error( ("EFM.6.07.27", "GFM.1.03.29"), _("Concept %(concept)s must end with Domain or Member for type of domainItemType" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.28 domainItemType must be duration if isDomainItemType and not isDuration: val.modelXbrl.error( ("EFM.6.07.28", "GFM.1.03.30"), _("Concept %(concept)s is a domainItemType and must be periodType duration" ), modelObject=modelConcept, concept=modelConcept.qname) #6.7.31 (version 27) fractions if modelConcept.isFraction: val.modelXbrl.error( "EFM.6.07.31", _("Concept %(concept)s is a fraction"), modelObject=modelConcept, concept=modelConcept.qname) #6.7.32 (version 27) instant non numeric if modelConcept.isItem and (not modelConcept.isNumeric and not isDuration and not modelConcept.isAbstract and not isDomainItemType): val.modelXbrl.error( "EFM.6.07.32", _("Taxonomy schema %(schema)s element %(concept)s is non-numeric but period type is not duration" ), modelObject=modelConcept, schema=os.path.basename(modelDocument.uri), concept=modelConcept.qname) # 6.8.5 semantic check, check LC3 name if name: if not name[0].isupper(): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.05.firstLetter", "GFM.2.03.05.firstLetter"), _("Concept %(concept)s name must start with a capital letter" ), modelObject=modelConcept, concept=modelConcept.qname) if namePattern.search(name): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.05.disallowedCharacter", "GFM.2.03.05.disallowedCharacter"), _("Concept %(concept)s has disallowed name character" ), modelObject=modelConcept, concept=modelConcept.qname) if len(name) > 200: val.modelXbrl.log( "ERROR-SEMANTIC", "EFM.6.08.05.nameLength", _("Concept %(concept)s name length %(namelength)s exceeds 200 characters" ), modelObject=modelConcept, concept=modelConcept.qname, namelength=len(name)) if isEFM: label = modelConcept.label(lang="en-US", fallbackToQname=False) if label: # allow Joe's Bar, N.A. to be JoesBarNA -- remove ', allow A. as not article "a" lc3name = ''.join( re.sub(r"['.-]", "", ( w[0] or w[2] or w[3] or w[4])).title() for w in re.findall( r"((\w+')+\w+)|(A[.-])|([.-]A(?=\W|$))|(\w+)", label ) # EFM implies this should allow - and . re.findall(r"[\w\-\.]+", label) if w[4].lower() not in ("the", "a", "an")) if not (name == lc3name or (name and lc3name and lc3name[0].isdigit() and name[1:] == lc3name and (name[0].isalpha() or name[0] == '_'))): val.modelXbrl.log( "WARNING-SEMANTIC", "EFM.6.08.05.LC3", _("Concept %(concept)s should match expected LC3 composition %(lc3name)s" ), modelObject=modelConcept, concept=modelConcept.qname, lc3name=lc3name) if conceptType is not None: # 6.8.6 semantic check if not isDomainItemType and conceptType.qname != XbrlConst.qnXbrliDurationItemType: nameProblems = nonDomainItemNameProblemPattern.findall( name) if any( any(t) for t in nameProblems ): # list of tuples with possibly nonempty strings val.modelXbrl.log( "WARNING-SEMANTIC", ("EFM.6.08.06", "GFM.2.03.06"), _("Concept %(concept)s should not contain company or period information, found: %(matches)s" ), modelObject=modelConcept, concept=modelConcept.qname, matches=", ".join(''.join(t) for t in nameProblems)) if conceptType.qname == XbrlConst.qnXbrliMonetaryItemType: if not modelConcept.balance: # 6.8.11 may not appear on a income or balance statement if any( linkroleDefinitionBalanceIncomeSheet. match(roleType.definition) for rel in val.modelXbrl. relationshipSet(XbrlConst.parentChild). toModelObject(modelConcept) for roleType in val.modelXbrl. roleTypes.get(rel.linkrole, ())): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.11", "GFM.2.03.11"), _("Concept %(concept)s must have a balance because it appears in a statement of income or balance sheet" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.11.5 semantic check, must have a documentation label stdLabel = modelConcept.label( lang="en-US", fallbackToQname=False) defLabel = modelConcept.label( preferredLabel=XbrlConst. documentationLabel, lang="en-US", fallbackToQname=False) if not defLabel or ( # want different words than std label stdLabel and re.findall(r"\w+", stdLabel) == re.findall(r"\w+", defLabel)): val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.11.05", "GFM.2.04.04"), _("Concept %(concept)s is monetary without a balance and must have a documentation label that disambiguates its sign" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.16 semantic check if conceptType.qname == XbrlConst.qnXbrliDateItemType and modelConcept.periodType != "duration": val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.16", "GFM.2.03.16"), _("Concept %(concept)s of type xbrli:dateItemType must have periodType duration" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.8.17 semantic check if conceptType.qname == XbrlConst.qnXbrliStringItemType and modelConcept.periodType != "duration": val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.17", "GFM.2.03.17"), _("Concept %(concept)s of type xbrli:stringItemType must have periodType duration" ), modelObject=modelConcept, concept=modelConcept.qname) # 6.7.8 check for embedded linkbase for e in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.xbrl.org/2003/linkbase}linkbase"): if isinstance(e, ModelObject): val.modelXbrl.error( ("EFM.6.07.08", "GFM.1.03.08"), _("Taxonomy schema %(schema)s contains an embedded linkbase" ), modelObject=e, schema=modelDocument.basename) break requiredUsedOns = { XbrlConst.qnLinkPresentationLink, XbrlConst.qnLinkCalculationLink, XbrlConst.qnLinkDefinitionLink } standardUsedOns = { XbrlConst.qnLinkLabel, XbrlConst.qnLinkReference, XbrlConst.qnLinkDefinitionArc, XbrlConst.qnLinkCalculationArc, XbrlConst.qnLinkPresentationArc, XbrlConst.qnLinkLabelArc, XbrlConst.qnLinkReferenceArc, # per WH, private footnote arc and footnore resource roles are not allowed XbrlConst.qnLinkFootnoteArc, XbrlConst.qnLinkFootnote, } # 6.7.9 role types authority for e in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.xbrl.org/2003/linkbase}roleType"): if isinstance(e, ModelObject): roleURI = e.get("roleURI") if targetNamespaceAuthority != UrlUtil.authority(roleURI): val.modelXbrl.error( ("EFM.6.07.09", "GFM.1.03.09"), _("RoleType %(roleType)s does not match authority %(targetNamespaceAuthority)s" ), modelObject=e, roleType=roleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.9 end with .../role/lc3 name if not roleTypePattern.match(roleURI): val.modelXbrl.warning( ("EFM.6.07.09.roleEnding", "GFM.1.03.09"), "RoleType %(roleType)s should end with /role/{LC3name}", modelObject=e, roleType=roleURI) # 6.7.10 only one role type declaration in DTS modelRoleTypes = val.modelXbrl.roleTypes.get(roleURI) if modelRoleTypes is not None: modelRoleType = modelRoleTypes[0] definition = modelRoleType.definitionNotStripped usedOns = modelRoleType.usedOns if len(modelRoleTypes) == 1: # 6.7.11 used on's for pre, cal, def if any has a used on if not usedOns.isdisjoint(requiredUsedOns) and len( requiredUsedOns - usedOns) > 0: val.modelXbrl.error( ("EFM.6.07.11", "GFM.1.03.11"), _("RoleType %(roleType)s missing used on %(usedOn)s" ), modelObject=e, roleType=roleURI, usedOn=requiredUsedOns - usedOns) # 6.7.12 definition match pattern if (val.disclosureSystem.roleDefinitionPattern is not None and (definition is None or not val.disclosureSystem. roleDefinitionPattern.match(definition))): val.modelXbrl.error( ("EFM.6.07.12", "GFM.1.03.12-14"), _("RoleType %(roleType)s definition \"%(definition)s\" must match {Sortcode} - {Type} - {Title}" ), modelObject=e, roleType=roleURI, definition=(definition or "")) if usedOns & standardUsedOns: # semantics check val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("RoleType %(roleuri)s is defined using role types already defined by standard roles for: %(qnames)s" ), modelObject=e, roleuri=roleURI, qnames=', '.join( str(qn) for qn in usedOns & standardUsedOns)) # 6.7.13 arcrole types authority for e in modelDocument.xmlRootElement.iterdescendants( tag="{http://www.xbrl.org/2003/linkbase}arcroleType"): if isinstance(e, ModelObject): arcroleURI = e.get("arcroleURI") if targetNamespaceAuthority != UrlUtil.authority(arcroleURI): val.modelXbrl.error( ("EFM.6.07.13", "GFM.1.03.15"), _("ArcroleType %(arcroleType)s does not match authority %(targetNamespaceAuthority)s" ), modelObject=e, arcroleType=arcroleURI, targetNamespaceAuthority=targetNamespaceAuthority, targetNamespace=modelDocument.targetNamespace) # 6.7.13 end with .../arcrole/lc3 name if not arcroleTypePattern.match(arcroleURI): val.modelXbrl.warning( ("EFM.6.07.13.arcroleEnding", "GFM.1.03.15"), _("ArcroleType %(arcroleType)s should end with /arcrole/{LC3name}" ), modelObject=e, arcroleType=arcroleURI) # 6.7.15 definition match pattern modelRoleTypes = val.modelXbrl.arcroleTypes[arcroleURI] definition = modelRoleTypes[0].definition if definition is None or not arcroleDefinitionPattern.match( definition): val.modelXbrl.error( ("EFM.6.07.15", "GFM.1.03.17"), _("ArcroleType %(arcroleType)s definition must be non-empty" ), modelObject=e, arcroleType=arcroleURI) # semantic checks usedOns = modelRoleTypes[0].usedOns if usedOns & standardUsedOns: # semantics check val.modelXbrl.log( "ERROR-SEMANTIC", ("EFM.6.08.03", "GFM.2.03.03"), _("ArcroleType %(arcroleuri)s is defined using role types already defined by standard arcroles for: %(qnames)s" ), modelObject=e, arcroleuri=arcroleURI, qnames=', '.join( str(qn) for qn in usedOns & standardUsedOns)) #6.3.3 filename check m = re.match(r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9]).xsd$", modelDocument.basename) if m: try: # check date value datetime.datetime.strptime(m.group(1), "%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}.xsd".format( val.fileNameBasePart, val.fileNameDatePart) if modelDocument.basename != expectedFilename: val.modelXbrl.log( "WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Schema file name warning: %(filename)s, should match %(expectedFilename)s' ), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid schema file base name part (date) in "{base}-{yyyymmdd}.xsd": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid schema file name, must match "{base}-{yyyymmdd}.xsd": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) elif modelDocument.type == ModelDocument.Type.LINKBASE: # if it is part of the submission (in same directory) check name labelRels = None if modelDocument.filepath.startswith( val.modelXbrl.modelDocument.filepathdir): #6.3.3 filename check extLinkElt = XmlUtil.descendant( modelDocument.xmlRootElement, XbrlConst.link, "*", "{http://www.w3.org/1999/xlink}type", "extended") if extLinkElt is None: # no ext link element val.modelXbrl.error( (val.EFM60303 + ".noLinkElement", "GFM.1.01.01.noLinkElement"), _('Invalid linkbase file name: %(filename)s, has no extended link element, cannot determine link type.' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03.noLinkElement", "EFM.6.23.01.noLinkElement", "GFM.1.01.01.noLinkElement")) elif extLinkElt.localName not in extLinkEltFileNameEnding: val.modelXbrl.error( "EFM.6.03.02", _('Invalid linkbase link element %(linkElement)s in %(filename)s' ), modelObject=modelDocument, linkElement=extLinkElt.localName, filename=modelDocument.basename) else: m = re.match( r"^\w+-([12][0-9]{3}[01][0-9][0-3][0-9])(_[a-z]{3}).xml$", modelDocument.basename) expectedSuffix = extLinkEltFileNameEnding[extLinkElt.localName] if m and m.group(2) == expectedSuffix: try: # check date value datetime.datetime.strptime(m.group(1), "%Y%m%d").date() # date and format are ok, check "should" part of 6.3.3 if val.fileNameBasePart: expectedFilename = "{0}-{1}{2}.xml".format( val.fileNameBasePart, val.fileNameDatePart, expectedSuffix) if modelDocument.basename != expectedFilename: val.modelXbrl.log( "WARNING-SEMANTIC", ("EFM.6.03.03.matchInstance", "GFM.1.01.01.matchInstance"), _('Linkbase name warning: %(filename)s should match %(expectedFilename)s' ), modelObject=modelDocument, filename=modelDocument.basename, expectedFilename=expectedFilename) except ValueError: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase base file name part (date) in "{base}-{yyyymmdd}_{suffix}.xml": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) else: val.modelXbrl.error( (val.EFM60303, "GFM.1.01.01"), _('Invalid linkbase name, must match "{base}-{yyyymmdd}%(expectedSuffix)s.xml": %(filename)s' ), modelObject=modelDocument, filename=modelDocument.basename, expectedSuffix=expectedSuffix, messageCodes=("EFM.6.03.03", "EFM.6.23.01", "GFM.1.01.01")) if extLinkElt.localName == "labelLink": if labelRels is None: labelRels = val.modelXbrl.relationshipSet( XbrlConst.conceptLabel) for labelElt in XmlUtil.children(extLinkElt, XbrlConst.link, "label"): # 6.10.9 if XbrlConst.isNumericRole(labelElt.role): for rel in labelRels.toModelObject(labelElt): if rel.fromModelObject is not None and not rel.fromModelObject.isNumeric: val.modelXbrl.error( "EFM.6.10.09", _("Label of non-numeric concept %(concept)s has a numeric role: %(role)s" ), modelObject=(labelElt, rel.fromModelObject), concept=rel.fromModelObject.qname, role=labelElt.role)
def init(modelXbrl): # setup modelXbrl for rendering evaluation # dimension defaults required in advance of validation from arelle import ValidateXbrlDimensions, ValidateFormula, ModelDocument ValidateXbrlDimensions.loadDimensionDefaults(modelXbrl) hasXbrlTables = False # validate table linkbase dimensions for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname and XbrlConst.isTableRenderingArcrole( arcrole): ValidateFormula.checkBaseSet( modelXbrl, arcrole, ELR, modelXbrl.relationshipSet(arcrole, ELR, linkqname, arcqname)) if arcrole in (XbrlConst.tableBreakdown, XbrlConst.tableBreakdownMMDD, XbrlConst.tableBreakdown201305, XbrlConst.tableBreakdown201301, XbrlConst.tableAxis2011): hasXbrlTables = True # provide context for view if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE: instance = None # use instance of the entry pont else: # need dummy instance instance = ModelDocument.create( modelXbrl, ModelDocument.Type.INSTANCE, "dummy.xml", # fake URI and fake schemaRef ("http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd", )) if hasXbrlTables: # formula processor is needed for 2011 XBRL tables but not for 2010 Eurofiling tables modelXbrl.rendrCntx = XPathContext.create(modelXbrl, instance) modelXbrl.profileStat(None) # setup fresh parameters from formula options modelXbrl.parameters = modelXbrl.modelManager.formulaOptions.typedParameters( ) # validate parameters and custom function signatures ValidateFormula.validate(modelXbrl, xpathContext=modelXbrl.rendrCntx, parametersOnly=True, statusMsg=_("compiling rendering tables")) # deprecated as of 2013-05-17 # check and extract message expressions into compilable programs for msgArcrole in (XbrlConst.tableDefinitionNodeMessage201301, XbrlConst.tableDefinitionNodeSelectionMessage201301, XbrlConst.tableAxisMessage2011, XbrlConst.tableAxisSelectionMessage2011): for msgRel in modelXbrl.relationshipSet( msgArcrole).modelRelationships: ValidateFormula.checkMessageExpressions( modelXbrl, msgRel.toModelObject) # compile and validate tables for modelTable in modelXbrl.modelRenderingTables: modelTable.fromInstanceQnames = None # required if referred to by variables scope chaining modelTable.compile() hasNsWithAspectModel = modelTable.namespaceURI in ( XbrlConst.euRend, XbrlConst.table2011, XbrlConst.table201301, XbrlConst.table201305) # check aspectModel (attribute removed 2013-06, now always dimensional) if modelTable.aspectModel not in ( "non-dimensional", "dimensional") and hasNsWithAspectModel: modelXbrl.error( "xbrlte:unknownAspectModel", _("Table %(xlinkLabel)s, aspect model %(aspectModel)s not recognized" ), modelObject=modelTable, xlinkLabel=modelTable.xlinkLabel, aspectModel=modelTable.aspectModel) else: modelTable.priorAspectAxisDisposition = {} # check ordinate aspects against aspectModel oppositeAspectModel = ( _DICT_SET({'dimensional', 'non-dimensional'}) - _DICT_SET({modelTable.aspectModel})).pop() if hasNsWithAspectModel: uncoverableAspects = aspectModels[ oppositeAspectModel] - aspectModels[ modelTable.aspectModel] else: uncoverableAspects = () aspectsCovered = set() for tblAxisRel in modelXbrl.relationshipSet( (XbrlConst.tableBreakdown, XbrlConst.tableBreakdownMMDD, XbrlConst.tableBreakdown201305, XbrlConst.tableBreakdown201301, XbrlConst.tableAxis2011)).fromModelObject(modelTable): breakdownAspectsCovered = set() hasCoveredAspect = checkBreakdownDefinitionNode( modelXbrl, modelTable, tblAxisRel, tblAxisRel.axisDisposition, uncoverableAspects, breakdownAspectsCovered) ''' removed 2013-10 if not hasCoveredAspect: definitionNode = tblAxisRel.toModelObject modelXbrl.error("xbrlte:breakdownDefinesNoAspects", _("Breakdown %(xlinkLabel)s has no participating aspects"), modelObject=(modelTable,definitionNode), xlinkLabel=definitionNode.xlinkLabel, axis=definitionNode.localName) ''' aspectsCovered |= breakdownAspectsCovered checkBreakdownLeafNodeAspects(modelXbrl, modelTable, tblAxisRel, set(), breakdownAspectsCovered) if Aspect.CONCEPT not in aspectsCovered and not hasNsWithAspectModel: modelXbrl.error( "xbrlte:tableMissingConceptAspect", _("Table %(xlinkLabel)s does not include the concept aspect as one of its participating aspects" ), modelObject=modelTable, xlinkLabel=modelTable.xlinkLabel) del modelTable.priorAspectAxisDisposition # check for table-parameter name clash parameterNames = {} for tblParamRel in modelXbrl.relationshipSet( (XbrlConst.tableParameter, XbrlConst.tableParameterMMDD )).fromModelObject(modelTable): parameterName = tblParamRel.variableQname if parameterName in parameterNames: modelXbrl.error( "xbrlte:tableParameterNameClash ", _("Table %(xlinkLabel)s has parameter name clash for variable %(name)s" ), modelObject=(modelTable, tblParamRel, parameterNames[parameterName]), xlinkLabel=modelTable.xlinkLabel, name=parameterName) else: parameterNames[parameterName] = tblParamRel modelXbrl.profileStat(_("compileTables"))
def __init__(self, modelXbrl, arcrole, linkrole=None, linkqname=None, arcqname=None, includeProhibits=False): self.isChanged = False self.modelXbrl = modelXbrl self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname baseSetKey = (arcrole, linkrole, linkqname, arcqname) relationshipSetKey = (arcrole, linkrole, linkqname, arcqname, includeProhibits) # base sets does not care about the #includeProhibits if baseSetKey in self.modelXbrl.baseSets: modelLinks = self.modelXbrl.baseSets[baseSetKey] else: modelLinks = [] # gather arcs relationships = {} isDimensionRel = self.arcrole == "XBRL-dimensions" # all dimensional relationship arcroles isFormulaRel = self.arcrole == "XBRL-formulae" # all formula relationship arcroles isTableRenderingRel = self.arcrole == "Table-rendering" isFootnoteRel = self.arcrole == "XBRL-footnotes" # all footnote relationship arcroles for modelLink in modelLinks: arcs = [] linkEltQname = modelLink.qname for linkChild in modelLink: linkChildArcrole = linkChild.get( "{http://www.w3.org/1999/xlink}arcrole") if linkChild.get("{http://www.w3.org/1999/xlink}type" ) == "arc" and linkChildArcrole: linkChildQname = linkChild if isFootnoteRel: arcs.append(linkChild) elif isDimensionRel: if XbrlConst.isDimensionArcrole(linkChildArcrole): arcs.append(linkChild) elif isFormulaRel: if XbrlConst.isFormulaArcrole(linkChildArcrole): arcs.append(linkChild) elif isTableRenderingRel: if XbrlConst.isTableRenderingArcrole(linkChildArcrole): arcs.append(linkChild) elif arcrole == linkChildArcrole and \ (arcqname is None or arcqname == linkChildQname) and \ (linkqname is None or linkqname == linkEltQname): arcs.append(linkChild) # build network for arcElement in arcs: arcrole = arcElement.get( "{http://www.w3.org/1999/xlink}arcrole") fromLabel = arcElement.get( "{http://www.w3.org/1999/xlink}from") toLabel = arcElement.get("{http://www.w3.org/1999/xlink}to") for fromResource in modelLink.labeledResources[fromLabel]: for toResource in modelLink.labeledResources[toLabel]: if isinstance(fromResource, ModelResource) and isinstance( toResource, ModelResource): modelRel = ModelDtsObject.ModelRelationship( modelLink.modelDocument, arcElement, fromResource.dereference(), toResource.dereference()) modelRelEquivalenceKey = modelRel.equivalenceKey # this is a complex tuple to compute, get once for below if modelRelEquivalenceKey not in relationships or \ modelRel.priorityOver(relationships[modelRelEquivalenceKey]): relationships[ modelRelEquivalenceKey] = modelRel #reduce effective arcs and order relationships... self.modelRelationshipsFrom = None self.modelRelationshipsTo = None self.modelConceptRoots = None self.modellinkRoleUris = None orderRels = defaultdict(list) for modelRel in relationships.values(): if includeProhibits or not modelRel.isProhibited: orderRels[modelRel.order].append(modelRel) self.modelRelationships = [ modelRel for order in sorted(orderRels.keys()) for modelRel in orderRels[order] ] modelXbrl.relationshipSets[relationshipSetKey] = self
def linkbaseDiscover(self, linkbaseElement, inInstance=False): for lbElement in linkbaseElement.iterchildren(): if isinstance(lbElement, ModelObject): lbLn = lbElement.localName lbNs = lbElement.namespaceURI if lbNs == XbrlConst.link: if lbLn == "roleRef" or lbLn == "arcroleRef": href = self.discoverHref(lbElement) if href is None: self.modelXbrl.error( "xmlSchema:requiredAttribute", _("Linkbase reference for %(linkbaseRefElement)s href attribute missing or malformed" ), modelObject=lbElement, linkbaseRefElement=lbLn) else: self.hrefObjects.append(href) continue if lbElement.get( "{http://www.w3.org/1999/xlink}type") == "extended": if isinstance(lbElement, ModelLink): self.schemalocateElementNamespace(lbElement) arcrolesFound = set() dimensionArcFound = False formulaArcFound = False tableRenderingArcFound = False linkQn = qname(lbElement) linkrole = lbElement.get( "{http://www.w3.org/1999/xlink}role") isStandardExtLink = XbrlConst.isStandardResourceOrExtLinkElement( lbElement) if inInstance: #index footnote links even if no arc children baseSetKeys = (("XBRL-footnotes", None, None, None), ("XBRL-footnotes", linkrole, None, None)) for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[baseSetKey].append( lbElement) for linkElement in lbElement.iterchildren(): if isinstance(linkElement, ModelObject): self.schemalocateElementNamespace(linkElement) xlinkType = linkElement.get( "{http://www.w3.org/1999/xlink}type") modelResource = None if xlinkType == "locator": nonDTS = linkElement.namespaceURI != XbrlConst.link or linkElement.localName != "loc" # only link:loc elements are discovered or processed href = self.discoverHref(linkElement, nonDTS=nonDTS) if href is None: if isStandardExtLink: self.modelXbrl.error( "xmlSchema:requiredAttribute", _("Locator href attribute missing or malformed in standard extended link" ), modelObejct=linkElement) else: self.modelXbrl.warning( "arelle:hrefWarning", _("Locator href attribute missing or malformed in non-standard extended link" ), modelObejct=linkElement) else: linkElement.modelHref = href modelResource = linkElement elif xlinkType == "arc": arcQn = qname(linkElement) arcrole = linkElement.get( "{http://www.w3.org/1999/xlink}arcrole" ) if arcrole not in arcrolesFound: if linkrole == "": linkrole = XbrlConst.defaultLinkRole #index by both arcrole and linkrole#arcrole and dimensionsions if applicable baseSetKeys = [(arcrole, linkrole, linkQn, arcQn)] baseSetKeys.append( (arcrole, linkrole, None, None)) baseSetKeys.append( (arcrole, None, None, None)) if XbrlConst.isDimensionArcrole( arcrole ) and not dimensionArcFound: baseSetKeys.append( ("XBRL-dimensions", None, None, None)) baseSetKeys.append( ("XBRL-dimensions", linkrole, None, None)) dimensionArcFound = True if XbrlConst.isFormulaArcrole( arcrole ) and not formulaArcFound: baseSetKeys.append( ("XBRL-formulae", None, None, None)) baseSetKeys.append( ("XBRL-formulae", linkrole, None, None)) formulaArcFound = True if XbrlConst.isTableRenderingArcrole( arcrole ) and not tableRenderingArcFound: baseSetKeys.append( ("Table-rendering", None, None, None)) baseSetKeys.append( ("Table-rendering", linkrole, None, None)) tableRenderingArcFound = True self.modelXbrl.hasTableRendering = True for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[ baseSetKey].append(lbElement) arcrolesFound.add(arcrole) elif xlinkType == "resource": # create resource and make accessible by id for document modelResource = linkElement if modelResource is not None: lbElement.labeledResources[linkElement.get("{http://www.w3.org/1999/xlink}label")] \ .append(modelResource) else: self.modelXbrl.error( "xbrl:schemaDefinitionMissing", _("Linkbase extended link %(element)s missing schema definition" ), modelObject=lbElement, element=lbElement.prefixedName)
def init(modelXbrl): # setup modelXbrl for rendering evaluation # dimension defaults required in advance of validation from arelle import ValidateXbrlDimensions, ValidateFormula, ModelDocument ValidateXbrlDimensions.loadDimensionDefaults(modelXbrl) hasXbrlTables = False # validate table linkbase dimensions for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if ELR and linkqname and arcqname and XbrlConst.isTableRenderingArcrole( arcrole): ValidateFormula.checkBaseSet( modelXbrl, arcrole, ELR, modelXbrl.relationshipSet(arcrole, ELR, linkqname, arcqname)) if arcrole in (XbrlConst.tableBreakdown, XbrlConst.tableBreakdown201301, XbrlConst.tableAxis2011): hasXbrlTables = True # provide context for view if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE: instance = None # use instance of the entry pont else: # need dummy instance instance = ModelDocument.create( modelXbrl, ModelDocument.Type.INSTANCE, "dummy.xml", # fake URI and fake schemaRef ("http://www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd", )) if hasXbrlTables: # formula processor is needed for 2011 XBRL tables but not for 2010 Eurofiling tables modelXbrl.rendrCntx = XPathContext.create(modelXbrl, instance) modelXbrl.profileStat(None) # setup fresh parameters from formula optoins modelXbrl.parameters = modelXbrl.modelManager.formulaOptions.typedParameters( ) # validate parameters and custom function signatures ValidateFormula.validate(modelXbrl, xpathContext=modelXbrl.rendrCntx, parametersOnly=True, statusMsg=_("compiling rendering tables")) # deprecated as of 2013-05-17 # check and extract message expressions into compilable programs for msgArcrole in (XbrlConst.tableDefinitionNodeMessage201301, XbrlConst.tableDefinitionNodeSelectionMessage201301, XbrlConst.tableAxisMessage2011, XbrlConst.tableAxisSelectionMessage2011): for msgRel in modelXbrl.relationshipSet( msgArcrole).modelRelationships: ValidateFormula.checkMessageExpressions( modelXbrl, msgRel.toModelObject) # compile and validate tables for modelTable in modelXbrl.modelRenderingTables: modelTable.fromInstanceQnames = None # required if referred to by variables scope chaining modelTable.compile() # check aspectModel if modelTable.aspectModel not in ("non-dimensional", "dimensional"): modelXbrl.error( "xbrlte:unknownAspectModel", _("Table %(xlinkLabel)s, aspect model %(aspectModel)s not recognized" ), modelObject=modelTable, xlinkLabel=modelTable.xlinkLabel, aspectModel=modelTable.aspectModel) else: modelTable.priorAspectAxisDisposition = {} # check ordinate aspects against aspectModel oppositeAspectModel = ( _DICT_SET({'dimensional', 'non-dimensional'}) - _DICT_SET({modelTable.aspectModel})).pop() uncoverableAspects = aspectModels[ oppositeAspectModel] - aspectModels[modelTable.aspectModel] for tblAxisRel in modelXbrl.relationshipSet( (XbrlConst.tableBreakdown, XbrlConst.tableBreakdown201301, XbrlConst.tableAxis2011)).fromModelObject(modelTable): checkDefinitionNodeAspectModel(modelXbrl, modelTable, tblAxisRel, uncoverableAspects) del modelTable.priorAspectAxisDisposition modelXbrl.profileStat(_("compileTables"))
def view(self, firstTime=False, relationshipSet=None): self.blockSelectEvent = 1 self.blockViewModelObject = 0 self.tag_has = defaultdict(list) # temporary until Tk 8.6 # relationship set based on linkrole parameter, to determine applicable linkroles if relationshipSet is None: relationshipSet = groupRelationshipSet(self.modelXbrl, self.arcrole, self.linkrole, self.linkqname, self.arcqname) if not relationshipSet: self.modelXbrl.modelManager.addToLog( _("no relationships for {0}").format( groupRelationshipLabel(self.arcrole))) return False if firstTime: self.showReferences = False # set up treeView widget and tabbed pane hdr = self.treeColHdr if self.treeColHdr else _( "{0} Relationships").format( groupRelationshipLabel(self.arcrole)) self.treeView.heading("#0", text=hdr) if self.isEbaTableIndex: # settings for the filing indicator column self.treeView.heading("#1", text='Filed') self.treeView.column("#1", width=40, anchor="w", stretch=False) if self.showColumns: if self.arcrole == XbrlConst.parentChild: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("preferredLabel", "type", "references") self.treeView.column("preferredLabel", width=64, anchor="w", stretch=False) self.treeView.heading("preferredLabel", text=_("Pref. Label")) self.treeView.column("type", width=100, anchor="w", stretch=False) self.treeView.heading("type", text=_("Type")) self.treeView.column("references", width=200, anchor="w", stretch=False) self.treeView.heading("references", text=_("References")) elif self.arcrole == XbrlConst.summationItem: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("weight", "balance") self.treeView.column("weight", width=48, anchor="w", stretch=False) self.treeView.heading("weight", text=_("Weight")) self.treeView.column("balance", width=70, anchor="w", stretch=False) self.treeView.heading("balance", text=_("Balance")) elif self.arcrole == "XBRL-dimensions": # add columns for dimensional information self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("arcrole", "contextElement", "closed", "usable") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("contextElement", width=50, anchor="center", stretch=False) self.treeView.heading("contextElement", text="Context") self.treeView.column("closed", width=40, anchor="center", stretch=False) self.treeView.heading("closed", text="Closed") self.treeView.column("usable", width=40, anchor="center", stretch=False) self.treeView.heading("usable", text="Usable") elif self.arcrole == "Table-rendering": # add columns for dimensional information self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("axis", "abstract", "merge", "header", "priItem", "dims") self.treeView.column("axis", width=28, anchor="center", stretch=False) self.treeView.heading("axis", text="Axis") self.treeView.column("abstract", width=24, anchor="center", stretch=False) self.treeView.heading("abstract", text="Abs") self.treeView.column("merge", width=26, anchor="center", stretch=False) self.treeView.heading("merge", text="Mrg") self.treeView.column("header", width=160, anchor="w", stretch=False) self.treeView.heading("header", text="Header") self.treeView.column("priItem", width=100, anchor="w", stretch=False) self.treeView.heading("priItem", text="Primary Item") self.treeView.column("dims", width=150, anchor="w", stretch=False) self.treeView.heading("dims", text=_("Dimensions")) elif isinstance(self.arcrole, (list, tuple)) or XbrlConst.isResourceArcrole( self.arcrole): self.isResourceArcrole = True self.showReferences = isinstance( self.arcrole, _STR_BASE) and self.arcrole.endswith("-reference") self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("arcrole", "resource", "resourcerole", "lang") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("resource", width=60, anchor="w", stretch=False) self.treeView.heading("resource", text="Resource") self.treeView.column("resourcerole", width=100, anchor="w", stretch=False) self.treeView.heading("resourcerole", text="Resource Role") self.treeView.column("lang", width=36, anchor="w", stretch=False) self.treeView.heading("lang", text="Lang") self.clearTreeView() self.id = 1 # sort URIs by definition linkroleUris = [] linkroleUriChildren = {} for linkroleUri in relationshipSet.linkRoleUris: modelRoleTypes = self.modelXbrl.roleTypes.get(linkroleUri) if modelRoleTypes: roledefinition = ( (self.hasTableIndex and getattr(modelRoleTypes[0], "_tableIndex", False)) or modelRoleTypes[0].genLabel(lang=self.lang, strip=True) or modelRoleTypes[0].definition or linkroleUri) roleId = modelRoleTypes[0].objectId(self.id) if (self.hasTableIndex and getattr(modelRoleTypes[0], "_tableChildren")): linkroleUriChildren[linkroleUri] = [ roleType.roleURI for roleType in modelRoleTypes[0]._tableChildren ] else: roledefinition = linkroleUri roleId = "node{0}".format(self.id) self.id += 1 linkroleUris.append((roledefinition, linkroleUri, roleId)) linkroleUris.sort() def insertLinkroleChildren(parentNode, childUris): for childUri in childUris: for roledefinition, linkroleUri, roleId in linkroleUris: if childUri == linkroleUri and isinstance( roledefinition, tuple): # tableGroup _nextTableGroup, _order, roledefinition = roledefinition childId = "_{}{}".format(self.id, roleId) self.id += 1 childNode = self.treeView.insert(parentNode, "end", childId, text=roledefinition, tags=("ELR", )) if childUri in linkroleUriChildren: insertLinkroleChildren( childNode, linkroleUriChildren[childUri]) break # for each URI in definition order tableGroup = "" for roledefinition, linkroleUri, roleId in linkroleUris: if self.showLinkroles: if isinstance(roledefinition, tuple): # tableGroup nextTableGroup, _order, roledefinition = roledefinition if tableGroup != nextTableGroup: self.treeView.insert("", "end", nextTableGroup, text=nextTableGroup[1:], tags=("Group", )) if not tableGroup: # first tableGroup item, expand it self.setTreeItemOpen(nextTableGroup, open=True) tableGroup = nextTableGroup linknode = self.treeView.insert(tableGroup, "end", roleId, text=roledefinition, tags=("ELR", )) # add tableChildren as child nodes if linkroleUri in linkroleUriChildren: insertLinkroleChildren(linknode, linkroleUriChildren[linkroleUri]) else: linknode = "" if self.showRelationships: linkRelationshipSet = groupRelationshipSet( self.modelXbrl, self.arcrole, linkroleUri, self.linkqname, self.arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.viewConcept(rootConcept, rootConcept, "", self.labelrole, linknode, 1, linkRelationshipSet, set()) self.tag_has[linkroleUri].append(linknode) if self.expandAllOnFirstDisplay: self.expandAll()
def checkDTSdocument(val, modelDocument, isFilingDocument): for hrefElt, _hrefedDoc, hrefId in modelDocument.hrefObjects: if hrefId: #check scheme regardless of whether document loaded # check all xpointer schemes for scheme, _path in XmlUtil.xpointerSchemes(hrefId): if scheme == "element" and val.validateDisclosureSystem: val.modelXbrl.error(("EFM.6.03.06", "GFM.1.01.03"), _("Href %(elementHref)s may only have shorthand xpointers"), modelObject=hrefElt, elementHref=hrefElt.get("{http://www.w3.org/1999/xlink}href")) if not isFilingDocument: return # not a filing's extension document if modelDocument.type == ModelDocument.Type.SCHEMA: if modelDocument.targetNamespace is not None and len(modelDocument.targetNamespace) > 85: l = len(modelDocument.targetNamespace.encode("utf-8")) if l > 255: val.modelXbrl.error("EFM.6.07.30", _("Schema targetNamespace length (%(length)s) is over 255 bytes long in utf-8 %(targetNamespace)s"), modelObject=modelDocument.xmlRootElement, length=l, targetNamespace=modelDocument.targetNamespace, value=modelDocument.targetNamespace) if modelDocument.type in (ModelDocument.Type.SCHEMA, ModelDocument.Type.LINKBASE): isSchema = modelDocument.type == ModelDocument.Type.SCHEMA if isSchema: for elt in modelDocument.xmlRootElement.iter(tag="{http://www.w3.org/2001/XMLSchema}*"): if elt.namespaceURI == XbrlConst.xsd: localName = elt.localName if localName in {"element", "complexType", "simpleType"}: name = elt.get("name") if name and len(name) > 64: l = len(name.encode("utf-8")) if l > 200: val.modelXbrl.error("EFM.6.07.29", _("Schema %(element)s has a name length (%(length)s) over 200 bytes long in utf-8, %(name)s."), modelObject=elt, element=localName, name=name, length=l) for elt in modelDocument.xmlRootElement.iter(): if isinstance(elt, ModelObject): xlinkType = elt.get("{http://www.w3.org/1999/xlink}type") xlinkRole = elt.get("{http://www.w3.org/1999/xlink}role") if elt.namespaceURI == XbrlConst.link: # check schema roleTypes if elt.localName in ("roleType","arcroleType"): uriAttr = {"roleType":"roleURI", "arcroleType":"arcroleURI"}[elt.localName] roleURI = elt.get(uriAttr) if len(roleURI) > 85: l = len(roleURI.encode("utf-8")) if l > 255: val.modelXbrl.error("EFM.6.07.30", _("Schema %(element)s %(attribute)s length (%(length)s) is over 255 bytes long in utf-8 %(roleURI)s"), modelObject=elt, element=elt.qname, attribute=uriAttr, length=l, roleURI=roleURI, value=roleURI) if elt.localName == "arcroleRef": refUri = elt.get("arcroleURI") hrefAttr = elt.get("{http://www.w3.org/1999/xlink}href") hrefUri, hrefId = UrlUtil.splitDecodeFragment(hrefAttr) if hrefUri not in val.disclosureSystem.standardTaxonomiesDict: val.modelXbrl.error(("EFM.6.09.06", "GFM.1.04.06"), _("Arcrole %(refURI)s arcroleRef %(xlinkHref)s must be a standard taxonomy"), modelObject=elt, refURI=refUri, xlinkHref=hrefUri) if modelDocument.type == ModelDocument.Type.INLINEXBRL and elt.namespaceURI in XbrlConst.ixbrlAll: if elt.localName == "footnote": if val.validateGFM: if elt.get("{http://www.w3.org/1999/xlink}arcrole") != XbrlConst.factFootnote: # must be in a nonDisplay div if not any(inlineDisplayNonePattern.search(e.get("style") or "") for e in XmlUtil.ancestors(elt, XbrlConst.xhtml, "div")): val.modelXbrl.error(("EFM.N/A", "GFM:1.10.16"), _("Inline XBRL footnote %(footnoteID)s must be in non-displayable div due to arcrole %(arcrole)s"), modelObject=elt, footnoteID=elt.get("footnoteID"), arcrole=elt.get("{http://www.w3.org/1999/xlink}arcrole")) if not elt.get("{http://www.w3.org/XML/1998/namespace}lang"): val.modelXbrl.error(("EFM.N/A", "GFM:1.10.13"), _("Inline XBRL footnote %(footnoteID)s is missing an xml:lang attribute"), modelObject=elt, footnoteID=id) if xlinkType == "extended": if not xlinkRole or xlinkRole == "": val.modelXbrl.error(("EFM.6.09.04", "GFM.1.04.04"), "%(element)s is missing an xlink:role", modelObject=elt, element=elt.qname) if not val.extendedElementName: val.extendedElementName = elt.qname elif val.extendedElementName != elt.qname: val.modelXbrl.error(("EFM.6.09.07", "GFM:1.04.07"), _("Extended element %(element)s must be the same as %(element2)s"), modelObject=elt, element=elt.qname, element2=val.extendedElementName) elif xlinkType == "resource": if not xlinkRole: val.modelXbrl.error(("EFM.6.09.04", "GFM.1.04.04"), _("%(element)s is missing an xlink:role"), modelObject=elt, element=elt.qname) elif not XbrlConst.isStandardRole(xlinkRole): modelsRole = val.modelXbrl.roleTypes.get(xlinkRole) if (modelsRole is None or len(modelsRole) == 0 or modelsRole[0].modelDocument.targetNamespace not in val.disclosureSystem.standardTaxonomiesDict): val.modelXbrl.error(("EFM.6.09.05", "GFM.1.04.05"), _("Resource %(xlinkLabel)s role %(role)s is not a standard taxonomy role"), modelObject=elt, xlinkLabel=elt.get("{http://www.w3.org/1999/xlink}label"), role=xlinkRole, element=elt.qname, roleDefinition=val.modelXbrl.roleTypeDefinition(xlinkRole)) elif xlinkType == "arc": if elt.get("priority") is not None: priority = elt.get("priority") try: if int(priority) >= 10: val.modelXbrl.error(("EFM.6.09.09", "GFM.1.04.08"), _("Arc from %(xlinkFrom)s to %(xlinkTo)s priority %(priority)s must be less than 10"), modelObject=elt, arcElement=elt.qname, xlinkFrom=elt.get("{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get("{http://www.w3.org/1999/xlink}to"), priority=priority) except (ValueError) : val.modelXbrl.error(("EFM.6.09.09", "GFM.1.04.08"), _("Arc from %(xlinkFrom)s to %(xlinkTo)s priority %(priority)s is not an integer"), modelObject=elt, arcElement=elt.qname, xlinkFrom=elt.get("{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get("{http://www.w3.org/1999/xlink}to"), priority=priority) if elt.namespaceURI == XbrlConst.link: if elt.localName == "presentationArc" and not elt.get("order"): val.modelXbrl.error(("EFM.6.12.01", "GFM.1.06.01"), _("PresentationArc from %(xlinkFrom)s to %(xlinkTo)s must have an order"), modelObject=elt, xlinkFrom=elt.get("{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get("{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt)) elif elt.localName == "calculationArc": if not elt.get("order"): val.modelXbrl.error(("EFM.6.14.01", "GFM.1.07.01"), _("CalculationArc from %(xlinkFrom)s to %(xlinkTo)s must have an order"), modelObject=elt, xlinkFrom=elt.get("{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get("{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt)) try: weightAttr = elt.get("weight") weight = float(weightAttr) if not weight in (1, -1): val.modelXbrl.error(("EFM.6.14.02", "GFM.1.07.02"), _("CalculationArc from %(xlinkFrom)s to %(xlinkTo)s weight %(weight)s must be 1 or -1"), modelObject=elt, xlinkFrom=elt.get("{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get("{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt), weight=weightAttr) except ValueError: val.modelXbrl.error(("EFM.6.14.02", "GFM.1.07.02"), _("CalculationArc from %(xlinkFrom)s to %(xlinkTo)s must have an weight (value error in \"%(weight)s\")"), modelObject=elt, xlinkFrom=elt.get("{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get("{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt), weight=weightAttr) elif elt.localName == "definitionArc": if not elt.get("order"): val.modelXbrl.error(("EFM.6.16.01", "GFM.1.08.01"), _("DefinitionArc from %(xlinkFrom)s to %(xlinkTo)s must have an order"), modelObject=elt, xlinkFrom=elt.get("{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get("{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt))
def linkbaseDiscover(self, linkbaseElement, inInstance=False): for lbElement in linkbaseElement.childNodes: if lbElement.nodeType == 1: #element lbLn = lbElement.localName lbNs = lbElement.namespaceURI if lbNs == XbrlConst.link: if lbLn == "roleRef" or lbLn == "arcroleRef": href = self.discoverHref(lbElement) if href is None: self.modelXbrl.error( "Linkbase in {0} {1} href attribute missing or malformed" .format(os.path.basename(self.uri), lbLn), "err", "xbrl:hrefMissing") else: self.hrefObjects.append(href) continue if lbElement.getAttributeNS(XbrlConst.xlink, "type") == "extended": self.schemalocateElementNamespace(lbElement) arcrolesFound = set() dimensionArcFound = False formulaArcFound = False euRenderingArcFound = False linkQn = qname(lbElement) linkrole = lbElement.getAttributeNS( XbrlConst.xlink, "role") modelLink = ModelObject.createLink(self, lbElement) if inInstance: #index footnote links even if no arc children baseSetKeys = (("XBRL-footnotes", None, None, None), ("XBRL-footnotes", linkrole, None, None)) for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[baseSetKey].append( modelLink) for linkElement in lbElement.childNodes: if linkElement.nodeType == 1: #element self.schemalocateElementNamespace(linkElement) xlinkType = linkElement.getAttributeNS( XbrlConst.xlink, "type") modelResource = None if xlinkType == "locator": nonDTS = linkElement.namespaceURI != XbrlConst.link or linkElement.localName != "loc" # only link:loc elements are discovered or processed href = self.discoverHref(linkElement, nonDTS=nonDTS) if href is None: self.modelXbrl.error( "Linkbase in {0} {1} href attribute missing or malformed" .format(os.path.basename(self.uri), lbLn), "err", "xbrl:hrefMissing") else: modelResource = ModelObject.createLocator( self, linkElement, href) elif xlinkType == "arc": arcQn = qname(linkElement) arcrole = linkElement.getAttributeNS( XbrlConst.xlink, "arcrole") if arcrole not in arcrolesFound: if linkrole == "": linkrole = XbrlConst.defaultLinkRole #index by both arcrole and linkrole#arcrole and dimensionsions if applicable baseSetKeys = [(arcrole, linkrole, linkQn, arcQn)] baseSetKeys.append( (arcrole, linkrole, None, None)) baseSetKeys.append( (arcrole, None, None, None)) if XbrlConst.isDimensionArcrole( arcrole) and not dimensionArcFound: baseSetKeys.append(("XBRL-dimensions", None, None, None)) baseSetKeys.append( ("XBRL-dimensions", linkrole, None, None)) dimensionArcFound = True if XbrlConst.isFormulaArcrole( arcrole) and not formulaArcFound: baseSetKeys.append(("XBRL-formulae", None, None, None)) baseSetKeys.append( ("XBRL-formulae", linkrole, None, None)) formulaArcFound = True if XbrlConst.isEuRenderingArcrole( arcrole ) and not euRenderingArcFound: baseSetKeys.append( ("EU-rendering", None, None, None)) baseSetKeys.append( ("EU-rendering", linkrole, None, None)) euRenderingArcFound = True self.modelXbrl.hasEuRendering = True for baseSetKey in baseSetKeys: self.modelXbrl.baseSets[ baseSetKey].append(modelLink) arcrolesFound.add(arcrole) elif xlinkType == "resource": # create resource and make accessible by id for document modelResource = ModelObject.createResource( self, linkElement) if modelResource is not None: if linkElement.hasAttribute("id"): self.idObjects[linkElement.getAttribute( "id")] = modelResource modelLink.labeledResources[linkElement.getAttributeNS(XbrlConst.xlink, "label")] \ .append(modelResource) else: XmlUtil.markIdAttributes(linkElement)
def view(self, firstTime=False, relationshipSet=None): self.blockSelectEvent = 1 self.blockViewModelObject = 0 self.tag_has = defaultdict(list) # temporary until Tk 8.6 # relationship set based on linkrole parameter, to determine applicable linkroles if relationshipSet is None: relationshipSet = groupRelationshipSet(self.modelXbrl, self.arcrole, self.linkrole, self.linkqname, self.arcqname) if not relationshipSet: self.modelXbrl.modelManager.addToLog(_("no relationships for {0}").format(groupRelationshipLabel(self.arcrole))) return False if firstTime: self.showReferences = False # set up treeView widget and tabbed pane hdr = self.treeColHdr if self.treeColHdr else _("{0} Relationships").format(groupRelationshipLabel(self.arcrole)) self.treeView.heading("#0", text=hdr) if self.showColumns: if self.arcrole == XbrlConst.parentChild: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("preferredLabel", "type", "references") self.treeView.column("preferredLabel", width=64, anchor="w", stretch=False) self.treeView.heading("preferredLabel", text=_("Pref. Label")) self.treeView.column("type", width=100, anchor="w", stretch=False) self.treeView.heading("type", text=_("Type")) self.treeView.column("references", width=200, anchor="w", stretch=False) self.treeView.heading("references", text=_("References")) elif self.arcrole == XbrlConst.summationItem: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("weight", "balance") self.treeView.column("weight", width=48, anchor="w", stretch=False) self.treeView.heading("weight", text=_("Weight")) self.treeView.column("balance", width=70, anchor="w", stretch=False) self.treeView.heading("balance", text=_("Balance")) elif self.arcrole == "XBRL-dimensions": # add columns for dimensional information self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("arcrole", "contextElement", "closed", "usable") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("contextElement", width=50, anchor="center", stretch=False) self.treeView.heading("contextElement", text="Context") self.treeView.column("closed", width=40, anchor="center", stretch=False) self.treeView.heading("closed", text="Closed") self.treeView.column("usable", width=40, anchor="center", stretch=False) self.treeView.heading("usable", text="Usable") elif self.arcrole == "Table-rendering": # add columns for dimensional information self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("axis", "abstract", "merge", "header", "priItem", "dims") self.treeView.column("axis", width=28, anchor="center", stretch=False) self.treeView.heading("axis", text="Axis") self.treeView.column("abstract", width=24, anchor="center", stretch=False) self.treeView.heading("abstract", text="Abs") self.treeView.column("merge", width=26, anchor="center", stretch=False) self.treeView.heading("merge", text="Mrg") self.treeView.column("header", width=160, anchor="w", stretch=False) self.treeView.heading("header", text="Header") self.treeView.column("priItem", width=100, anchor="w", stretch=False) self.treeView.heading("priItem", text="Primary Item") self.treeView.column("dims", width=150, anchor="w", stretch=False) self.treeView.heading("dims", text=_("Dimensions")) elif isinstance(self.arcrole, (list,tuple)) or XbrlConst.isResourceArcrole(self.arcrole): self.isResourceArcrole = True self.showReferences = isinstance(self.arcrole, _STR_BASE) and self.arcrole.endswith("-reference") self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("arcrole", "resource", "resourcerole", "lang") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("resource", width=60, anchor="w", stretch=False) self.treeView.heading("resource", text="Resource") self.treeView.column("resourcerole", width=100, anchor="w", stretch=False) self.treeView.heading("resourcerole", text="Resource Role") self.treeView.column("lang", width=36, anchor="w", stretch=False) self.treeView.heading("lang", text="Lang") self.id = 1 for previousNode in self.treeView.get_children(""): self.treeView.delete(previousNode) # sort URIs by definition linkroleUris = [] for linkroleUri in relationshipSet.linkRoleUris: modelRoleTypes = self.modelXbrl.roleTypes.get(linkroleUri) if modelRoleTypes: roledefinition = (modelRoleTypes[0].genLabel(lang=self.lang, strip=True) or modelRoleTypes[0].definition or linkroleUri) roleId = modelRoleTypes[0].objectId(self.id) else: roledefinition = linkroleUri roleId = "node{0}".format(self.id) self.id += 1 linkroleUris.append((roledefinition, linkroleUri, roleId)) linkroleUris.sort() # for each URI in definition order for roledefinition, linkroleUri, roleId in linkroleUris: if self.showLinkroles: linknode = self.treeView.insert("", "end", roleId, text=roledefinition, tags=("ELR",)) else: linknode = "" if self.showRelationships: linkRelationshipSet = groupRelationshipSet(self.modelXbrl, self.arcrole, linkroleUri, self.linkqname, self.arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.viewConcept(rootConcept, rootConcept, "", self.labelrole, linknode, 1, linkRelationshipSet, set()) self.tag_has[linkroleUri].append(linknode) if self.expandAllOnFirstDisplay: self.expandAll()
def baseSetArcroles(modelXbrl): # returns sorted list of tuples of arcrole basename and uri return sorted( set((XbrlConst.baseSetArcroleLabel(b[0]), b[0]) for b in modelXbrl.baseSets.keys()))
def validate(self, modelXbrl, parameters=None): self.parameters = parameters self.precisionPattern = re.compile("^([0-9]+|INF)$") self.decimalsPattern = re.compile("^(-?[0-9]+|INF)$") self.isoCurrencyPattern = re.compile(r"^[A-Z]{3}$") self.modelXbrl = modelXbrl self.validateDisclosureSystem = modelXbrl.modelManager.validateDisclosureSystem self.disclosureSystem = modelXbrl.modelManager.disclosureSystem self.validateEFM = self.validateDisclosureSystem and self.disclosureSystem.EFM self.validateGFM = self.validateDisclosureSystem and self.disclosureSystem.GFM self.validateEFMorGFM = self.validateDisclosureSystem and self.disclosureSystem.EFMorGFM self.validateHMRC = self.validateDisclosureSystem and self.disclosureSystem.HMRC self.validateSBRNL = self.validateDisclosureSystem and self.disclosureSystem.SBRNL self.validateXmlLang = self.validateDisclosureSystem and self.disclosureSystem.xmlLangPattern self.validateCalcLB = modelXbrl.modelManager.validateCalcLB self.validateInferDecimals = modelXbrl.modelManager.validateInferDecimals # xlink validation modelXbrl.profileStat(None) modelXbrl.modelManager.showStatus(_("validating links")) modelLinks = set() self.remoteResourceLocElements = set() self.genericArcArcroles = set() for baseSetExtLinks in modelXbrl.baseSets.values(): for baseSetExtLink in baseSetExtLinks: modelLinks.add(baseSetExtLink) # ext links are unique (no dups) for modelLink in modelLinks: fromToArcs = {} locLabels = {} resourceLabels = {} resourceArcTos = [] for arcElt in modelLink.iterchildren(): if isinstance(arcElt,ModelObject): xlinkType = arcElt.get("{http://www.w3.org/1999/xlink}type") # locator must have an href if xlinkType == "locator": if arcElt.get("{http://www.w3.org/1999/xlink}href") is None: modelXbrl.error("xlink:locatorHref", _("Xlink locator %(xlinkLabel)s missing href in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=arcElt.get("{http://www.w3.org/1999/xlink}label")) locLabels[arcElt.get("{http://www.w3.org/1999/xlink}label")] = arcElt elif xlinkType == "resource": resourceLabels[arcElt.get("{http://www.w3.org/1999/xlink}label")] = arcElt # can be no duplicated arcs between same from and to elif xlinkType == "arc": fromLabel = arcElt.get("{http://www.w3.org/1999/xlink}from") toLabel = arcElt.get("{http://www.w3.org/1999/xlink}to") fromTo = (fromLabel,toLabel) if fromTo in fromToArcs: modelXbrl.error("xlink:dupArcs", _("Duplicate xlink arcs in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel) else: fromToArcs[fromTo] = arcElt if arcElt.namespaceURI == XbrlConst.link: if arcElt.localName in arcNamesTo21Resource: #("labelArc","referenceArc"): resourceArcTos.append((toLabel, arcElt.get("use"), arcElt)) elif self.isGenericArc(arcElt): arcrole = arcElt.get("{http://www.w3.org/1999/xlink}arcrole") self.genericArcArcroles.add(arcrole) if arcrole in (XbrlConst.elementLabel, XbrlConst.elementReference): resourceArcTos.append((toLabel, arcrole, arcElt)) # values of type (not needed for validating parsers) if xlinkType not in xlinkTypeValues: # ("", "simple", "extended", "locator", "arc", "resource", "title", "none"): modelXbrl.error("xlink:type", _("Xlink type %(xlinkType)s invalid in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkType=xlinkType) # values of actuate (not needed for validating parsers) xlinkActuate = arcElt.get("{http://www.w3.org/1999/xlink}actuate") if xlinkActuate not in xlinkActuateValues: # ("", "onLoad", "onRequest", "other", "none"): modelXbrl.error("xlink:actuate", _("Actuate %(xlinkActuate)s invalid in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkActuate=xlinkActuate) # values of show (not needed for validating parsers) xlinkShow = arcElt.get("{http://www.w3.org/1999/xlink}show") if xlinkShow not in xlinkShowValues: # ("", "new", "replace", "embed", "other", "none"): modelXbrl.error("xlink:show", _("Show %(xlinkShow)s invalid in extended link %(linkrole)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkShow=xlinkShow) # check from, to of arcs have a resource or loc for fromTo, arcElt in fromToArcs.items(): fromLabel, toLabel = fromTo for name, value, sect in (("from", fromLabel, "3.5.3.9.2"),("to",toLabel, "3.5.3.9.3")): if value not in locLabels and value not in resourceLabels: modelXbrl.error("xbrl.{0}:arcResource".format(sect), _("Arc in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s attribute '%(attribute)s' has no matching loc or resource label"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel, attribute=name) if arcElt.localName == "footnoteArc" and arcElt.namespaceURI == XbrlConst.link and \ arcElt.get("{http://www.w3.org/1999/xlink}arcrole") == XbrlConst.factFootnote: if fromLabel not in locLabels: modelXbrl.error("xbrl.4.11.1.3.1:factFootnoteArcFrom", _("Footnote arc in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s \"from\" is not a loc"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel) if toLabel not in resourceLabels or qname(resourceLabels[toLabel]) != XbrlConst.qnLinkFootnote: modelXbrl.error("xbrl.4.11.1.3.1:factFootnoteArcTo", _("Footnote arc in extended link %(linkrole)s from %(xlinkLabelFrom)s to %(xlinkLabelTo)s \"to\" is not a footnote resource"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabelFrom=fromLabel, xlinkLabelTo=toLabel) # check unprohibited label arcs to remote locs for resourceArcTo in resourceArcTos: resourceArcToLabel, resourceArcUse, arcElt = resourceArcTo if resourceArcToLabel in locLabels: toLabel = locLabels[resourceArcToLabel] if resourceArcUse == "prohibited": self.remoteResourceLocElements.add(toLabel) else: modelXbrl.error("xbrl.5.2.2.3:labelArcRemoteResource", _("Unprohibited labelArc in extended link %(linkrole)s has illegal remote resource loc labeled %(xlinkLabel)s href %(xlinkHref)s"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=resourceArcToLabel, xlinkHref=toLabel.get("{http://www.w3.org/1999/xlink}href")) elif resourceArcToLabel in resourceLabels: toResource = resourceLabels[resourceArcToLabel] if resourceArcUse == XbrlConst.elementLabel: if not self.isGenericLabel(toResource): modelXbrl.error("xbrlle.2.1.1:genericLabelTarget", _("Generic label arc in extended link %(linkrole)s to %(xlinkLabel)s must target a generic label"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=resourceArcToLabel) elif resourceArcUse == XbrlConst.elementReference: if not self.isGenericReference(toResource): modelXbrl.error("xbrlre.2.1.1:genericReferenceTarget", _("Generic reference arc in extended link %(linkrole)s to %(xlinkLabel)s must target a generic reference"), modelObject=arcElt, linkrole=modelLink.role, xlinkLabel=resourceArcToLabel) resourceArcTos = None # dereference arcs modelXbrl.profileStat(_("validateLinks")) modelXbrl.dimensionDefaultConcepts = {} modelXbrl.qnameDimensionDefaults = {} modelXbrl.qnameDimensionContextElement = {} # check base set cycles, dimensions modelXbrl.modelManager.showStatus(_("validating relationship sets")) for baseSetKey in modelXbrl.baseSets.keys(): arcrole, ELR, linkqname, arcqname = baseSetKey if arcrole.startswith("XBRL-") or ELR is None or \ linkqname is None or arcqname is None: continue elif arcrole in XbrlConst.standardArcroleCyclesAllowed: # TODO: table should be in this module, where it is used cyclesAllowed, specSect = XbrlConst.standardArcroleCyclesAllowed[arcrole] elif arcrole in self.modelXbrl.arcroleTypes and len(self.modelXbrl.arcroleTypes[arcrole]) > 0: cyclesAllowed = self.modelXbrl.arcroleTypes[arcrole][0].cyclesAllowed if arcrole in self.genericArcArcroles: specSect = "xbrlgene:violatedCyclesConstraint" else: specSect = "xbrl.5.1.4.3:cycles" else: cyclesAllowed = "any" specSect = None if cyclesAllowed != "any" or arcrole in (XbrlConst.summationItem,) \ or arcrole in self.genericArcArcroles \ or arcrole.startswith(XbrlConst.formulaStartsWith): relsSet = modelXbrl.relationshipSet(arcrole,ELR,linkqname,arcqname) if cyclesAllowed != "any" and \ (XbrlConst.isStandardExtLinkQname(linkqname) and XbrlConst.isStandardArcQname(arcqname)) \ or arcrole in self.genericArcArcroles: noUndirected = cyclesAllowed == "none" fromRelationships = relsSet.fromModelObjects() for relFrom, rels in fromRelationships.items(): cycleFound = self.fwdCycle(relsSet, rels, noUndirected, {relFrom}) if cycleFound is not None: path = str(relFrom.qname) + " " + " - ".join( "{0}:{1} {2}".format(rel.modelDocument.basename, rel.sourceline, rel.toModelObject.qname) for rel in reversed(cycleFound[1:])) modelXbrl.error(specSect, _("Relationships have a %(cycle)s cycle in arcrole %(arcrole)s \nlink role %(linkrole)s \nlink %(linkname)s, \narc %(arcname)s, \npath %(path)s"), modelObject=cycleFound[1:], cycle=cycleFound[0], path=path, arcrole=arcrole, linkrole=ELR, linkname=linkqname, arcname=arcqname), break # check calculation arcs for weight issues (note calc arc is an "any" cycles) if arcrole == XbrlConst.summationItem: for modelRel in relsSet.modelRelationships: weight = modelRel.weight fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept is not None and toConcept is not None: if weight == 0: modelXbrl.error("xbrl.5.2.5.2.1:zeroWeight", _("Calculation relationship has zero weight from %(source)s to %(target)s in link role %(linkrole)s"), modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR), fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if (fromBalance == toBalance and weight < 0) or \ (fromBalance != toBalance and weight > 0): modelXbrl.error("xbrl.5.1.1.2:balanceCalcWeightIllegal" + ("Negative" if weight < 0 else "Positive"), _("Calculation relationship has illegal weight %(weight)s from %(source)s, %(sourceBalance)s, to %(target)s, %(targetBalance)s, in link role %(linkrole)s (per 5.1.1.2 Table 6)"), modelObject=modelRel, weight=weight, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR, sourceBalance=fromBalance, targetBalance=toBalance) if not fromConcept.isNumeric or not toConcept.isNumeric: modelXbrl.error("xbrl.5.2.5.2:nonNumericCalc", _("Calculation relationship has illegal concept from %(source)s%(sourceNumericDecorator)s to %(target)s%(targetNumericDecorator)s in link role %(linkrole)s"), modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR, sourceNumericDecorator="" if fromConcept.isNumeric else _(" (non-numeric)"), targetNumericDecorator="" if toConcept.isNumeric else _(" (non-numeric)")) # check presentation relationships for preferredLabel issues elif arcrole == XbrlConst.parentChild: for modelRel in relsSet.modelRelationships: preferredLabel = modelRel.preferredLabel toConcept = modelRel.toModelObject if preferredLabel is not None and toConcept is not None and \ toConcept.label(preferredLabel=preferredLabel,fallbackToQname=False) is None: modelXbrl.error("xbrl.5.2.4.2.1:preferredLabelMissing", _("Presentation relationship from %(source)s to %(target)s in link role %(linkrole)s missing preferredLabel %(preferredLabel)s"), modelObject=modelRel, source=modelRel.fromModelObject.qname, target=toConcept.qname, linkrole=ELR, preferredLabel=preferredLabel) # check essence-alias relationships elif arcrole == XbrlConst.essenceAlias: for modelRel in relsSet.modelRelationships: fromConcept = modelRel.fromModelObject toConcept = modelRel.toModelObject if fromConcept is not None and toConcept is not None: if fromConcept.type != toConcept.type or fromConcept.periodType != toConcept.periodType: modelXbrl.error("xbrl.5.2.6.2.2:essenceAliasTypes", _("Essence-alias relationship from %(source)s to %(target)s in link role %(linkrole)s has different types or periodTypes"), modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR) fromBalance = fromConcept.balance toBalance = toConcept.balance if fromBalance and toBalance: if fromBalance and toBalance and fromBalance != toBalance: modelXbrl.error("xbrl.5.2.6.2.2:essenceAliasBalance", _("Essence-alias relationship from %(source)s to %(target)s in link role %(linkrole)s has different balances")).format( modelObject=modelRel, source=fromConcept.qname, target=toConcept.qname, linkrole=ELR) elif modelXbrl.hasXDT and arcrole.startswith(XbrlConst.dimStartsWith): ValidateXbrlDimensions.checkBaseSet(self, arcrole, ELR, relsSet) elif (modelXbrl.hasFormulae or modelXbrl.hasTableRendering) and arcrole.startswith(XbrlConst.formulaStartsWith): ValidateFormula.checkBaseSet(self, arcrole, ELR, relsSet) modelXbrl.isDimensionsValidated = True modelXbrl.profileStat(_("validateRelationships")) # instance checks modelXbrl.modelManager.showStatus(_("validating instance")) self.footnoteRefs = set() if modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE or \ modelXbrl.modelDocument.type == ModelDocument.Type.INLINEXBRL: self.factsWithDeprecatedIxNamespace = [] self.checkFacts(modelXbrl.facts) if self.factsWithDeprecatedIxNamespace: self.modelXbrl.info("arelle:info", _("%(count)s facts have deprecated transformation namespace %(namespace)s"), modelObject=self.factsWithDeprecatedIxNamespace, count=len(self.factsWithDeprecatedIxNamespace), namespace=FunctionIxt.deprecatedNamespaceURI) del self.factsWithDeprecatedIxNamespace #instance checks for cntx in modelXbrl.contexts.values(): if cntx.isStartEndPeriod: try: if cntx.endDatetime <= cntx.startDatetime: self.modelXbrl.error("xbrl.4.7.2:periodStartBeforeEnd", _("Context %(contextID)s must have startDate less than endDate"), modelObject=cntx, contextID=cntx.id) except (TypeError, ValueError) as err: self.modelXbrl.error("xbrl.4.7.2:contextDateError", _("Context %(contextID) startDate or endDate: %(error)s"), modelObject=cntx, contextID=cntx.id, error=err) elif cntx.isInstantPeriod: try: cntx.instantDatetime #parse field except ValueError as err: self.modelXbrl.error("xbrl.4.7.2:contextDateError", _("Context %(contextID)s instant date: %(error)s"), modelObject=cntx, contextID=cntx.id, error=err) self.segmentScenario(cntx.segment, cntx.id, "segment", "4.7.3.2") self.segmentScenario(cntx.scenario, cntx.id, "scenario", "4.7.4") for unit in modelXbrl.units.values(): mulDivMeasures = unit.measures if mulDivMeasures: for measures in mulDivMeasures: for measure in measures: if measure.namespaceURI == XbrlConst.xbrli and not \ measure in (XbrlConst.qnXbrliPure, XbrlConst.qnXbrliShares): self.modelXbrl.error("xbrl.4.8.2:measureElement", _("Unit %(unitID)s illegal measure: %(measure)s"), modelObject=unit, unitID=unit.id, measure=measure) for numeratorMeasure in mulDivMeasures[0]: if numeratorMeasure in mulDivMeasures[1]: self.modelXbrl.error("xbrl.4.8.4:measureBothNumDenom", _("Unit %(unitID)s numerator measure: %(measure)s also appears as denominator measure"), modelObject=unit, unitID=unit.id, measure=numeratorMeasure) modelXbrl.profileStat(_("validateInstance")) if modelXbrl.hasXDT: modelXbrl.modelManager.showStatus(_("validating dimensions")) ''' uncomment if using otherFacts in checkFact dimCheckableFacts = set(f for f in modelXbrl.factsInInstance if f.concept.isItem and f.context is not None) while (dimCheckableFacts): # check one and all of its compatible family members f = dimCheckableFacts.pop() ValidateXbrlDimensions.checkFact(self, f, dimCheckableFacts) del dimCheckableFacts ''' self.checkFactDimensions(modelXbrl.facts) # check fact dimensions in document order for cntx in modelXbrl.contexts.values(): ValidateXbrlDimensions.checkContext(self,cntx) modelXbrl.profileStat(_("validateDimensions")) # dimensional validity #concepts checks modelXbrl.modelManager.showStatus(_("validating concepts")) for concept in modelXbrl.qnameConcepts.values(): conceptType = concept.type if XbrlConst.isStandardNamespace(concept.qname.namespaceURI) or \ not concept.modelDocument.inDTS: continue if concept.isTuple: # must be global if not concept.getparent().localName == "schema": self.modelXbrl.error("xbrl.4.9:tupleGloballyDeclared", _("Tuple %(concept)s must be declared globally"), modelObject=concept, concept=concept.qname) if concept.periodType: self.modelXbrl.error("xbrl.4.9:tuplePeriodType", _("Tuple %(concept)s must not have periodType"), modelObject=concept, concept=concept.qname) if concept.balance: self.modelXbrl.error("xbrl.4.9:tupleBalance", _("Tuple %(concept)s must not have balance"), modelObject=concept, concept=concept.qname) if conceptType is not None: # check attribute declarations for attribute in conceptType.attributes.values(): if attribute.qname.namespaceURI in (XbrlConst.xbrli, XbrlConst.link, XbrlConst.xlink, XbrlConst.xl): self.modelXbrl.error("xbrl.4.9:tupleAttribute", _("Tuple %(concept)s must not have attribute in this namespace %(attribute)s"), modelObject=concept, concept=concept.qname, attribute=attribute.qname) # check for mixed="true" or simple content if XmlUtil.descendantAttr(conceptType, XbrlConst.xsd, ("complexType", "complexContent"), "mixed") == "true": self.modelXbrl.error("xbrl.4.9:tupleMixedContent", _("Tuple %(concept)s must not have mixed content"), modelObject=concept, concept=concept.qname) if XmlUtil.descendant(conceptType, XbrlConst.xsd, "simpleContent"): self.modelXbrl.error("xbrl.4.9:tupleSimpleContent", _("Tuple %(concept)s must not have simple content"), modelObject=concept, concept=concept.qname) # child elements must be item or tuple for elementQname in conceptType.elements: childConcept = self.modelXbrl.qnameConcepts.get(elementQname) if childConcept is None: self.modelXbrl.error("xbrl.4.9:tupleElementUndefined", _("Tuple %(concept)s element %(tupleElement)s not defined"), modelObject=concept, concept=str(concept.qname), tupleElement=elementQname) elif not (childConcept.isItem or childConcept.isTuple or # isItem/isTuple do not include item or tuple itself childConcept.qname == XbrlConst.qnXbrliItem or # subs group includes item as member childConcept.qname == XbrlConst.qnXbrliTuple): self.modelXbrl.error("xbrl.4.9:tupleElementItemOrTuple", _("Tuple %(concept)s must not have element %(tupleElement)s not an item or tuple"), modelObject=concept, concept=concept.qname, tupleElement=elementQname) elif concept.isItem: if concept.periodType not in periodTypeValues: #("instant","duration"): self.modelXbrl.error("xbrl.5.1.1.1:itemPeriodType", _("Item %(concept)s must have a valid periodType"), modelObject=concept, concept=concept.qname) if concept.isMonetary: if concept.balance not in balanceValues: #(None, "credit","debit"): self.modelXbrl.error("xbrl.5.1.1.2:itemBalance", _("Item %(concept)s must have a valid balance %(balance)s"), modelObject=concept, concept=concept.qname, balance=concept.balance) else: if concept.balance: self.modelXbrl.error("xbrl.5.1.1.2:itemBalance", _("Item %(concept)s may not have a balance"), modelObject=concept, concept=concept.qname) if concept.baseXbrliType not in baseXbrliTypes: self.modelXbrl.error("xbrl.5.1.1.3:itemType", _("Item %(concept)s type %(itemType)s invalid"), modelObject=concept, concept=concept.qname, itemType=concept.baseXbrliType) if modelXbrl.hasXDT: if concept.isHypercubeItem and not concept.abstract == "true": self.modelXbrl.error("xbrldte:HypercubeElementIsNotAbstractError", _("Hypercube item %(concept)s must be abstract"), modelObject=concept, concept=concept.qname) elif concept.isDimensionItem and not concept.abstract == "true": self.modelXbrl.error("xbrldte:DimensionElementIsNotAbstractError", _("Dimension item %(concept)s must be abstract"), modelObject=concept, concept=concept.qname) if modelXbrl.hasXDT: ValidateXbrlDimensions.checkConcept(self, concept) modelXbrl.profileStat(_("validateConcepts")) modelXbrl.modelManager.showStatus(_("validating DTS")) self.DTSreferenceResourceIDs = {} ValidateXbrlDTS.checkDTS(self, modelXbrl.modelDocument, []) del self.DTSreferenceResourceIDs global validateUniqueParticleAttribution if validateUniqueParticleAttribution is None: from arelle.XmlValidateParticles import validateUniqueParticleAttribution for modelType in modelXbrl.qnameTypes.values(): validateUniqueParticleAttribution(modelXbrl, modelType.particlesList, modelType) modelXbrl.profileStat(_("validateDTS")) if self.validateCalcLB: modelXbrl.modelManager.showStatus(_("Validating instance calculations")) ValidateXbrlCalcs.validate(modelXbrl, inferDecimals=self.validateInferDecimals) modelXbrl.profileStat(_("validateCalculations")) if (modelXbrl.modelManager.validateUtr or (self.parameters and self.parameters.get(qname("forceUtrValidation",noPrefixIsNoNamespace=True),(None,"false"))[1] == "true") or #(self.validateEFM and #any((concept.namespaceURI in self.disclosureSystem.standardTaxonomiesDict) # for concept in self.modelXbrl.nameConcepts.get("UTR",())))): (self.validateEFM and any(modelDoc.definesUTR for modelDoc in self.modelXbrl.urlDocs.values()))): ValidateUtr.validate(modelXbrl) modelXbrl.profileStat(_("validateUTR")) if modelXbrl.hasFormulae or modelXbrl.modelRenderingTables: ValidateFormula.validate(self, statusMsg=_("compiling formulae and rendering tables") if (modelXbrl.hasFormulae and modelXbrl.modelRenderingTables) else (_("compiling formulae") if modelXbrl.hasFormulae else _("compiling rendering tables"))) modelXbrl.modelManager.showStatus(_("ready"), 2000)
def __init__(self, modelXbrl, arcrole, linkrole=None, linkqname=None, arcqname=None, includeProhibits=False): self.isChanged = False self.modelXbrl = modelXbrl self.arcrole = arcrole # may be str, tuple or frozenset self.linkrole = linkrole # may be str, tuple or frozenset self.linkqname = linkqname self.arcqname = arcqname relationshipSetKey = (arcrole, linkrole, linkqname, arcqname, includeProhibits) # base sets does not care about the #includeProhibits if isinstance(arcrole, (str, NoneType)) and isinstance(linkrole, (str, NoneType)): modelLinks = self.modelXbrl.baseSets.get( (arcrole, linkrole, linkqname, arcqname), []) else: # arcrole is a set of arcroles modelLinks = [] for ar in (arcrole, ) if isinstance(arcrole, (str, NoneType)) else arcrole: for lr in (linkrole, ) if isinstance(linkrole, (str, NoneType)) else linkrole: modelLinks.extend( self.modelXbrl.baseSets.get( (ar, lr, linkqname, arcqname), [])) # gather arcs relationships = {} isDimensionRel = self.arcrole == "XBRL-dimensions" # all dimensional relationship arcroles isFormulaRel = self.arcrole == "XBRL-formulae" # all formula relationship arcroles isTableRenderingRel = self.arcrole == "Table-rendering" isFootnoteRel = self.arcrole == "XBRL-footnotes" # all footnote relationship arcroles if not isinstance(arcrole, (tuple, frozenset)): arcrole = (arcrole, ) for modelLink in modelLinks: arcs = [] linkEltQname = modelLink.qname for linkChild in modelLink: linkChildArcrole = linkChild.get( "{http://www.w3.org/1999/xlink}arcrole") if linkChild.get("{http://www.w3.org/1999/xlink}type" ) == "arc" and linkChildArcrole: if isFootnoteRel: # arcrole is fact-footnote or other custom footnote relationship arcs.append(linkChild) elif isDimensionRel: if XbrlConst.isDimensionArcrole(linkChildArcrole): arcs.append(linkChild) elif isFormulaRel: if XbrlConst.isFormulaArcrole(linkChildArcrole): arcs.append(linkChild) elif isTableRenderingRel: if XbrlConst.isTableRenderingArcrole(linkChildArcrole): arcs.append(linkChild) elif (linkChildArcrole in arcrole and (arcqname is None or arcqname == linkChild.qname) and (linkqname is None or linkqname == linkEltQname)): arcs.append(linkChild) # build network for arcElement in arcs: fromLabel = arcElement.get( "{http://www.w3.org/1999/xlink}from") toLabel = arcElement.get("{http://www.w3.org/1999/xlink}to") for fromResource in modelLink.labeledResources[fromLabel]: for toResource in modelLink.labeledResources[toLabel]: if isinstance( fromResource, (ModelResource, LocPrototype)) and isinstance( toResource, (ModelResource, LocPrototype)): modelRel = ModelDtsObject.ModelRelationship( modelLink.modelDocument, arcElement, fromResource.dereference(), toResource.dereference()) modelRelEquivalenceHash = modelRel.equivalenceHash if modelRelEquivalenceHash not in relationships: relationships[ modelRelEquivalenceHash] = modelRel else: # use equivalenceKey instead of hash otherRel = relationships[ modelRelEquivalenceHash] if otherRel is not USING_EQUIVALENCE_KEY: # move equivalentRel to use key instead of hasn if modelRel.isIdenticalTo(otherRel): continue # skip identical arc relationships[ otherRel.equivalenceKey] = otherRel relationships[ modelRelEquivalenceHash] = USING_EQUIVALENCE_KEY modelRelEquivalenceKey = modelRel.equivalenceKey # this is a complex tuple to compute, get once for below if modelRelEquivalenceKey not in relationships or \ modelRel.priorityOver(relationships[modelRelEquivalenceKey]): relationships[ modelRelEquivalenceKey] = modelRel #reduce effective arcs and order relationships... self.modelRelationshipsFrom = None self.modelRelationshipsTo = None self.modelConceptRoots = None self.modellinkRoleUris = None orderRels = defaultdict(list) for modelRel in relationships.values(): if (modelRel is not USING_EQUIVALENCE_KEY and (includeProhibits or not modelRel.isProhibited)): orderRels[modelRel.order].append(modelRel) self.modelRelationships = [ modelRel for order in sorted(orderRels.keys()) for modelRel in orderRels[order] ] modelXbrl.relationshipSets[relationshipSetKey] = self
def __init__(self, modelXbrl, arcrole, linkrole=None, linkqname=None, arcqname=None, includeProhibits=False): self.isChanged = False self.modelXbrl = modelXbrl self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname baseSetKey = (arcrole, linkrole, linkqname, arcqname) relationshipSetKey = (arcrole, linkrole, linkqname, arcqname, includeProhibits) # base sets does not care about the #includeProhibits if baseSetKey in self.modelXbrl.baseSets: modelLinks = self.modelXbrl.baseSets[baseSetKey] else: modelLinks = [] # gather arcs relationships = {} isDimensionRel = self.arcrole == "XBRL-dimensions" # all dimensional relationship arcroles isFormulaRel = self.arcrole == "XBRL-formulae" # all formula relationship arcroles isEuRenderingRel = self.arcrole == "EU-rendering" isFootnoteRel = self.arcrole == "XBRL-footnotes" # all footnote relationship arcroles for modelLink in modelLinks: arcs = [] linkEltQname = modelLink.qname for linkChild in modelLink.element.childNodes: if ( linkChild.nodeType == 1 and linkChild.getAttributeNS(XbrlConst.xlink, "type") == "arc" and linkChild.hasAttributeNS(XbrlConst.xlink, "arcrole") ): linkChildArcrole = linkChild.getAttributeNS(XbrlConst.xlink, "arcrole") linkChildQname = linkChild if isFootnoteRel: arcs.append(linkChild) elif isDimensionRel: if XbrlConst.isDimensionArcrole(linkChildArcrole): arcs.append(linkChild) elif isFormulaRel: if XbrlConst.isFormulaArcrole(linkChildArcrole): arcs.append(linkChild) elif isEuRenderingRel: if XbrlConst.isEuRenderingArcrole(linkChildArcrole): arcs.append(linkChild) elif ( arcrole == linkChildArcrole and (arcqname is None or arcqname == linkChildQname) and (linkqname is None or linkqname == linkEltQname) ): arcs.append(linkChild) # build network for arcElement in arcs: arcrole = arcElement.getAttributeNS(XbrlConst.xlink, "arcrole") fromLabel = arcElement.getAttributeNS(XbrlConst.xlink, "from") toLabel = arcElement.getAttributeNS(XbrlConst.xlink, "to") for fromResource in modelLink.labeledResources[fromLabel]: for toResource in modelLink.labeledResources[toLabel]: modelRel = ModelObject.createRelationship( modelLink.modelDocument, arcElement, fromResource.dereference(), toResource.dereference() ) modelRelEquivalenceKey = ( modelRel.equivalenceKey ) # this is a complex tuple to compute, get once for below if modelRelEquivalenceKey not in relationships or modelRel.priorityOver( relationships[modelRelEquivalenceKey] ): relationships[modelRelEquivalenceKey] = modelRel # reduce effective arcs and order relationships... self.modelRelationships = [] self.modelRelationshipsFrom = None self.modelRelationshipsTo = None self.modelConceptRoots = None self.modellinkRoleUris = None orderRels = defaultdict(list) for modelRel in relationships.values(): if includeProhibits or not modelRel.isProhibited: orderRels[modelRel.order].append(modelRel) for order in sorted(orderRels.keys()): for modelRel in orderRels[order]: self.modelRelationships.append(modelRel) modelXbrl.relationshipSets[relationshipSetKey] = self
def view(self, firstTime=False, relationshipSet=None): self.blockSelectEvent = 1 self.blockViewModelObject = 0 self.tag_has = defaultdict(list) # temporary until Tk 8.6 # relationship set based on linkrole parameter, to determine applicable linkroles if relationshipSet is None: relationshipSet = groupRelationshipSet(self.modelXbrl, self.arcrole, self.linkrole, self.linkqname, self.arcqname) if not relationshipSet: self.modelXbrl.modelManager.addToLog(_("no relationships for {0}").format(groupRelationshipLabel(self.arcrole))) return False if firstTime: self.showReferences = False # set up treeView widget and tabbed pane hdr = self.treeColHdr if self.treeColHdr else _("{0} Relationships").format(groupRelationshipLabel(self.arcrole)) self.treeView.heading("#0", text=hdr) if self.showColumns: if self.arcrole == XbrlConst.parentChild: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("preferredLabel", "type", "references") self.treeView.column("preferredLabel", width=64, anchor="w", stretch=False) self.treeView.heading("preferredLabel", text=_("Pref. Label")) self.treeView.column("type", width=100, anchor="w", stretch=False) self.treeView.heading("type", text=_("Type")) self.treeView.column("references", width=200, anchor="w", stretch=False) self.treeView.heading("references", text=_("References")) elif self.arcrole == XbrlConst.summationItem: # extra columns self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("weight", "balance") self.treeView.column("weight", width=48, anchor="w", stretch=False) self.treeView.heading("weight", text=_("Weight")) self.treeView.column("balance", width=70, anchor="w", stretch=False) self.treeView.heading("balance", text=_("Balance")) elif self.arcrole == "XBRL-dimensions": # add columns for dimensional information self.treeView.column("#0", width=300, anchor="w") self.treeView["columns"] = ("arcrole", "contextElement", "closed", "usable") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("contextElement", width=50, anchor="center", stretch=False) self.treeView.heading("contextElement", text="Context") self.treeView.column("closed", width=40, anchor="center", stretch=False) self.treeView.heading("closed", text="Closed") self.treeView.column("usable", width=40, anchor="center", stretch=False) self.treeView.heading("usable", text="Usable") elif self.arcrole == "Table-rendering": # add columns for dimensional information self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("axis", "abstract", "merge", "header", "priItem", "dims") self.treeView.column("axis", width=28, anchor="center", stretch=False) self.treeView.heading("axis", text="Axis") self.treeView.column("abstract", width=24, anchor="center", stretch=False) self.treeView.heading("abstract", text="Abs") self.treeView.column("merge", width=26, anchor="center", stretch=False) self.treeView.heading("merge", text="Mrg") self.treeView.column("header", width=160, anchor="w", stretch=False) self.treeView.heading("header", text="Header") self.treeView.column("priItem", width=100, anchor="w", stretch=False) self.treeView.heading("priItem", text="Primary Item") self.treeView.column("dims", width=150, anchor="w", stretch=False) self.treeView.heading("dims", text=_("Dimensions")) elif isinstance(self.arcrole, (list,tuple)) or XbrlConst.isResourceArcrole(self.arcrole): self.isResourceArcrole = True self.showReferences = isinstance(self.arcrole, _STR_BASE) and self.arcrole.endswith("-reference") self.treeView.column("#0", width=160, anchor="w") self.treeView["columns"] = ("arcrole", "resource", "resourcerole", "lang") self.treeView.column("arcrole", width=100, anchor="w", stretch=False) self.treeView.heading("arcrole", text="Arcrole") self.treeView.column("resource", width=60, anchor="w", stretch=False) self.treeView.heading("resource", text="Resource") self.treeView.column("resourcerole", width=100, anchor="w", stretch=False) self.treeView.heading("resourcerole", text="Resource Role") self.treeView.column("lang", width=36, anchor="w", stretch=False) self.treeView.heading("lang", text="Lang") self.clearTreeView() self.id = 1 # sort URIs by definition linkroleUris = [] for linkroleUri in relationshipSet.linkRoleUris: modelRoleTypes = self.modelXbrl.roleTypes.get(linkroleUri) if modelRoleTypes: roledefinition = ((self.hasTableIndex and getattr(modelRoleTypes[0], "_tableIndex", False)) or modelRoleTypes[0].genLabel(lang=self.lang, strip=True) or modelRoleTypes[0].definition or linkroleUri) roleId = modelRoleTypes[0].objectId(self.id) else: roledefinition = linkroleUri roleId = "node{0}".format(self.id) self.id += 1 linkroleUris.append((roledefinition, linkroleUri, roleId)) linkroleUris.sort() # for each URI in definition order tableGroup = "" for roledefinition, linkroleUri, roleId in linkroleUris: if self.showLinkroles: if isinstance(roledefinition, tuple): # tableGroup nextTableGroup, order, roledefinition = roledefinition if tableGroup != nextTableGroup: self.treeView.insert("", "end", nextTableGroup, text=nextTableGroup[1:], tags=("Group",)) if not tableGroup: # first tableGroup item, expand it self.setTreeItemOpen(nextTableGroup,open=True) tableGroup = nextTableGroup linknode = self.treeView.insert(tableGroup, "end", roleId, text=roledefinition, tags=("ELR",)) else: linknode = "" if self.showRelationships: linkRelationshipSet = groupRelationshipSet(self.modelXbrl, self.arcrole, linkroleUri, self.linkqname, self.arcqname) for rootConcept in linkRelationshipSet.rootConcepts: self.viewConcept(rootConcept, rootConcept, "", self.labelrole, linknode, 1, linkRelationshipSet, set()) self.tag_has[linkroleUri].append(linknode) if self.expandAllOnFirstDisplay: self.expandAll()
def checkDTSdocument(val, modelDocument, isFilingDocument): for hrefElt, _hrefedDoc, hrefId in modelDocument.hrefObjects: if hrefId: #check scheme regardless of whether document loaded # check all xpointer schemes for scheme, _path in XmlUtil.xpointerSchemes(hrefId): if scheme == "element" and val.validateDisclosureSystem: val.modelXbrl.error( ("EFM.6.03.06", "GFM.1.01.03"), _("Href %(elementHref)s may only have shorthand xpointers" ), edgarCode="cp-0306-Href-Xpointers", modelObject=hrefElt, elementHref=hrefElt.get( "{http://www.w3.org/1999/xlink}href")) if not isFilingDocument: return # not a filing's extension document if modelDocument.type == ModelDocument.Type.SCHEMA: if modelDocument.targetNamespace is not None and len( modelDocument.targetNamespace) > 85: l = len(modelDocument.targetNamespace.encode("utf-8")) if l > 255: val.modelXbrl.error( "EFM.6.07.30", _("Shorten this declaration URI from %(length)s to 255 bytes or fewer in UTF-8: %(value)s." ), edgarCode="du-0730-uri-length-limit", modelObject=modelDocument.xmlRootElement, length=l, targetNamespace=modelDocument.targetNamespace, value=modelDocument.targetNamespace) if modelDocument.type in (ModelDocument.Type.SCHEMA, ModelDocument.Type.LINKBASE): isSchema = modelDocument.type == ModelDocument.Type.SCHEMA if isSchema: for elt in modelDocument.xmlRootElement.iter( tag="{http://www.w3.org/2001/XMLSchema}*"): if elt.namespaceURI == XbrlConst.xsd: localName = elt.localName if localName in {"element", "complexType", "simpleType"}: name = elt.get("name") if name and len(name) > 64: l = len(name.encode("utf-8")) if l > 200: val.modelXbrl.error( "EFM.6.07.29", _("Shorten this declaration name from %(length)s to 200 bytes or fewer in UTF-8: %(name)s" ), edgarCode="du-0729-name-length-limit", modelObject=elt, element=localName, name=name, length=l) for elt in modelDocument.xmlRootElement.iter(): if isinstance(elt, ModelObject): xlinkType = elt.get("{http://www.w3.org/1999/xlink}type") xlinkRole = elt.get("{http://www.w3.org/1999/xlink}role") if elt.namespaceURI == XbrlConst.link: # check schema roleTypes if elt.localName in ("roleType", "arcroleType"): uriAttr = { "roleType": "roleURI", "arcroleType": "arcroleURI" }[elt.localName] roleURI = elt.get(uriAttr) if len(roleURI) > 85: l = len(roleURI.encode("utf-8")) if l > 255: val.modelXbrl.error( "EFM.6.07.30", _("Shorten this declaration URI from %(length)s to 255 bytes or fewer in UTF-8: %(value)s." ), edgarCode="du-0730-uri-length-limit", modelObject=elt, element=elt.qname, attribute=uriAttr, length=l, roleURI=roleURI, value=roleURI) if elt.localName == "arcroleRef": refUri = elt.get("arcroleURI") hrefAttr = elt.get("{http://www.w3.org/1999/xlink}href") hrefUri, hrefId = UrlUtil.splitDecodeFragment(hrefAttr) if hrefUri not in val.disclosureSystem.standardTaxonomiesDict: val.modelXbrl.error( ("EFM.6.09.06", "GFM.1.04.06"), _("An arcroleRef element refers to %(xlinkHref)s, an arcrole, %(refURI)s, that is not defined in a standard taxonomy. " "Please recheck submission."), edgarCode="fs-0906-Custom-Arcrole-Referenced", modelObject=elt, refURI=refUri, xlinkHref=hrefUri) if modelDocument.type == ModelDocument.Type.INLINEXBRL and elt.namespaceURI in XbrlConst.ixbrlAll: if elt.localName == "footnote": if val.validateGFM: if elt.get("{http://www.w3.org/1999/xlink}arcrole" ) != XbrlConst.factFootnote: # must be in a nonDisplay div if not any( inlineDisplayNonePattern.search( e.get("style") or "") for e in XmlUtil.ancestors( elt, XbrlConst.xhtml, "div")): val.modelXbrl.error( ("EFM.N/A", "GFM:1.10.16"), _("Inline XBRL footnote %(footnoteID)s must be in non-displayable div due to arcrole %(arcrole)s" ), modelObject=elt, footnoteID=elt.get("footnoteID"), arcrole=elt.get( "{http://www.w3.org/1999/xlink}arcrole" )) if not elt.get( "{http://www.w3.org/XML/1998/namespace}lang"): val.modelXbrl.error( ("EFM.N/A", "GFM:1.10.13"), _("Inline XBRL footnote %(footnoteID)s is missing an xml:lang attribute" ), modelObject=elt, footnoteID=id) if xlinkType == "extended": if not xlinkRole or xlinkRole == "": val.modelXbrl.error( ("EFM.6.09.04", "GFM.1.04.04"), "The %(element)s element requires an xlink:role such as the default, 'http://www.xbrl.org/2003/role/label'. " "Please recheck submission.", edgarCode="fs-0904-Resource-Role-Missing", modelObject=elt, element=elt.qname) if not val.extendedElementName: val.extendedElementName = elt.qname elif val.extendedElementName != elt.qname: val.modelXbrl.error( ("EFM.6.09.07", "GFM:1.04.07"), _("Your filing contained extended type links with roles of different namesapces and local names, %(element)s " "and %(element2)s in the same linkbase, %(refUrl)s. Please recheck your submission and ensure that all " "extended-type links in a single linkbase have the same namespace and local name." ), edgarCode="cp-0907-Linkbases-Distinct", refUrl=elt.modelDocument.basename, modelObject=elt, element=elt.qname, element2=val.extendedElementName) elif xlinkType == "resource": if not xlinkRole: val.modelXbrl.error( ("EFM.6.09.04", "GFM.1.04.04"), _("The %(element)s element requires an xlink:role such as the default, 'http://www.xbrl.org/2003/role/label'. " "Please recheck submission."), edgarCode="fs-0904-Resource-Role-Missing", modelObject=elt, element=elt.qname) elif not XbrlConst.isStandardRole(xlinkRole): modelsRole = val.modelXbrl.roleTypes.get(xlinkRole) if (modelsRole is None or len(modelsRole) == 0 or modelsRole[0].modelDocument.targetNamespace not in val.disclosureSystem.standardTaxonomiesDict ): val.modelXbrl.error( ("EFM.6.09.05", "GFM.1.04.05"), _("The %(element)s element has a role, %(roleDefinition)s, that is not defined in a standard taxonomy, " "resource labeled %(xlinkLabel)s. Please recheck submission." ), edgarCode="fs-0905-Custom-Resource-Role-Used", modelObject=elt, xlinkLabel=elt.get( "{http://www.w3.org/1999/xlink}label"), role=xlinkRole, element=elt.qname, roleDefinition=val.modelXbrl.roleTypeDefinition( xlinkRole)) elif xlinkType == "arc": if elt.get("priority") is not None: priority = elt.get("priority") try: if int(priority) >= 10: val.modelXbrl.error( ("EFM.6.09.09", "GFM.1.04.08"), _("The priority attribute %(priority)s has a value of ten or greater, on arc %(arcElement)s from %(xlinkFrom)s to %(xlinkTo)s. " "Please change the priority to value less than 10 and resubmit." ), edgarCode= "du-0909-Relationship-Priority-Not-Less-Than-Ten", modelObject=elt, arcElement=elt.qname, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), priority=priority) except (ValueError): val.modelXbrl.error( ("EFM.6.09.09", "GFM.1.04.08"), _("The priority attribute %(priority)s has a value of ten or greater, on arc %(arcElement)s from %(xlinkFrom)s to %(xlinkTo)s. " "Please change the priority to value less than 10 and resubmit." ), edgarCode= "du-0909-Relationship-Priority-Not-Less-Than-Ten", modelObject=elt, arcElement=elt.qname, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), priority=priority) if elt.namespaceURI == XbrlConst.link: if elt.localName == "presentationArc" and not elt.get( "order"): val.modelXbrl.error( ("EFM.6.12.01", "GFM.1.06.01"), _("The presentation relationship from %(conceptFrom)s to %(conceptTo)s does not have an order attribute. " "Please provide a value."), edgarCode="rq-1201-Presentation-Order-Missing", modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt)) elif elt.localName == "calculationArc": if not elt.get("order"): val.modelXbrl.error( ("EFM.6.14.01", "GFM.1.07.01"), _("The calculation relationship from %(conceptFrom)s to %(conceptTo)s does not have an order attribute." ), edgarCode= "du-1401-Calculation-Relationship-Order-Missing", modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt)) try: weightAttr = elt.get("weight") weight = float(weightAttr) if not weight in (1, -1): val.modelXbrl.error( ("EFM.6.14.02", "GFM.1.07.02"), _("Weight value %(weight)s on the calculation relationship from %(conceptFrom)s to %(conceptTo)s " "must be equal to 1 or to -1. Please recheck submission." ), edgarCode= "fs-1402-Calculation-Relationship-Weight-Not-Unitary", modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt), weight=weightAttr) except ValueError: val.modelXbrl.error( ("EFM.6.14.02", "GFM.1.07.02"), _("Weight value %(weight)s on the calculation relationship from %(conceptFrom)s to %(conceptTo)s " "must be equal to 1 or to -1. Please recheck submission." ), edgarCode= "fs-1402-Calculation-Relationship-Weight-Not-Unitary", modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt), weight=weightAttr) elif elt.localName == "definitionArc": if not elt.get("order"): val.modelXbrl.error( ("EFM.6.16.01", "GFM.1.08.01"), _("The Definition relationship from %(conceptFrom)s to %(conceptTo)s does not have an order attribute." ), edgarCode= "du-1601-Definition-Relationship-Order-Missing", modelObject=elt, xlinkFrom=elt.get( "{http://www.w3.org/1999/xlink}from"), xlinkTo=elt.get( "{http://www.w3.org/1999/xlink}to"), conceptFrom=arcFromConceptQname(elt), conceptTo=arcToConceptQname(elt))
def __init__(self, modelXbrl, arcrole, linkrole=None, linkqname=None, arcqname=None, includeProhibits=False): self.isChanged = False self.modelXbrl = modelXbrl self.arcrole = arcrole self.linkrole = linkrole self.linkqname = linkqname self.arcqname = arcqname relationshipSetKey = (arcrole, linkrole, linkqname, arcqname, includeProhibits) # base sets does not care about the #includeProhibits if not isinstance(arcrole,(tuple,frozenset)): modelLinks = self.modelXbrl.baseSets.get((arcrole, linkrole, linkqname, arcqname), []) else: # arcrole is a set of arcroles modelLinks = [] for ar in arcrole: modelLinks.extend(self.modelXbrl.baseSets.get((ar, linkrole, linkqname, arcqname), [])) # gather arcs relationships = {} isDimensionRel = self.arcrole == "XBRL-dimensions" # all dimensional relationship arcroles isFormulaRel = self.arcrole == "XBRL-formulae" # all formula relationship arcroles isTableRenderingRel = self.arcrole == "Table-rendering" isFootnoteRel = self.arcrole == "XBRL-footnotes" # all footnote relationship arcroles if not isinstance(arcrole,(tuple,frozenset)): arcrole = (arcrole,) for modelLink in modelLinks: arcs = [] linkEltQname = modelLink.qname for linkChild in modelLink: linkChildArcrole = linkChild.get("{http://www.w3.org/1999/xlink}arcrole") if linkChild.get("{http://www.w3.org/1999/xlink}type") == "arc" and linkChildArcrole: linkChildQname = linkChild if isFootnoteRel: arcs.append(linkChild) elif isDimensionRel: if XbrlConst.isDimensionArcrole(linkChildArcrole): arcs.append(linkChild) elif isFormulaRel: if XbrlConst.isFormulaArcrole(linkChildArcrole): arcs.append(linkChild) elif isTableRenderingRel: if XbrlConst.isTableRenderingArcrole(linkChildArcrole): arcs.append(linkChild) elif (linkChildArcrole in arcrole and (arcqname is None or arcqname == linkChildQname) and (linkqname is None or linkqname == linkEltQname)): arcs.append(linkChild) # build network for arcElement in arcs: fromLabel = arcElement.get("{http://www.w3.org/1999/xlink}from") toLabel = arcElement.get("{http://www.w3.org/1999/xlink}to") for fromResource in modelLink.labeledResources[fromLabel]: for toResource in modelLink.labeledResources[toLabel]: if isinstance(fromResource,ModelResource) and isinstance(toResource,ModelResource): modelRel = ModelDtsObject.ModelRelationship(modelLink.modelDocument, arcElement, fromResource.dereference(), toResource.dereference()) modelRelEquivalenceKey = modelRel.equivalenceKey # this is a complex tuple to compute, get once for below if modelRelEquivalenceKey not in relationships or \ modelRel.priorityOver(relationships[modelRelEquivalenceKey]): relationships[modelRelEquivalenceKey] = modelRel #reduce effective arcs and order relationships... self.modelRelationshipsFrom = None self.modelRelationshipsTo = None self.modelConceptRoots = None self.modellinkRoleUris = None orderRels = defaultdict(list) for modelRel in relationships.values(): if includeProhibits or not modelRel.isProhibited: orderRels[modelRel.order].append(modelRel) self.modelRelationships = [modelRel for order in sorted(orderRels.keys()) for modelRel in orderRels[order]] modelXbrl.relationshipSets[relationshipSetKey] = self
def groupRelationshipLabel(arcrole): if isinstance(arcrole, (list, tuple)): # (group-name, [arcroles]) arcroleName = arcrole[0] else: arcroleName = XbrlConst.baseSetArcroleLabel(arcrole)[1:] return arcroleName