Exemple #1
0
def validateXbrlFinally(val, *args, **kwargs):
    if not (val.validateFERCplugin):
        return

    _xhtmlNs = "{{{}}}".format(xhtml)
    _xhtmlNsLen = len(_xhtmlNs)
    modelXbrl = val.modelXbrl
    modelDocument = modelXbrl.modelDocument
    if not modelDocument:
        return  # never loaded properly
    disclosureSystem = val.disclosureSystem

    _statusMsg = _("validating {0} filing rules").format(
        val.disclosureSystem.name)
    modelXbrl.profileActivity()
    modelXbrl.modelManager.showStatus(_statusMsg)

    isInlineXbrl = modelXbrl.modelDocument.type in (
        ModelDocument.Type.INLINEXBRL,
        ModelDocument.Type.INLINEXBRLDOCUMENTSET)
    requiredFactLang = disclosureSystem.defaultXmlLang.lower(
    ) if disclosureSystem.defaultXmlLang else disclosureSystem.defaultXmlLang

    # inline doc set has multiple instance names to check
    if modelXbrl.modelDocument.type == ModelDocument.Type.INLINEXBRLDOCUMENTSET:
        instanceNames = [
            ixDoc.basename
            for ixDoc in modelXbrl.modelDocument.referencesDocument.keys()
            if ixDoc.type == ModelDocument.Type.INLINEXBRL
        ]
        xbrlInstRoots = modelXbrl.ixdsHtmlElements
    else:  # single instance document to check is the entry point document
        instanceNames = [modelXbrl.modelDocument.basename]
        xbrlInstRoots = [modelXbrl.modelDocument.xmlDocument.getroot()]

    #6.5.15 facts with xml in text blocks
    ValidateFilingText.validateTextBlockFacts(
        modelXbrl,
        {
            True: ("gif", "jpg", "jpeg", "png"),  # img file extensions
            False:
            ("gif", "jpeg", "png")  # mime types: jpg is not a valid mime type
        })

    # check footnotes text
    if isInlineXbrl:
        _linkEltIter = (
            linkPrototype for linkKey, links in modelXbrl.baseSets.items()
            for linkPrototype in links if linkPrototype.modelDocument.type in (
                ModelDocument.Type.INLINEXBRL,
                ModelDocument.Type.INLINEXBRLDOCUMENTSET) and linkKey[1]
            and linkKey[2] and linkKey[3]  # fully specified roles
            and linkKey[0] != "XBRL-footnotes")
    else:
        _linkEltIter = xbrlInstRoots[0].iterdescendants(
            tag="{http://www.xbrl.org/2003/linkbase}footnoteLink")
    for footnoteLinkElt in _linkEltIter:
        if isinstance(footnoteLinkElt, (ModelObject, LinkPrototype)):
            for child in footnoteLinkElt:
                if isinstance(child,
                              (ModelObject, LocPrototype, ArcPrototype)):
                    xlinkType = child.get("{http://www.w3.org/1999/xlink}type")
                    if xlinkType == "resource" or isinstance(
                            child, ModelInlineFootnote):  # footnote
                        if not isInlineXbrl:  # inline content was validated before and needs continuations assembly
                            ValidateFilingText.validateFootnote(
                                modelXbrl, child)

    # same identifier in all contexts (EFM 6.5.3)
    entityIdentifiers = set()
    for xbrlInstRoot in xbrlInstRoots:  # check all inline docs in ix doc set
        for entityIdentifierElt in xbrlInstRoot.iterdescendants(
                "{http://www.xbrl.org/2003/instance}identifier"):
            if isinstance(entityIdentifierElt, ModelObject):
                entityIdentifiers.add("{}#{}".format(
                    entityIdentifierElt.get("scheme"),
                    XmlUtil.text(entityIdentifierElt)))
    if len(entityIdentifiers) > 1:
        modelXbrl.error(
            "FERC.6.05.03",
            _("There are more than one entity identifiers: %(entityIdentifiers)s."
              ),
            modelObject=modelXbrl,
            entityIdentifiers=", ".join(sorted(entityIdentifiers)))
    for ei in sorted(entityIdentifiers):
        scheme, _sep, identifier = ei.rpartition("#")
        if not disclosureSystem.identifierSchemePattern.match(
                scheme) or not disclosureSystem.identifierValuePattern.match(
                    identifier):
            modelXbrl.error(
                "FERC.6.05.01",
                _("Entity identifier %(identifier)s, or scheme %(scheme)s does not adhere "
                  "to the standard naming convention of <identifier scheme='http://www.ferc.gov/CID'>Cnnnnnn</identifier>'.  "
                  ),
                modelObject=modelXbrl,
                scheme=scheme,
                identifier=identifier)

    #6.5.4 scenario
    segContexts = set()
    uniqueContextHashes = {}
    contextIDs = set()
    precisionFacts = set()
    formType = None
    formEntrySchema = None
    factsForLang = {}
    keysNotDefaultLang = {}
    allFormEntryXsd = ()

    for c in modelXbrl.contexts.values():
        if XmlUtil.hasChild(c, xbrli, "segment"):
            segContexts.add(c)
        h = c.contextDimAwareHash
        if h in uniqueContextHashes:
            if c.isEqualTo(uniqueContextHashes[h]):
                modelXbrl.error(
                    "FERC.6.05.07",
                    _("The instance document contained more than one context equivalent to %(context)s (%(context2)s).  "
                      "Please remove duplicate contexts from the instance."),
                    modelObject=(c, uniqueContextHashes[h]),
                    context=c.id,
                    context2=uniqueContextHashes[h].id)
        else:
            uniqueContextHashes[h] = c
        contextIDs.add(c.id)

    if segContexts:
        modelXbrl.error(
            "FERC.6.05.04",
            _("There must be no contexts with segment, but %(count)s was(were) found: %(context)s."
              ),
            modelObject=segContexts,
            count=len(segContexts),
            context=", ".join(sorted(c.id for c in segContexts)))

    # unused contexts
    for f in modelXbrl.facts:
        factContextID = f.contextID
        contextIDs.discard(factContextID)
        if f.isNumeric:
            if f.precision is not None:
                precisionFacts.add(f)
        elif not f.isNil:
            langTestKey = "{0},{1}".format(f.qname, f.contextID)
            factsForLang.setdefault(langTestKey, []).append(f)
            lang = f.xmlLang
            if lang and lang.lower(
            ) != requiredFactLang:  # not lang.startswith(factLangStartsWith):
                keysNotDefaultLang[langTestKey] = f
        if getattr(f, "xValid", 0) >= VALID:
            if f.qname.localName == "FormType":
                formType = f.xValue
                formNum = re.sub("([0-9]+).*", r"\1", formType)
                formLtr = re.match("[^A-Z]*([A-Z]?)", formType).group(1)
                txDate = re.sub(
                    "http://ferc.gov/form/([0-9]{4}-[0-9]{2}-[0-9]{2})/ferc",
                    r"\1", f.qname.namespaceURI)

                formEntryXsd = "https://eCollection.ferc.gov/taxonomy/form{}/{}/form/form{}{}/form-{}{}_{}.xsd".format(
                    formNum, txDate, formNum, formLtr, formNum, formLtr,
                    txDate)

                formEntryXsdUAT = formEntryXsd.replace("eCollection",
                                                       "uat.eforms")
                formEntryXsdTest = formEntryXsd.replace(
                    "eCollection", "test.eforms")
                formEntryXsdDev = formEntryXsd.replace("eCollection",
                                                       "dev.eforms")

                confFormEntryXsd = "https://eCollection.ferc.gov/taxonomy/form{}/{}/ferc-core-footnote-roles_{}.xsd".format(
                    formNum, txDate, txDate)

                confFormEntryXsdUAT = confFormEntryXsd.replace(
                    "eCollection", "uat.eforms")
                confFormEntryXsdTest = confFormEntryXsd.replace(
                    "eCollection", "test.eforms")
                confFormEntryXsdDev = confFormEntryXsd.replace(
                    "eCollection", "dev.eforms")

                allFormEntryXsd = [
                    formEntryXsd, formEntryXsdUAT, formEntryXsdTest,
                    formEntryXsdDev, confFormEntryXsd, confFormEntryXsdUAT,
                    confFormEntryXsdTest, confFormEntryXsdDev
                ]

    unexpectedXsds = set(doc.modelDocument.uri for doc, referencingDoc in
                         modelXbrl.modelDocument.referencesDocument.items()
                         if "href" in referencingDoc.referenceTypes
                         if doc.modelDocument.uri not in allFormEntryXsd)
    if unexpectedXsds:
        modelXbrl.error(
            "FERC.22.00",
            _("The instance document contained unexpected schema references %(schemaReferences)s."
              ),
            modelXbrl=modelXbrl,
            schemaReferences=", ".join(sorted(unexpectedXsds)))

    if contextIDs:  # check if contextID is on any undefined facts
        for undefinedFact in modelXbrl.undefinedFacts:
            contextIDs.discard(undefinedFact.get("contextRef"))
        if contextIDs:
            modelXbrl.error(
                "FERC.6.05.08",
                _("The instance document contained context(s) %(contextIDs)s that was(were) not used in any fact."
                  ),
                modelXbrl=modelXbrl,
                contextIDs=", ".join(str(c) for c in contextIDs))
    if precisionFacts:
        modelXbrl.error(
            "FERC.6.05.17",
            _("The instance document contains elements using the precision attribute."
              ),
            modelObject=precisionFacts)

    #6.5.14 facts without english text
    for keyNotDefaultLang, factNotDefaultLang in keysNotDefaultLang.items():
        anyDefaultLangFact = False
        for fact in factsForLang[keyNotDefaultLang]:
            if fact.xmlLang.lower(
            ) == requiredFactLang:  #.startswith(factLangStartsWith):
                anyDefaultLangFact = True
                break
        if not anyDefaultLangFact:
            val.modelXbrl.error(
                "FERC.6.05.14",
                _("Element %(fact)s in context %(contextID)s has text with xml:lang other than '%(lang2)s' (%(lang)s) without matching English text.  "
                  ),
                modelObject=factNotDefaultLang,
                fact=factNotDefaultLang.qname,
                contextID=factNotDefaultLang.contextID,
                lang=factNotDefaultLang.xmlLang,
                lang2=disclosureSystem.defaultXmlLang
            )  # report lexical format default lang

    modelXbrl.profileActivity(_statusMsg, minTimeToShow=0.0)
    modelXbrl.modelManager.showStatus(None)