arelle-release 2.17.1__py3-none-any.whl → 2.37.71__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arelle/Aspect.py +6 -3
- arelle/BetaFeatures.py +3 -0
- arelle/Cntlr.py +117 -413
- arelle/CntlrCmdLine.py +364 -227
- arelle/CntlrQuickBooks.py +4 -2
- arelle/CntlrWebMain.py +179 -119
- arelle/CntlrWinMain.py +342 -124
- arelle/DialogAbout.py +1 -1
- arelle/DialogArcroleGroup.py +1 -1
- arelle/DialogFind.py +1 -1
- arelle/DialogFormulaParameters.py +2 -2
- arelle/DialogLanguage.py +45 -36
- arelle/DialogNewFactItem.py +1 -1
- arelle/DialogOpenArchive.py +30 -34
- arelle/DialogPackageManager.py +11 -8
- arelle/DialogPluginManager.py +43 -89
- arelle/DialogRssWatch.py +2 -2
- arelle/DialogURL.py +1 -1
- arelle/DialogUserPassword.py +1 -1
- arelle/DisclosureSystem.py +69 -1
- arelle/ErrorManager.py +321 -0
- arelle/FileSource.py +201 -99
- arelle/FunctionFn.py +44 -5
- arelle/FunctionIxt.py +16 -13
- arelle/FunctionUtil.py +1 -1
- arelle/FunctionXfi.py +68 -24
- arelle/FunctionXs.py +1 -1
- arelle/HtmlUtil.py +5 -4
- arelle/LeiUtil.py +63 -43
- arelle/LinkbaseType.py +94 -0
- arelle/LocalViewer.py +8 -2
- arelle/Locale.py +239 -79
- arelle/ModelDocument.py +180 -48
- arelle/ModelDtsObject.py +32 -27
- arelle/ModelFormulaObject.py +14 -6
- arelle/ModelInstanceObject.py +43 -14
- arelle/ModelManager.py +6 -1
- arelle/ModelObject.py +12 -8
- arelle/ModelObjectFactory.py +18 -2
- arelle/ModelRelationshipSet.py +46 -3
- arelle/ModelRenderingObject.py +899 -951
- arelle/ModelRssItem.py +2 -4
- arelle/ModelTestcaseObject.py +41 -4
- arelle/ModelValue.py +42 -14
- arelle/ModelVersReport.py +1 -1
- arelle/ModelXbrl.py +133 -273
- arelle/PackageManager.py +202 -176
- arelle/PluginManager.py +551 -239
- arelle/PluginUtils.py +54 -0
- arelle/PrototypeInstanceObject.py +53 -12
- arelle/PythonUtil.py +166 -25
- arelle/RuntimeOptions.py +32 -18
- arelle/SocketUtils.py +3 -0
- arelle/SystemInfo.py +1 -1
- arelle/TkTableWrapper.py +33 -5
- arelle/UITkTable.py +27 -21
- arelle/Updater.py +7 -3
- arelle/UrlUtil.py +6 -2
- arelle/Validate.py +596 -402
- arelle/ValidateDuplicateFacts.py +584 -0
- arelle/ValidateFileSource.py +38 -0
- arelle/ValidateFilingText.py +46 -29
- arelle/ValidateInfoset.py +104 -32
- arelle/ValidateUtr.py +31 -4
- arelle/ValidateXbrl.py +144 -120
- arelle/ValidateXbrlCalcs.py +51 -66
- arelle/ValidateXbrlDTS.py +98 -29
- arelle/Version.py +2 -7
- arelle/ViewFile.py +2 -0
- arelle/ViewFileDTS.py +7 -2
- arelle/ViewFileFactList.py +1 -1
- arelle/ViewFileFactTable.py +4 -4
- arelle/ViewFileRelationshipSet.py +36 -19
- arelle/ViewFileRenderedGrid.py +164 -753
- arelle/ViewFileRenderedLayout.py +174 -0
- arelle/ViewFileRenderedStructure.py +108 -0
- arelle/ViewWinDTS.py +4 -1
- arelle/ViewWinFactList.py +1 -1
- arelle/ViewWinFactTable.py +1 -1
- arelle/ViewWinRelationshipSet.py +14 -8
- arelle/ViewWinRenderedGrid.py +397 -287
- arelle/ViewWinRssFeed.py +4 -0
- arelle/ViewWinTree.py +15 -9
- arelle/ViewWinXml.py +1 -1
- arelle/WebCache.py +510 -272
- arelle/XbrlConst.py +202 -196
- arelle/XbrlUtil.py +2 -1
- arelle/XhtmlValidate.py +9 -23
- arelle/XmlUtil.py +24 -19
- arelle/XmlValidate.py +95 -67
- arelle/XmlValidateParticles.py +4 -4
- arelle/_version.py +33 -3
- arelle/api/Session.py +183 -0
- arelle/config/creationSoftwareNames.json +24 -10
- arelle/config/disclosuresystems.xml +1 -1
- arelle/config/disclosuresystems.xsd +3 -0
- arelle/config/sbr-text-formatting.xsd +737 -0
- arelle/conformance/CSVTestcaseLoader.py +102 -0
- arelle/formula/FactAspectsCache.py +10 -4
- arelle/formula/ValidateFormula.py +82 -194
- arelle/formula/XPathContext.py +42 -28
- arelle/formula/XPathParser.py +15 -9
- arelle/locale/ar_EG/LC_MESSAGES/ar_EG.po +3 -4
- arelle/locale/messages.pot +1 -2
- arelle/locale/ru/LC_MESSAGES/ru.po +2 -2
- arelle/logging/formatters/LogFormatter.py +47 -0
- arelle/logging/handlers/LogHandlerWithXml.py +94 -0
- arelle/logging/handlers/LogToBufferHandler.py +20 -0
- arelle/logging/handlers/LogToPrintHandler.py +41 -0
- arelle/logging/handlers/LogToXmlHandler.py +244 -0
- arelle/logging/handlers/StructuredMessageLogHandler.py +74 -0
- arelle/model/CommentBase.py +3 -0
- arelle/model/ElementBase.py +3 -0
- arelle/model/PIBase.py +3 -0
- arelle/model/__init__.py +3 -0
- arelle/oim/Load.py +2869 -0
- arelle/oim/Validate.py +155 -0
- arelle/oim/xml/Save.py +21 -0
- arelle/packages/PackageConst.py +7 -0
- arelle/packages/PackageType.py +13 -0
- arelle/packages/PackageUtils.py +22 -0
- arelle/packages/PackageValidation.py +200 -0
- arelle/packages/report/DetectReportPackage.py +13 -0
- arelle/packages/report/ReportPackage.py +265 -0
- arelle/packages/report/ReportPackageConst.py +79 -0
- arelle/packages/report/ReportPackageValidator.py +207 -0
- arelle/plugin/EdgarRendererAllReports.py +12 -12
- arelle/plugin/OimTaxonomy/ModelValueMore.py +15 -0
- arelle/plugin/OimTaxonomy/ValidateDTS.py +484 -0
- arelle/plugin/OimTaxonomy/ViewXbrlTxmyObj.py +239 -0
- arelle/plugin/OimTaxonomy/XbrlAbstract.py +16 -0
- arelle/plugin/OimTaxonomy/XbrlConcept.py +68 -0
- arelle/plugin/OimTaxonomy/XbrlConst.py +261 -0
- arelle/plugin/OimTaxonomy/XbrlCube.py +91 -0
- arelle/plugin/OimTaxonomy/XbrlDimension.py +38 -0
- arelle/plugin/OimTaxonomy/XbrlDts.py +152 -0
- arelle/plugin/OimTaxonomy/XbrlEntity.py +16 -0
- arelle/plugin/OimTaxonomy/XbrlGroup.py +22 -0
- arelle/plugin/OimTaxonomy/XbrlImportedTaxonomy.py +22 -0
- arelle/plugin/OimTaxonomy/XbrlLabel.py +31 -0
- arelle/plugin/OimTaxonomy/XbrlNetwork.py +100 -0
- arelle/plugin/OimTaxonomy/XbrlProperty.py +28 -0
- arelle/plugin/OimTaxonomy/XbrlReference.py +33 -0
- arelle/plugin/OimTaxonomy/XbrlReport.py +24 -0
- arelle/plugin/OimTaxonomy/XbrlTableTemplate.py +35 -0
- arelle/plugin/OimTaxonomy/XbrlTaxonomy.py +93 -0
- arelle/plugin/OimTaxonomy/XbrlTaxonomyObject.py +154 -0
- arelle/plugin/OimTaxonomy/XbrlTransform.py +17 -0
- arelle/plugin/OimTaxonomy/XbrlTypes.py +23 -0
- arelle/plugin/OimTaxonomy/XbrlUnit.py +17 -0
- arelle/plugin/OimTaxonomy/__init__.py +1037 -0
- arelle/plugin/OimTaxonomy/resources/iso4217.json +4479 -0
- arelle/plugin/OimTaxonomy/resources/oim-taxonomy-schema.json +935 -0
- arelle/plugin/OimTaxonomy/resources/ref.json +333 -0
- arelle/plugin/OimTaxonomy/resources/transform-types.json +2481 -0
- arelle/plugin/OimTaxonomy/resources/types.json +727 -0
- arelle/plugin/OimTaxonomy/resources/utr.json +3046 -0
- arelle/plugin/OimTaxonomy/resources/xbrlSpec.json +1082 -0
- arelle/plugin/formulaLoader.py +3 -3
- arelle/plugin/formulaSaver.py +4 -4
- arelle/plugin/inlineXbrlDocumentSet.py +331 -102
- arelle/plugin/internet/proxyNTLM/HTTPNtlmAuthHandler.py +1 -1
- arelle/plugin/loadFromExcel.py +13 -10
- arelle/plugin/loadFromOIM.py +16 -3096
- arelle/plugin/logging/saveMessages.py +1 -1
- arelle/plugin/saveHtmlEBAtables.py +293 -213
- arelle/plugin/saveLoadableOIM.py +652 -361
- arelle/plugin/saveOIMTaxonomy.py +311 -0
- arelle/plugin/streamingExtensions.py +1 -1
- arelle/plugin/systemInfo.py +46 -0
- arelle/plugin/transforms/tester.py +4 -4
- arelle/plugin/validate/CIPC/Const.py +18 -0
- arelle/plugin/validate/CIPC/__init__.py +34 -13
- arelle/plugin/validate/DBA/DisclosureSystems.py +12 -0
- arelle/plugin/validate/DBA/PluginValidationDataExtension.py +165 -0
- arelle/plugin/validate/DBA/ValidationPluginExtension.py +545 -0
- arelle/plugin/validate/DBA/__init__.py +49 -0
- arelle/plugin/validate/DBA/resources/config.xml +21 -0
- arelle/plugin/validate/DBA/rules/__init__.py +323 -0
- arelle/plugin/validate/DBA/rules/fr.py +1377 -0
- arelle/plugin/validate/DBA/rules/tc.py +45 -0
- arelle/plugin/validate/DBA/rules/th.py +172 -0
- arelle/plugin/validate/DBA/rules/tm.py +351 -0
- arelle/plugin/validate/DBA/rules/tr.py +373 -0
- arelle/plugin/validate/EBA/__init__.py +11 -12
- arelle/plugin/validate/EDINET/Constants.py +208 -0
- arelle/plugin/validate/EDINET/ContextRequirement.py +58 -0
- arelle/plugin/validate/EDINET/ControllerPluginData.py +298 -0
- arelle/plugin/validate/EDINET/CoverItemRequirements.py +42 -0
- arelle/plugin/validate/EDINET/DeiRequirements.py +118 -0
- arelle/plugin/validate/EDINET/DisclosureSystems.py +1 -0
- arelle/plugin/validate/EDINET/FilingFormat.py +275 -0
- arelle/plugin/validate/EDINET/FormType.py +134 -0
- arelle/plugin/validate/EDINET/ManifestInstance.py +236 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +640 -0
- arelle/plugin/validate/EDINET/ReportFolderType.py +162 -0
- arelle/plugin/validate/EDINET/Statement.py +139 -0
- arelle/plugin/validate/EDINET/TableOfContentsBuilder.py +493 -0
- arelle/plugin/validate/EDINET/UploadContents.py +48 -0
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +64 -0
- arelle/plugin/validate/EDINET/__init__.py +109 -0
- arelle/plugin/validate/EDINET/resources/config.xml +22 -0
- arelle/plugin/validate/EDINET/resources/cover-item-requirements.json +793 -0
- arelle/plugin/validate/EDINET/resources/dei-requirements.csv +27 -0
- arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml +62 -0
- arelle/plugin/validate/EDINET/rules/contexts.py +547 -0
- arelle/plugin/validate/EDINET/rules/edinet.py +1991 -0
- arelle/plugin/validate/EDINET/rules/frta.py +301 -0
- arelle/plugin/validate/EDINET/rules/gfm.py +2394 -0
- arelle/plugin/validate/EDINET/rules/manifests.py +88 -0
- arelle/plugin/validate/EDINET/rules/upload.py +1370 -0
- arelle/plugin/validate/ESEF/Const.py +16 -75
- arelle/plugin/validate/ESEF/Dimensions.py +2 -2
- arelle/plugin/validate/ESEF/ESEF_2021/DTS.py +6 -1
- arelle/plugin/validate/ESEF/ESEF_2021/Image.py +7 -4
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +80 -57
- arelle/plugin/validate/ESEF/ESEF_Current/DTS.py +60 -22
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +272 -133
- arelle/plugin/validate/ESEF/Util.py +5 -19
- arelle/plugin/validate/ESEF/__init__.py +61 -46
- arelle/plugin/validate/ESEF/resources/authority-validations.json +120 -10
- arelle/plugin/validate/ESEF/resources/config.xml +44 -3
- arelle/plugin/validate/FERC/__init__.py +31 -18
- arelle/plugin/validate/FERC/config.xml +18 -18
- arelle/plugin/validate/NL/DisclosureSystems.py +19 -3
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +710 -7
- arelle/plugin/validate/NL/ValidationPluginExtension.py +117 -12
- arelle/plugin/validate/NL/__init__.py +21 -10
- arelle/plugin/validate/NL/resources/config.xml +27 -6
- arelle/plugin/validate/NL/rules/br_kvk.py +81 -21
- arelle/plugin/validate/NL/rules/fg_nl.py +292 -0
- arelle/plugin/validate/NL/rules/fr_kvk.py +60 -7
- arelle/plugin/validate/NL/rules/fr_nl.py +705 -24
- arelle/plugin/validate/NL/rules/nl_kvk.py +2182 -0
- arelle/plugin/validate/ROS/DisclosureSystems.py +1 -0
- arelle/plugin/validate/ROS/PluginValidationDataExtension.py +109 -0
- arelle/plugin/validate/ROS/ValidationPluginExtension.py +27 -0
- arelle/plugin/validate/ROS/__init__.py +28 -341
- arelle/plugin/validate/ROS/resources/config.xml +20 -0
- arelle/plugin/validate/ROS/rules/__init__.py +56 -0
- arelle/plugin/validate/ROS/rules/ros.py +393 -0
- arelle/plugin/validate/UK/ValidateUK.py +1372 -0
- arelle/plugin/validate/{HMRC → UK}/__init__.py +195 -189
- arelle/plugin/validate/{HMRC → UK}/config.xml +1 -1
- arelle/plugin/xbrlDB/SqlDb.py +1 -1
- arelle/plugin/xbrlDB/XbrlOpenSqlDB.py +5 -5
- arelle/plugin/xbrlDB/XbrlPublicPostgresDB.py +1 -1
- arelle/plugin/xbrlDB/XbrlSemanticJsonDB.py +1 -1
- arelle/plugin/xbrlDB/XbrlSemanticRdfDB.py +1 -1
- arelle/plugin/xbrlDB/__init__.py +4 -1
- arelle/{RenderingEvaluator.py → rendering/RenderingEvaluator.py} +120 -99
- arelle/rendering/RenderingLayout.py +476 -0
- arelle/rendering/RenderingResolution.py +814 -0
- arelle/resources/cache/http/www.eurofiling.info/eu/fr/xbrl/ext/filing-indicators.xsd +20 -0
- arelle/resources/cache/http/www.w3.org/2001/03/xml.xsd +117 -0
- arelle/resources/cache/http/www.w3.org/2001/XMLSchema.xsd +2534 -0
- arelle/resources/cache/http/www.w3.org/2001/xml.xsd +287 -0
- arelle/resources/cache/http/www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd +779 -0
- arelle/resources/cache/http/www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd +486 -0
- arelle/resources/cache/http/www.xbrl.org/2003/xl-2003-12-31.xsd +251 -0
- arelle/resources/cache/http/www.xbrl.org/2003/xlink-2003-12-31.xsd +121 -0
- arelle/resources/cache/http/www.xbrl.org/2004/ref-2004-08-10.xsd +129 -0
- arelle/resources/cache/http/www.xbrl.org/2005/xbrldt-2005.xsd +53 -0
- arelle/resources/cache/http/www.xbrl.org/2006/ref-2006-02-27.xsd +120 -0
- arelle/resources/cache/http/www.xbrl.org/2006/xbrldi-2006.xsd +41 -0
- arelle/resources/cache/http/www.xbrl.org/2008/boolean-filter.xsd +51 -0
- arelle/resources/cache/http/www.xbrl.org/2008/concept-filter.xsd +127 -0
- arelle/resources/cache/http/www.xbrl.org/2008/conformance.xsd +116 -0
- arelle/resources/cache/http/www.xbrl.org/2008/conformanceFunction.xsd +51 -0
- arelle/resources/cache/http/www.xbrl.org/2008/consistency-assertion.xsd +66 -0
- arelle/resources/cache/http/www.xbrl.org/2008/dimension-filter.xsd +86 -0
- arelle/resources/cache/http/www.xbrl.org/2008/entity-filter.xsd +92 -0
- arelle/resources/cache/http/www.xbrl.org/2008/existence-assertion.xsd +44 -0
- arelle/resources/cache/http/www.xbrl.org/2008/formula.xsd +261 -0
- arelle/resources/cache/http/www.xbrl.org/2008/function.xsd +90 -0
- arelle/resources/cache/http/www.xbrl.org/2008/general-filter.xsd +38 -0
- arelle/resources/cache/http/www.xbrl.org/2008/generic-label.xsd +80 -0
- arelle/resources/cache/http/www.xbrl.org/2008/generic-link.xsd +81 -0
- arelle/resources/cache/http/www.xbrl.org/2008/generic-reference.xsd +63 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xbrl-instance-2003-12-31-ixmod.xsd +788 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xbrl-linkbase-2003-12-31-ixmod.xsd +488 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xl-2003-12-31.xsd +248 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xlink-2003-12-31.xsd +117 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xframes-1.xsd +166 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-applet-1.xsd +66 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-attribs-1.xsd +72 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-base-1.xsd +36 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic-form-1.xsd +195 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic-table-1.xsd +169 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10-model-1.xsd +376 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10-module-redefines-1.xsd +61 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10-modules-1.xsd +259 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10.xsd +98 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-bdo-1.xsd +78 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-blkphras-1.xsd +161 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-blkpres-1.xsd +37 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-blkstruct-1.xsd +49 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-charent-1.xsd +38 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-copyright-1.xsd +29 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-csismap-1.xsd +96 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-datatypes-1.xsd +128 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-edit-1.xsd +39 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-events-1.xsd +130 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-form-1.xsd +326 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-frames-1.xsd +113 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-framework-1.xsd +66 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-hypertext-1.xsd +47 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-iframe-1.xsd +68 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-image-1.xsd +45 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlphras-1.xsd +163 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlpres-1.xsd +39 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlstruct-1.xsd +50 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlstyle-1.xsd +27 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-legacy-1.xsd +97 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-link-1.xsd +45 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-list-1.xsd +99 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-meta-1.xsd +42 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-misc-1.xsd +441 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-nameident-1.xsd +63 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-notations-1.xsd +69 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-object-1.xsd +76 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-param-1.xsd +51 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-pres-1.xsd +51 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-ruby-1.xsd +171 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-ruby-basic-1.xsd +89 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-script-1.xsd +70 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-ssismap-1.xsd +43 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-struct-1.xsd +112 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-style-1.xsd +52 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-table-1.xsd +272 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-target-1.xsd +53 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-text-1.xsd +67 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11-model-1.xsd +677 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11-module-redefines-1.xsd +335 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11-modules-1.xsd +528 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11.xsd +104 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml2.xsd +21 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-1.xsd +73 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-2.xsd +74 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-attribs-1.xsd +73 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-attribs-2.xsd +75 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-copyright-1.xsd +34 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-copyright-2.xsd +34 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-handlers-2.xsd +98 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0-definitions.xsd +225 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0-model.xsd +681 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0-modules.xsd +535 -0
- arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0.xsd +40 -0
- arelle/resources/cache/http/www.xbrl.org/2008/match-filter.xsd +94 -0
- arelle/resources/cache/http/www.xbrl.org/2008/period-filter.xsd +86 -0
- arelle/resources/cache/http/www.xbrl.org/2008/registry.xsd +145 -0
- arelle/resources/cache/http/www.xbrl.org/2008/relative-filter.xsd +38 -0
- arelle/resources/cache/http/www.xbrl.org/2008/segment-scenario-filter.xsd +41 -0
- arelle/resources/cache/http/www.xbrl.org/2008/tuple-filter.xsd +83 -0
- arelle/resources/cache/http/www.xbrl.org/2008/unit-filter.xsd +58 -0
- arelle/resources/cache/http/www.xbrl.org/2008/validation.xsd +78 -0
- arelle/resources/cache/http/www.xbrl.org/2008/value-assertion.xsd +43 -0
- arelle/resources/cache/http/www.xbrl.org/2008/value-filter.xsd +43 -0
- arelle/resources/cache/http/www.xbrl.org/2008/variable.xsd +240 -0
- arelle/resources/cache/http/www.xbrl.org/2010/aspect-cover-filter.xsd +67 -0
- arelle/resources/cache/http/www.xbrl.org/2010/concept-relation-filter.xsd +108 -0
- arelle/resources/cache/http/www.xbrl.org/2010/custom-function-implementation.xsd +71 -0
- arelle/resources/cache/http/www.xbrl.org/2010/generic-message.xsd +68 -0
- arelle/resources/cache/http/www.xbrl.org/2010/validation-message.xsd +50 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xbrl-instance-2003-12-31-ixmod.xsd +788 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xbrl-linkbase-2003-12-31-ixmod.xsd +488 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xl-2003-12-31.xsd +248 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xlink-2003-12-31.xsd +117 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xframes-1.xsd +166 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-applet-1.xsd +66 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-attribs-1.xsd +72 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-base-1.xsd +36 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic-form-1.xsd +195 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic-table-1.xsd +169 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10-model-1.xsd +376 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10-module-redefines-1.xsd +61 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10-modules-1.xsd +259 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10.xsd +98 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-bdo-1.xsd +78 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-blkphras-1.xsd +161 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-blkpres-1.xsd +37 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-blkstruct-1.xsd +49 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-charent-1.xsd +38 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-copyright-1.xsd +29 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-csismap-1.xsd +96 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-datatypes-1.xsd +128 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-edit-1.xsd +39 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-events-1.xsd +130 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-form-1.xsd +326 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-frames-1.xsd +113 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-framework-1.xsd +66 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-hypertext-1.xsd +47 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-iframe-1.xsd +68 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-image-1.xsd +45 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlphras-1.xsd +163 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlpres-1.xsd +39 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlstruct-1.xsd +50 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlstyle-1.xsd +27 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-legacy-1.xsd +97 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-link-1.xsd +45 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-list-1.xsd +99 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-meta-1.xsd +42 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-misc-1.xsd +441 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-nameident-1.xsd +63 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-notations-1.xsd +69 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-object-1.xsd +76 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-param-1.xsd +51 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-pres-1.xsd +51 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-ruby-1.xsd +171 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-ruby-basic-1.xsd +89 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-script-1.xsd +70 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-ssismap-1.xsd +43 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-struct-1.xsd +112 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-style-1.xsd +52 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-table-1.xsd +272 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-target-1.xsd +53 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-text-1.xsd +67 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11-model-1.xsd +677 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11-module-redefines-1.xsd +335 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11-modules-1.xsd +528 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11.xsd +104 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml2.xsd +21 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-1.xsd +73 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-2.xsd +74 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-attribs-1.xsd +73 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-attribs-2.xsd +75 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-copyright-1.xsd +34 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-copyright-2.xsd +34 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-handlers-2.xsd +98 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1-definitions.xsd +252 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1-model.xsd +681 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1-modules.xsd +535 -0
- arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1.xsd +40 -0
- arelle/resources/cache/http/www.xbrl.org/2013/match-filter.xsd +94 -0
- arelle/resources/cache/http/www.xbrl.org/2013/preferred-label.xsd +12 -0
- arelle/resources/cache/http/www.xbrl.org/2013/versioning-base.xsd +142 -0
- arelle/resources/cache/http/www.xbrl.org/2013/versioning-concept-details.xsd +143 -0
- arelle/resources/cache/http/www.xbrl.org/2013/versioning-concept-use.xsd +64 -0
- arelle/resources/cache/http/www.xbrl.org/2013/versioning-dimensions.xsd +162 -0
- arelle/resources/cache/http/www.xbrl.org/2014/extensible-enumerations.xsd +27 -0
- arelle/resources/cache/http/www.xbrl.org/2014/table.xsd +356 -0
- arelle/resources/cache/http/www.xbrl.org/2014/tablemodel.xsd +156 -0
- arelle/resources/cache/http/www.xbrl.org/2016/assertion-severity.xsd +29 -0
- arelle/resources/cache/http/www.xbrl.org/2016/severities.xml +21 -0
- arelle/resources/cache/http/www.xbrl.org/2016/taxonomy-package-catalog.xsd +38 -0
- arelle/resources/cache/http/www.xbrl.org/2016/taxonomy-package.xsd +154 -0
- arelle/resources/cache/http/www.xbrl.org/2022/assertion-severity.xsd +43 -0
- arelle/resources/cache/http/www.xbrl.org/2022/severities.xml +21 -0
- arelle/resources/cache/http/www.xbrl.org/dtr/type/nonNumeric-2009-12-16.xsd +76 -0
- arelle/resources/cache/http/www.xbrl.org/dtr/type/numeric-2009-12-16.xsd +78 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/accounting-arcrole-2023-01-04.xsd +39 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/deprecated-2009-12-16.xsd +29 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/esma-arcrole-2018-11-21.xsd +14 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/factExplanatory-2009-12-16.xsd +13 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/jpfr-arcrole-2007-11-07.xsd +27 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/parent-child-2013-09-19.xsd +12 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/deprecated-2009-12-16.xsd +17 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/jpfr-role-2007-11-07.xsd +47 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/negated-2008-03-31.xsd +30 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/negated-2009-12-16.xsd +33 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/negative-2009-12-16.xsd +25 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/net-2009-12-16.xsd +13 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/positive-2009-12-16.xsd +25 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/property-2022-09-28.xsd +15 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/reference-2009-12-16.xsd +21 -0
- arelle/resources/cache/http/www.xbrl.org/lrr/role/restated-2006-02-21.xsd +19 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2012-01-30/utr.xml +4543 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2012-10-31/utr.xml +4510 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2012-11-30/utr.xml +4567 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2013-02-28/utr.xml +5180 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2013-05-17/utr.xml +5349 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2016-08-10/utr.xml +5363 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2017-07-12/utr.xml +6164 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2021-02-16/utr.xml +6370 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2021-12-08/utr.xml +6493 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2022-02-16/utr.xml +6551 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2022-07-20/utr.xml +6638 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2023-12-20/utr.xml +6680 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2024-01-31/utr.xml +6680 -0
- arelle/resources/cache/http/www.xbrl.org/utr/2024-10-22/utr.xml +6693 -0
- arelle/resources/cache/http/www.xbrl.org/utr/utr.xml +6693 -0
- arelle/resources/cache/https/www.xbrl.org/2005/conformance.xsd +87 -0
- arelle/resources/cache/https/www.xbrl.org/2020/extensible-enumerations-2.0.xsd +51 -0
- arelle/resources/cache/https/www.xbrl.org/2023/calculation-1.1.xsd +27 -0
- arelle/resources/cache/https/www.xbrl.org/dtr/type/2020-01-21/types.xsd +812 -0
- arelle/resources/cache/https/www.xbrl.org/dtr/type/2022-03-31/types.xsd +847 -0
- arelle/resources/cache/https/www.xbrl.org/dtr/type/2024-01-31/types.xsd +897 -0
- arelle/resources/cache/https/www.xbrl.org/taxonomy/int/filing-indicators/REC/2021-02-03/filing-indicators-def.xml +68 -0
- arelle/resources/cache/https/www.xbrl.org/taxonomy/int/filing-indicators/REC/2021-02-03/filing-indicators-label.xml +84 -0
- arelle/resources/cache/https/www.xbrl.org/taxonomy/int/filing-indicators/REC/2021-02-03/filing-indicators.xsd +67 -0
- arelle/resources/libs/Tktable2.11/linux-x86_64/README.txt +149 -0
- arelle/resources/libs/Tktable2.11/linux-x86_64/html/tkTable.html +2039 -0
- arelle/resources/libs/Tktable2.11/linux-x86_64/libTktable2.11.so +0 -0
- arelle/resources/libs/Tktable2.11/linux-x86_64/license.txt +51 -0
- arelle/resources/libs/Tktable2.11/linux-x86_64/pkgIndex.tcl +3 -0
- arelle/resources/libs/Tktable2.11/linux-x86_64/tkTable.tcl +825 -0
- arelle/resources/libs/Tktable2.11/linux-x86_64/tktable.py +674 -0
- arelle/resources/libs/Tktable2.11/macos-arm64/README.txt +149 -0
- arelle/resources/libs/Tktable2.11/macos-arm64/html/tkTable.html +2039 -0
- arelle/resources/libs/Tktable2.11/macos-arm64/libTktable2.11.dylib +0 -0
- arelle/resources/libs/Tktable2.11/macos-arm64/license.txt +51 -0
- arelle/resources/libs/Tktable2.11/macos-arm64/pkgIndex.tcl +2 -0
- arelle/resources/libs/Tktable2.11/macos-arm64/tkTable.tcl +825 -0
- arelle/resources/libs/Tktable2.11/macos-arm64/tktable.py +674 -0
- arelle/resources/libs/Tktable2.11/macos-x86_64/README.txt +149 -0
- arelle/resources/libs/Tktable2.11/macos-x86_64/html/tkTable.html +2039 -0
- arelle/resources/libs/Tktable2.11/macos-x86_64/libTktable2.11.dylib +0 -0
- arelle/resources/libs/Tktable2.11/macos-x86_64/license.txt +51 -0
- arelle/resources/libs/Tktable2.11/macos-x86_64/pkgIndex.tcl +2 -0
- arelle/resources/libs/Tktable2.11/macos-x86_64/tkTable.tcl +825 -0
- arelle/resources/libs/Tktable2.11/macos-x86_64/tktable.py +674 -0
- arelle/resources/libs/Tktable2.11/win-x86_64/Tktable.dll +0 -0
- arelle/resources/libs/Tktable2.11/win-x86_64/pkgIndex.tcl +2 -0
- arelle/resources/libs/Tktable2.11/win-x86_64/tkTable.tcl +825 -0
- arelle/typing.py +9 -4
- arelle/utils/Contexts.py +38 -0
- arelle/utils/EntryPointDetection.py +139 -0
- arelle/utils/Equivalence.py +22 -0
- arelle/utils/{validate/PluginValidationData.py → PluginData.py} +5 -2
- arelle/utils/PluginHooks.py +256 -5
- arelle/utils/Units.py +36 -0
- arelle/utils/validate/Decorator.py +5 -3
- arelle/utils/validate/DetectScriptsInXhtml.py +99 -0
- arelle/utils/validate/ESEFImage.py +283 -0
- arelle/utils/validate/Validation.py +9 -1
- arelle/utils/validate/ValidationPlugin.py +74 -11
- arelle/utils/validate/ValidationUtil.py +55 -0
- arelle/webserver/bottle.py +5 -4424
- {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info}/METADATA +51 -42
- arelle_release-2.37.71.dist-info/RECORD +697 -0
- {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info}/WHEEL +1 -1
- {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info/licenses}/LICENSE.md +1 -4
- {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info}/top_level.txt +0 -1
- arelle/DialogOpenTaxonomyPackage.py +0 -1
- arelle/RenderingResolver.py +0 -624
- arelle/examples/.pydevproject +0 -5
- arelle/examples/CustomLogger.py +0 -43
- arelle/examples/LoadEFMvalidate.py +0 -32
- arelle/examples/LoadSavePreLbCsv.py +0 -26
- arelle/examples/LoadValidate.cs +0 -31
- arelle/examples/LoadValidate.py +0 -36
- arelle/examples/LoadValidateCmdLine.java +0 -69
- arelle/examples/LoadValidatePostedZip.java +0 -57
- arelle/examples/LoadValidateWebService.java +0 -34
- arelle/examples/SaveTableToExelle.py +0 -140
- arelle/examples/TR3toTR4.py +0 -88
- arelle/examples/plugin/bigInstance.py +0 -394
- arelle/examples/plugin/cmdWebServerExtension.py +0 -42
- arelle/examples/plugin/crashTest.py +0 -38
- arelle/examples/plugin/formulaSuiteConverter.py +0 -212
- arelle/examples/plugin/functionsCustom.py +0 -59
- arelle/examples/plugin/hello_dolly.py +0 -64
- arelle/examples/plugin/hello_i18n.pot +0 -26
- arelle/examples/plugin/hello_i18n.py +0 -32
- arelle/examples/plugin/importTestChild1.py +0 -21
- arelle/examples/plugin/importTestChild2.py +0 -22
- arelle/examples/plugin/importTestGrandchild1.py +0 -21
- arelle/examples/plugin/importTestGrandchild2.py +0 -21
- arelle/examples/plugin/importTestImported1.py +0 -23
- arelle/examples/plugin/importTestImported11.py +0 -22
- arelle/examples/plugin/importTestParent.py +0 -48
- arelle/examples/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
- arelle/examples/plugin/packagedImportTest/__init__.py +0 -47
- arelle/examples/plugin/packagedImportTest/importTestChild1.py +0 -21
- arelle/examples/plugin/packagedImportTest/importTestChild2.py +0 -22
- arelle/examples/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
- arelle/examples/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
- arelle/examples/plugin/packagedImportTest/importTestImported1.py +0 -24
- arelle/examples/plugin/packagedImportTest/importTestImported11.py +0 -21
- arelle/examples/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
- arelle/examples/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
- arelle/examples/plugin/sakaCalendar.py +0 -215
- arelle/examples/plugin/saveInstanceInfoset.py +0 -121
- arelle/examples/plugin/streamingExtensions.py +0 -335
- arelle/examples/plugin/testcaseCalc11ValidateSetup.py +0 -32
- arelle/examples/plugin/testcaseIxExpectedHtmlFixup.py +0 -45
- arelle/examples/plugin/updateTableLB.py +0 -242
- arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
- arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
- arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -49
- arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
- arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
- arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -109
- arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -58
- arelle/examples/plugin/validateSchemaLxml.py +0 -156
- arelle/examples/plugin/validateTableInfoset.py +0 -52
- arelle/examples/us-gaap-dei-docType-extraction-frm.xml +0 -90
- arelle/examples/us-gaap-dei-ratio-cash-frm.xml +0 -150
- arelle/plugin/functionsXmlCreation.py +0 -106
- arelle/plugin/instanceInfo.py +0 -306
- arelle/plugin/loadFromOIM-2018.py +0 -1282
- arelle/plugin/objectmaker.py +0 -285
- arelle/plugin/sphinx/FormulaGenerator.py +0 -823
- arelle/plugin/sphinx/SphinxContext.py +0 -404
- arelle/plugin/sphinx/SphinxEvaluator.py +0 -783
- arelle/plugin/sphinx/SphinxMethods.py +0 -1287
- arelle/plugin/sphinx/SphinxParser.py +0 -1093
- arelle/plugin/sphinx/SphinxValidator.py +0 -163
- arelle/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
- arelle/plugin/sphinx/__init__.py +0 -285
- arelle/plugin/transforms/SEC/__init__.py +0 -308
- arelle/plugin/transforms/SEC/conf/README.md +0 -39
- arelle/plugin/transforms/SEC/conf/extractTestcase.sh +0 -2
- arelle/plugin/transforms/SEC/conf/extractTestcase.xsl +0 -109
- arelle/plugin/transforms/SEC/conf/runIxtSecTests.sh +0 -16
- arelle/plugin/transforms/SEC/conf/saxon9.jar +0 -0
- arelle/plugin/transforms/SEC/conf/testcase.xml +0 -7117
- arelle/plugin/transforms/SEC/conf/tests.xml +0 -848
- arelle/plugin/transforms/SEC/text2num.py +0 -110
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-boolballotbox.xml +0 -66
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-countrynameen.xml +0 -65
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-datequarterend.xml +0 -66
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durday.xml +0 -61
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durhour.xml +0 -69
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durmonth.xml +0 -71
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durweek.xml +0 -70
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durwordsen.xml +0 -56
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-duryear.xml +0 -64
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-edgarprovcountryen.xml +0 -65
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-entityfilercategoryen.xml +0 -63
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-exchnameen.xml +0 -86
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-numwordsen.xml +0 -55
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-stateprovnameen.xml +0 -64
- arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-yesnoballotbox.xml +0 -66
- arelle/plugin/transforms/SEC/transformationRegistry/registry/transform-registry.xml +0 -314
- arelle/plugin/transforms/SEC/transformationRegistry/schema/inlinexbrl-sec-transformation.xsd +0 -418
- arelle/plugin/validate/EFM/Consts.py +0 -362
- arelle/plugin/validate/EFM/DTS.py +0 -569
- arelle/plugin/validate/EFM/Dimensions.py +0 -264
- arelle/plugin/validate/EFM/Document.py +0 -206
- arelle/plugin/validate/EFM/Filing.py +0 -4383
- arelle/plugin/validate/EFM/IsolateSeparateIXDSes.py +0 -19
- arelle/plugin/validate/EFM/MessageNumericId.py +0 -142
- arelle/plugin/validate/EFM/PreCalAlignment.py +0 -324
- arelle/plugin/validate/EFM/Util.py +0 -688
- arelle/plugin/validate/EFM/__init__.py +0 -843
- arelle/plugin/validate/EFM/config.xml +0 -333
- arelle/plugin/validate/EFM/resources/README.md +0 -57
- arelle/plugin/validate/EFM/resources/axiswarnings.json +0 -27
- arelle/plugin/validate/EFM/resources/cef-deprecated-concepts.json +0 -8
- arelle/plugin/validate/EFM/resources/country-deprecated-concepts.json +0 -19
- arelle/plugin/validate/EFM/resources/currency-deprecated-concepts.json +0 -7
- arelle/plugin/validate/EFM/resources/dei-deprecated-concepts.json +0 -8
- arelle/plugin/validate/EFM/resources/dei-validations.json +0 -2884
- arelle/plugin/validate/EFM/resources/dqc-us-rules.json +0 -1389
- arelle/plugin/validate/EFM/resources/ecd-deprecated-concepts.json +0 -1
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-16-4.xml +0 -115
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-0-4.xml +0 -300
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-1.xml +0 -304
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-2.xml +0 -290
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-3-1.xml +0 -294
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-18.1.xml +0 -549
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-18.2.xml +0 -506
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-18.3.xml +0 -496
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-19-1.xml +0 -662
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-19-2.xml +0 -3761
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-19-3.xml +0 -3577
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-20-1.xml +0 -4020
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-20-2.xml +0 -3320
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-20-3.xml +0 -2998
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2012.xml +0 -957
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2013.xml +0 -982
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2014.xml +0 -1001
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2015.xml +0 -1076
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2016.xml +0 -120
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-1.xml +0 -3329
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-2.xml +0 -1230
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-3.xml +0 -1113
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-4.xml +0 -1321
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-1-preview.xml +0 -1721
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-1.xml +0 -1841
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-2-2.xml +0 -1450
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-2.xml +0 -1429
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-4.xml +0 -1527
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-1-1.xml +0 -2049
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-1.xml +0 -2065
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-2.xml +0 -1674
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-3.xml +0 -1715
- arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-all-years.xml +0 -10671
- arelle/plugin/validate/EFM/resources/edgartaxonomies/erxl.xsd +0 -69
- arelle/plugin/validate/EFM/resources/edgartaxonomies/extendedtaxonomies-all-years.xml +0 -18
- arelle/plugin/validate/EFM/resources/edgartaxonomies/ifrs-taxonomies.xml +0 -3126
- arelle/plugin/validate/EFM/resources/edgartaxonomies/ifrstaxonomies-all-years.xml +0 -22
- arelle/plugin/validate/EFM/resources/ex26-validations.json +0 -255
- arelle/plugin/validate/EFM/resources/exch-deprecated-concepts.json +0 -163
- arelle/plugin/validate/EFM/resources/ifrs-full-deprecated-concepts.json +0 -204
- arelle/plugin/validate/EFM/resources/invest-deprecated-concepts.json +0 -99
- arelle/plugin/validate/EFM/resources/ixbrl-transform-registries.json +0 -16
- arelle/plugin/validate/EFM/resources/naics-deprecated-concepts.json +0 -295
- arelle/plugin/validate/EFM/resources/oef-deprecated-concepts.json +0 -59
- arelle/plugin/validate/EFM/resources/other-standard-taxonomies.json +0 -12
- arelle/plugin/validate/EFM/resources/rr-deprecated-concepts.json +0 -14
- arelle/plugin/validate/EFM/resources/rxp-deprecated-concepts.json +0 -1
- arelle/plugin/validate/EFM/resources/shr-deprecated-concepts.json +0 -1
- arelle/plugin/validate/EFM/resources/sic-deprecated-concepts.json +0 -1
- arelle/plugin/validate/EFM/resources/signwarnings.json +0 -112
- arelle/plugin/validate/EFM/resources/srt-deprecated-concepts.json +0 -1
- arelle/plugin/validate/EFM/resources/stpr-deprecated-concepts.json +0 -1
- arelle/plugin/validate/EFM/resources/taxonomy-compatibility.json +0 -79
- arelle/plugin/validate/EFM/resources/us-gaap-deprecated-concepts.json +0 -2886
- arelle/plugin/validate/EFM/resources/us-gaap-rels-2020.json +0 -29068
- arelle/plugin/validate/EFM/resources/us-gaap-rels-2021.json +0 -29598
- arelle/plugin/validate/EFM/resources/us-gaap-rels-2022.json +0 -29141
- arelle/plugin/validate/EFM/resources/us-gaap-rels-2023.json +0 -29400
- arelle/plugin/validate/EFM/resources/vip-deprecated-concepts.json +0 -1
- arelle/plugin/validate/EFM/tools/CheckTxmyRefs.py +0 -180
- arelle/plugin/validate/EFM-htm/Const.py +0 -205
- arelle/plugin/validate/EFM-htm/__init__.py +0 -217
- arelle/plugin/validate/EFM-htm/config.xml +0 -17
- arelle/plugin/validate/EFM-htm/resources/efm-htm.dtd +0 -664
- arelle/plugin/validate/ESEF/ESEF_Current/Image.py +0 -199
- arelle/plugin/validate/ESEF_2022/__init__.py +0 -47
- arelle/plugin/validate/GFM/__init__.py +0 -59
- arelle/plugin/validate/GFM/config.xml +0 -82
- arelle/plugin/validate/SBRnl/CustomLoader.py +0 -19
- arelle/plugin/validate/SBRnl/DTS.py +0 -305
- arelle/plugin/validate/SBRnl/Dimensions.py +0 -357
- arelle/plugin/validate/SBRnl/Document.py +0 -799
- arelle/plugin/validate/SBRnl/Filing.py +0 -467
- arelle/plugin/validate/SBRnl/__init__.py +0 -75
- arelle/plugin/validate/SBRnl/config.xml +0 -26
- arelle/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
- arelle/plugin/validate/USBestPractices.py +0 -570
- arelle/plugin/validate/USCorpAction.py +0 -557
- arelle/plugin/validate/USSecTagging.py +0 -337
- arelle/plugin/validate/XDC/__init__.py +0 -77
- arelle/plugin/validate/XDC/config.xml +0 -20
- arelle/plugin/validate/XFsyntax/__init__.py +0 -64
- arelle/plugin/validate/XFsyntax/xf.py +0 -2227
- arelle/plugin/validate/__init__.py +0 -20
- arelle/plugin/validate/calc2.py +0 -536
- arelle/plugin/validateSBRnl.py +0 -530
- arelle/scripts-macOS/startWebServer.command +0 -3
- arelle/scripts-unix/startWebServer.sh +0 -1
- arelle/scripts-windows/startWebServer.bat +0 -5
- arelle_release-2.17.1.dist-info/RECORD +0 -676
- tests/__init__.py +0 -0
- tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/ArelleGUITest.csproj +0 -30
- tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/Tests.cs +0 -381
- tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/Usings.cs +0 -1
- tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest.sln +0 -31
- tests/integration_tests/ui_tests/resources/workiva.zip +0 -0
- tests/integration_tests/validation/README.md +0 -50
- tests/integration_tests/validation/conformance_suite_config.py +0 -59
- tests/integration_tests/validation/conformance_suite_configs.py +0 -63
- tests/integration_tests/validation/conformance_suite_configurations/efm_current.py +0 -261
- tests/integration_tests/validation/conformance_suite_configurations/esef_ixbrl_2021.py +0 -85
- tests/integration_tests/validation/conformance_suite_configurations/esef_ixbrl_2022.py +0 -90
- tests/integration_tests/validation/conformance_suite_configurations/esef_xhtml_2021.py +0 -15
- tests/integration_tests/validation/conformance_suite_configurations/esef_xhtml_2022.py +0 -15
- tests/integration_tests/validation/conformance_suite_configurations/nl_nt16.py +0 -13
- tests/integration_tests/validation/conformance_suite_configurations/nl_nt17.py +0 -13
- tests/integration_tests/validation/conformance_suite_configurations/nl_nt18.py +0 -13
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_2_1.py +0 -34
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_calculations_1_1.py +0 -40
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_dimensions_1_0.py +0 -28
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_extensible_enumerations_1_0.py +0 -10
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_extensible_enumerations_2_0.py +0 -10
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_formula_1_0.py +0 -9
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_formula_1_0_assertion_severity_2_0.py +0 -9
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_formula_1_0_function_registry.py +0 -14
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_ixbrl_1_1.py +0 -22
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_link_role_registry_1_0.py +0 -11
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_oim_1_0.py +0 -19
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_table_linkbase_1_0.py +0 -296
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_taxonomy_packages_1_0.py +0 -13
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_transformation_registry_3.py +0 -13
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_transformation_registry_4.py +0 -13
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_transformation_registry_5.py +0 -13
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_utr_malformed_1_0.py +0 -31
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_utr_registry_1_0.py +0 -22
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_utr_structure_1_0.py +0 -18
- tests/integration_tests/validation/conftest.py +0 -24
- tests/integration_tests/validation/discover_tests.py +0 -22
- tests/integration_tests/validation/download_conformance_suites.py +0 -47
- tests/integration_tests/validation/run_conformance_suites.py +0 -175
- tests/integration_tests/validation/test_conformance_suites.py +0 -14
- tests/integration_tests/validation/validation_util.py +0 -271
- tests/resources/conformance_suites/nl_nt16/br_kvk/2-04-invalid-period.xbrl +0 -63
- tests/resources/conformance_suites/nl_nt16/br_kvk/2-04-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/br_kvk/3-01-missing.xbrl +0 -53
- tests/resources/conformance_suites/nl_nt16/br_kvk/3-01-multiple.xbrl +0 -60
- tests/resources/conformance_suites/nl_nt16/br_kvk/3-01-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-07-invalid.xbrl +0 -57
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-07-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-10-invalid.xbrl +0 -58
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-10-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-12-invalid.xbrl +0 -63
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-12-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-16-missing.xbrl +0 -61
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-16-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-20-invalid-date.xbrl +0 -89
- tests/resources/conformance_suites/nl_nt16/br_kvk/4-20-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_kvk/1-01-invalid-file-extension.xml +0 -56
- tests/resources/conformance_suites/nl_nt16/fr_kvk/1-01-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_kvk/2-01-invalid-lang.xbrl +0 -56
- tests/resources/conformance_suites/nl_nt16/fr_kvk/2-01-missing-lang.xbrl +0 -55
- tests/resources/conformance_suites/nl_nt16/fr_kvk/2-01-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt16/fr_kvk/2-03-entrypoint.xsd +0 -6
- tests/resources/conformance_suites/nl_nt16/fr_kvk/2-03-invalid-entrypoint.xbrl +0 -63
- tests/resources/conformance_suites/nl_nt16/fr_kvk/2-03-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_kvk/5-01-invalid-decimals.xbrl +0 -49
- tests/resources/conformance_suites/nl_nt16/fr_kvk/5-01-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_kvk/5-02-invalid-decimals.xbrl +0 -49
- tests/resources/conformance_suites/nl_nt16/fr_kvk/5-02-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-01-invalid-file.xbrl +0 -2
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-01-invalid-zip.zip +0 -0
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-01-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-03-invalid-doctype.xbrl +0 -4
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-03-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-05-invalid-encoding.xbrl +0 -0
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-05-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-06-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt16/fr_nl/1-06.invalid.xbrl +0 -2
- tests/resources/conformance_suites/nl_nt16/fr_nl/2-06-invalid-file.xbrl +0 -8
- tests/resources/conformance_suites/nl_nt16/fr_nl/2-06-invalid-zip.zip +0 -0
- tests/resources/conformance_suites/nl_nt16/fr_nl/2-06-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt16/index.xml +0 -20
- tests/resources/conformance_suites/nl_nt17/br_kvk/2-04-invalid-period.xbrl +0 -63
- tests/resources/conformance_suites/nl_nt17/br_kvk/2-04-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/br_kvk/3-01-missing.xbrl +0 -53
- tests/resources/conformance_suites/nl_nt17/br_kvk/3-01-multiple.xbrl +0 -60
- tests/resources/conformance_suites/nl_nt17/br_kvk/3-01-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-07-invalid.xbrl +0 -57
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-07-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-10-invalid.xbrl +0 -58
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-10-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-12-invalid.xbrl +0 -63
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-12-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-16-missing.xbrl +0 -61
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-16-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-20-invalid-date.xbrl +0 -89
- tests/resources/conformance_suites/nl_nt17/br_kvk/4-20-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_kvk/1-01-invalid-file-extension.xml +0 -56
- tests/resources/conformance_suites/nl_nt17/fr_kvk/1-01-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_kvk/2-01-invalid-lang.xbrl +0 -56
- tests/resources/conformance_suites/nl_nt17/fr_kvk/2-01-missing-lang.xbrl +0 -55
- tests/resources/conformance_suites/nl_nt17/fr_kvk/2-01-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt17/fr_kvk/2-03-entrypoint.xsd +0 -6
- tests/resources/conformance_suites/nl_nt17/fr_kvk/2-03-invalid-entrypoint.xbrl +0 -63
- tests/resources/conformance_suites/nl_nt17/fr_kvk/2-03-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_kvk/5-01-invalid-decimals.xbrl +0 -49
- tests/resources/conformance_suites/nl_nt17/fr_kvk/5-01-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_kvk/5-02-invalid-decimals.xbrl +0 -49
- tests/resources/conformance_suites/nl_nt17/fr_kvk/5-02-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-01-invalid-file.xbrl +0 -2
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-01-invalid-zip.zip +0 -0
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-01-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-03-invalid-doctype.xbrl +0 -4
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-03-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-05-invalid-encoding.xbrl +0 -0
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-05-testcases.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-06-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt17/fr_nl/1-06.invalid.xbrl +0 -2
- tests/resources/conformance_suites/nl_nt17/fr_nl/2-06-invalid-file.xbrl +0 -8
- tests/resources/conformance_suites/nl_nt17/fr_nl/2-06-invalid-zip.zip +0 -0
- tests/resources/conformance_suites/nl_nt17/fr_nl/2-06-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt17/index.xml +0 -20
- tests/resources/conformance_suites/nl_nt18/fr_nl/1-03-invalid-doctype.xbrl +0 -4
- tests/resources/conformance_suites/nl_nt18/fr_nl/1-03-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt18/fr_nl/1-05-invalid-encoding.xbrl +0 -0
- tests/resources/conformance_suites/nl_nt18/fr_nl/1-05-testcases.xml +0 -22
- tests/resources/conformance_suites/nl_nt18/fr_nl/1-06-testcase.xml +0 -22
- tests/resources/conformance_suites/nl_nt18/fr_nl/1-06.invalid.xbrl +0 -2
- tests/resources/conformance_suites/nl_nt18/fr_nl/1.01-invalid-file.xbrl +0 -2
- tests/resources/conformance_suites/nl_nt18/fr_nl/1.01-invalid-zip.zip +0 -0
- tests/resources/conformance_suites/nl_nt18/fr_nl/1.01-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt18/fr_nl/2-06-invalid-file.xbrl +0 -8
- tests/resources/conformance_suites/nl_nt18/fr_nl/2-06-invalid-zip.zip +0 -0
- tests/resources/conformance_suites/nl_nt18/fr_nl/2-06-testcase.xml +0 -33
- tests/resources/conformance_suites/nl_nt18/index.xml +0 -8
- tests/unit_tests/arelle/conftest.py +0 -16
- tests/unit_tests/arelle/formula/test_fact_aspects_cache.py +0 -170
- tests/unit_tests/arelle/plugin/test_loadfromoim.py +0 -40
- tests/unit_tests/arelle/plugin/test_plugin_imports.py +0 -27
- tests/unit_tests/arelle/test_betafeatures.py +0 -81
- tests/unit_tests/arelle/test_cntlr.py +0 -28
- tests/unit_tests/arelle/test_import.py +0 -40
- tests/unit_tests/arelle/test_locale.py +0 -73
- tests/unit_tests/arelle/test_modelmanager.py +0 -15
- tests/unit_tests/arelle/test_packagemanager.py +0 -65
- tests/unit_tests/arelle/test_pluginmanager.py +0 -176
- tests/unit_tests/arelle/test_qname.py +0 -140
- tests/unit_tests/arelle/test_runtimeoptions.py +0 -56
- tests/unit_tests/arelle/test_system_info.py +0 -26
- tests/unit_tests/arelle/test_updater.py +0 -507
- tests/unit_tests/arelle/test_urlutil.py +0 -49
- tests/unit_tests/arelle/test_version.py +0 -46
- tests/unit_tests/arelle/utils/validate/test_decorator.py +0 -59
- /arelle/{examples/plugin/validate/XYZ → plugin/validate/EDINET}/rules/__init__.py +0 -0
- /arelle/plugin/validate/{HMRC → UK}/consistencyChecksByName.json +0 -0
- /arelle/plugin/validate/{HMRC → UK}/hmrc-taxonomies.xml +0 -0
- {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info}/entry_points.txt +0 -0
|
@@ -1,4383 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
'''
|
|
3
|
-
This is a collective work.
|
|
4
|
-
See COPYRIGHT.md for copyright information for original work.
|
|
5
|
-
Subsequent validations and enhancements created by staff of the U.S. Securities and Exchange Commission.
|
|
6
|
-
Data and content created by government employees within the scope of their employment are not subject
|
|
7
|
-
to domestic copyright protection. 17 U.S.C. 105.
|
|
8
|
-
Implementation of DQC rules invokes https://xbrl.us/dqc-license and https://xbrl.us/dqc-patent
|
|
9
|
-
|
|
10
|
-
'''
|
|
11
|
-
import datetime, decimal, json, unicodedata, holidays, fnmatch
|
|
12
|
-
from decimal import Decimal, InvalidOperation
|
|
13
|
-
import regex as re
|
|
14
|
-
from math import isnan, pow, isinf
|
|
15
|
-
from collections import defaultdict, OrderedDict
|
|
16
|
-
from pytz import timezone
|
|
17
|
-
from arelle import (ModelDocument, ModelValue, ModelRelationshipSet,
|
|
18
|
-
XmlUtil, XbrlConst, ValidateFilingText)
|
|
19
|
-
from arelle.ModelValue import qname, QName, dateUnionEqual, DateTime, dateTime
|
|
20
|
-
from arelle.ValidateXbrlCalcs import insignificantDigits
|
|
21
|
-
from arelle.ModelObject import ModelObject
|
|
22
|
-
from arelle.ModelInstanceObject import ModelFact, ModelInlineFact, ModelInlineFootnote
|
|
23
|
-
from arelle.ModelDtsObject import ModelConcept, ModelResource
|
|
24
|
-
from arelle.ModelXbrl import NONDEFAULT
|
|
25
|
-
from arelle.PluginManager import pluginClassMethods
|
|
26
|
-
from arelle.PrototypeDtsObject import LinkPrototype, LocPrototype, ArcPrototype
|
|
27
|
-
from arelle.PythonUtil import pyNamedObject, strTruncate, normalizeSpace, lcStr, flattenSequence, flattenToSet, OrderedSet
|
|
28
|
-
from arelle.UrlUtil import isHttpUrl
|
|
29
|
-
from arelle.ValidateXbrlCalcs import inferredDecimals, rangeValue, roundValue, ONE
|
|
30
|
-
from arelle.XmlValidate import VALID
|
|
31
|
-
from .DTS import checkFilingDTS
|
|
32
|
-
from .Consts import submissionTypesAllowingSeriesClasses, \
|
|
33
|
-
submissionTypesRequiringOefClasses, invCompanyTypesRequiringOefClasses, \
|
|
34
|
-
submissionTypesExemptFromRoleOrder, docTypesExemptFromRoleOrder, \
|
|
35
|
-
docTypesRequiringPeriodOfReport, \
|
|
36
|
-
invCompanyTypesAllowingSeriesClasses, \
|
|
37
|
-
docTypesNotAllowingInlineXBRL, \
|
|
38
|
-
docTypesRequiringRrSchema, docTypesNotAllowingIfrs, \
|
|
39
|
-
untransformableTypes, rrUntransformableEltsPattern, \
|
|
40
|
-
hideableNamespacesPattern, linkbaseValidations, \
|
|
41
|
-
feeTaggingAttachmentDocumentTypePattern, docTypesAttachmentDocumentType, docTypesSubType
|
|
42
|
-
|
|
43
|
-
from .Dimensions import checkFilingDimensions
|
|
44
|
-
from .PreCalAlignment import checkCalcsTreeWalk
|
|
45
|
-
from .Util import conflictClassFromNamespace, abbreviatedNamespace, NOYEAR, WITHYEARandWILD, loadDeprecatedConceptDates, \
|
|
46
|
-
loadCustomAxesReplacements, loadNonNegativeFacts, loadDeiValidations, loadOtherStandardTaxonomies, \
|
|
47
|
-
loadUgtRelQnames, loadDqcRules, factBindings, leastDecimals, axisMemQnames, memChildQnames, \
|
|
48
|
-
loadTaxonomyCompatibility, loadIxTransformRegistries, ValueRange
|
|
49
|
-
|
|
50
|
-
MIN_DOC_PER_END_DATE = ModelValue.dateTime("1980-01-01", type=ModelValue.DATE)
|
|
51
|
-
MAX_DOC_PER_END_DATE = ModelValue.dateTime("2050-12-31", type=ModelValue.DATE)
|
|
52
|
-
ONE_DAY = datetime.timedelta(days=1)
|
|
53
|
-
EMPTY_DICT = {}
|
|
54
|
-
EMPTY_SET = set()
|
|
55
|
-
EMPTY_LIST = []
|
|
56
|
-
NONE_SET = {None}
|
|
57
|
-
|
|
58
|
-
nonQuotedStringPatterns = re.compile(r"\d{2}[/-]\d{2}[/-]\d{4}|\d{4}[/-]\d{2}[/-]\d{2}|true|false")
|
|
59
|
-
def sevMessageArgValue(x, pf=None): # pf is prototype Fact if any
|
|
60
|
-
if isinstance(x, (list,tuple)):
|
|
61
|
-
return ", ".join(sevMessageArgValue(v,pf) for v in x)
|
|
62
|
-
if isinstance(x, ModelFact):
|
|
63
|
-
return sevMessageArgValue(x.xValue, x)
|
|
64
|
-
elif x is None:
|
|
65
|
-
return "(none)"
|
|
66
|
-
elif isinstance(x, bool):
|
|
67
|
-
return ("false", "true")[x]
|
|
68
|
-
elif isinstance(x, Decimal): # add , separators and drop excessive fractional zeros
|
|
69
|
-
m = re.match("^([^.]*[.][0-9]{2})([0-9]*[1-9])*0+$", "{:,}".format(x))
|
|
70
|
-
if m:
|
|
71
|
-
x = m.group(1) + (m.group(2) or "")
|
|
72
|
-
else:
|
|
73
|
-
x = "{:,}".format(x)
|
|
74
|
-
if pf is not None and pf.concept.isMonetary: # dont provide shares and other unit symbols
|
|
75
|
-
x = pf.unitSymbol() + x
|
|
76
|
-
elif isinstance(x, (ModelValue.DateTime,ModelValue.DayTimeDuration,ModelValue.gMonthDay)):
|
|
77
|
-
pass # do not treat as string to add quote marks
|
|
78
|
-
elif isinstance(x, str): # may need to check pf for str type as well
|
|
79
|
-
if x.startswith("!do-not-quote!"):
|
|
80
|
-
x = x[14:]
|
|
81
|
-
elif not nonQuotedStringPatterns.match(x):
|
|
82
|
-
x = '"' + x + '"'
|
|
83
|
-
return str(x)
|
|
84
|
-
|
|
85
|
-
def logMsg(msg):
|
|
86
|
-
return re.sub(r"{(\w+)}", r"%(\1)s", msg.replace("%","%%")) # replace {...} args with %(...)s args for modelXbrl.log functionality
|
|
87
|
-
|
|
88
|
-
def allowableJsonCharsForEdgar(str):
|
|
89
|
-
# encode xml-legal ascii bytes not acceptable to EDGAR
|
|
90
|
-
return re.sub("[\\^\x7F]", lambda m: "\\u%04X" % ord(m[0]), str)
|
|
91
|
-
|
|
92
|
-
def validateFiling(val, modelXbrl, isEFM=False, isGFM=False):
|
|
93
|
-
if not modelXbrl.modelDocument or not hasattr(modelXbrl.modelDocument, "xmlDocument"): # not parsed
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
datePattern = re.compile(r"([12][0-9]{3})-([01][0-9])-([0-3][0-9])")
|
|
97
|
-
GFMcontextDatePattern = re.compile(r"^[12][0-9]{3}-[01][0-9]-[0-3][0-9]$")
|
|
98
|
-
# note \u20zc = euro, \u00a3 = pound, \u00a5 = yen
|
|
99
|
-
signOrCurrencyPattern = re.compile("^(-)[0-9]+|[^eE](-)[0-9]+|(\\()[0-9].*(\\))|([$\u20ac\u00a3\00a5])")
|
|
100
|
-
instanceFileNamePattern = re.compile(r"^(\w+)-([12][0-9]{3}[01][0-9][0-3][0-9]).xml$")
|
|
101
|
-
htmlFileNamePattern = re.compile(r"([a-zA-Z0-9][._a-zA-Z0-9-]*)\.htm$")
|
|
102
|
-
linkroleDefinitionStatementSheet = re.compile(r"[^-]+-\s+Statement\s+-\s+.*", # no restriction to type of statement
|
|
103
|
-
re.IGNORECASE)
|
|
104
|
-
efmCIKpattern = re.compile(r"^[0-9]{10}$")
|
|
105
|
-
instantPreferredLabelRolePattern = re.compile(r".*[pP]eriod(Start|End)")
|
|
106
|
-
embeddingCommandPattern = re.compile(r"[^~]*~\s*()[^~]*~")
|
|
107
|
-
styleIxHiddenPattern = re.compile(r"(.*[^\w]|^)-sec-ix-hidden\s*:\s*([\w.-]+).*")
|
|
108
|
-
efmRoleDefinitionPattern = re.compile(r"([0-9]+) - (Statement|Disclosure|Schedule|Document) - (.+)")
|
|
109
|
-
messageKeySectionPattern = re.compile(r"(.*[{]efmSection[}]|[a-z]{2}-[0-9]{4})(.*)")
|
|
110
|
-
secDomainPattern = re.compile(r"(fasb\.org|xbrl\.sec\.gov)")
|
|
111
|
-
|
|
112
|
-
val._isStandardUri = {}
|
|
113
|
-
modelXbrl.modelManager.disclosureSystem.loadStandardTaxonomiesDict()
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
datetimeNowAtSEC = ModelValue.dateTime(
|
|
117
|
-
val.params.get("datetimeForTesting",
|
|
118
|
-
datetime.datetime.now(tz=timezone("US/Eastern")).isoformat()[:19])) # re-strip time zone
|
|
119
|
-
dqcRuleFilter = re.compile(val.params.get("dqcRuleFilter",""))
|
|
120
|
-
upcomingSECHolidays = holidays.US(state=None, years=[datetimeNowAtSEC.year, datetimeNowAtSEC.year+1])
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
# note that some XFM tests are done by ValidateXbrl to prevent mulstiple node walks
|
|
124
|
-
disclosureSystem = val.disclosureSystem
|
|
125
|
-
val.disclosureSystemVersion = disclosureSystemVersion = disclosureSystem.version
|
|
126
|
-
|
|
127
|
-
modelXbrl.modelManager.showStatus(_("validating {0}").format(disclosureSystem.name))
|
|
128
|
-
|
|
129
|
-
val.modelXbrl.profileActivity()
|
|
130
|
-
conceptsUsed = {} # key=concept object value=True if has presentation label
|
|
131
|
-
labelsRelationshipSet = modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
132
|
-
# genLabelsRelationshipSet = modelXbrl.relationshipSet(XbrlConst.elementLabel)
|
|
133
|
-
# presentationRelationshipSet = modelXbrl.relationshipSet(XbrlConst.parentChild)
|
|
134
|
-
referencesRelationshipSetWithProhibits = modelXbrl.relationshipSet(XbrlConst.conceptReference, includeProhibits=True)
|
|
135
|
-
val.modelXbrl.profileActivity("... cache lbl, pre, ref relationships", minTimeToShow=1.0)
|
|
136
|
-
|
|
137
|
-
validateInlineXbrlGFM = (modelXbrl.modelDocument.type == ModelDocument.Type.INLINEXBRL and
|
|
138
|
-
isGFM)
|
|
139
|
-
validateEFMpragmatic = disclosureSystem.names and "efm-pragmatic" in disclosureSystem.names
|
|
140
|
-
val.validateLoggingSemantic = validateLoggingSemantic = (
|
|
141
|
-
modelXbrl.isLoggingEffectiveFor(level="WARNING-SEMANTIC") or
|
|
142
|
-
modelXbrl.isLoggingEffectiveFor(level="ERROR-SEMANTIC"))
|
|
143
|
-
|
|
144
|
-
if isEFM:
|
|
145
|
-
for pluginXbrlMethod in pluginClassMethods("Validate.EFM.Start"):
|
|
146
|
-
pluginXbrlMethod(val)
|
|
147
|
-
|
|
148
|
-
if "EFM/Filing.py#validateFiling_start" in val.modelXbrl.arelleUnitTests:
|
|
149
|
-
raise pyNamedObject(val.modelXbrl.arelleUnitTests["EFM/Filing.py#validateFiling_start"], "EFM/Filing.py#validateFiling_start")
|
|
150
|
-
|
|
151
|
-
# instance checks
|
|
152
|
-
val.fileNameBasePart = None # prevent testing on fileNameParts if not instance or invalid
|
|
153
|
-
val.fileNameDate = None
|
|
154
|
-
val.entityRegistrantName = None
|
|
155
|
-
val.requiredContext = None
|
|
156
|
-
deiDocumentType = None # needed for non-instance validation too
|
|
157
|
-
submissionType = val.params.get("submissionType", "")
|
|
158
|
-
attachmentDocumentType = val.params.get("attachmentDocumentType", "") # this is different from dei:documentType
|
|
159
|
-
isFeeTagging = feeTaggingAttachmentDocumentTypePattern.match(attachmentDocumentType)
|
|
160
|
-
requiredFactLang = disclosureSystem.defaultXmlLang.lower() if disclosureSystem.defaultXmlLang else disclosureSystem.defaultXmlLang
|
|
161
|
-
hasSubmissionType = bool(submissionType)
|
|
162
|
-
hasAttachmentDocumentType = bool(attachmentDocumentType)
|
|
163
|
-
dqcRules = {}
|
|
164
|
-
isInlineXbrl = modelXbrl.modelDocument.type in (ModelDocument.Type.INLINEXBRL, ModelDocument.Type.INLINEXBRLDOCUMENTSET)
|
|
165
|
-
isXbrlInstance = isInlineXbrl or modelXbrl.modelDocument.type == ModelDocument.Type.INSTANCE
|
|
166
|
-
isFtJson = any(pluginXbrlMethod(modelXbrl) for pluginXbrlMethod in pluginClassMethods("FtJson.IsFtJsonDocument"))
|
|
167
|
-
if isEFM:
|
|
168
|
-
if not attachmentDocumentType or not hasSubmissionType: # unspecified submission parameters (from cmd line or formula parameters dialog)
|
|
169
|
-
isFeeTagging = any(doc.targetNamespace.startswith("http://xbrl.sec.gov/ffd/") for doc in modelXbrl.urlDocs.values() if doc.targetNamespace)
|
|
170
|
-
if isFeeTagging:
|
|
171
|
-
if not attachmentDocumentType:
|
|
172
|
-
attachmentDocumentType = "EX-FILING FEES"
|
|
173
|
-
if not hasSubmissionType:
|
|
174
|
-
for f in modelXbrl.factsByLocalName["SubmissnTp"]:
|
|
175
|
-
if f.xValid >= VALID and not f.isNil:
|
|
176
|
-
submissionType = f.xValue # infer submissionType parameter from ffd:SubmissnTp
|
|
177
|
-
break
|
|
178
|
-
else:
|
|
179
|
-
for f in modelXbrl.factsByLocalName["DocumentType"]:
|
|
180
|
-
if f.xValid >= VALID and not f.isNil:
|
|
181
|
-
if not attachmentDocumentType: # infer attachmentDocumentType parameter from dei:DocumentType
|
|
182
|
-
attachmentDocumentType = docTypesAttachmentDocumentType.get(f.xValue, f.xValue)
|
|
183
|
-
if not hasSubmissionType: # infer submissionType parameter from dei:DocumentType
|
|
184
|
-
submissionType = docTypesSubType.get(f.xValue, f.xValue)
|
|
185
|
-
break
|
|
186
|
-
_setParams = []
|
|
187
|
-
if (not hasSubmissionType and submissionType):
|
|
188
|
-
_setParams.append (f"submissionType {submissionType}")
|
|
189
|
-
if (not hasAttachmentDocumentType and attachmentDocumentType):
|
|
190
|
-
_setParams.append (f"attachmentDocumentType {attachmentDocumentType}")
|
|
191
|
-
if _setParams:
|
|
192
|
-
modelXbrl.info("info",_("Setting submission parameters: %(setParams)s"), setParams=", ".join(_setParams))
|
|
193
|
-
|
|
194
|
-
modelXbrl.efmSubmissionType = submissionType
|
|
195
|
-
modelXbrl.efmAttachmentDocumentType = attachmentDocumentType
|
|
196
|
-
val.otherStandardTaxonomies = loadOtherStandardTaxonomies(modelXbrl, val)
|
|
197
|
-
compatibleTaxonomies = loadTaxonomyCompatibility(modelXbrl)
|
|
198
|
-
if isXbrlInstance:
|
|
199
|
-
deprecatedConceptDates = {}
|
|
200
|
-
deprecatedConceptFacts = defaultdict(list) # index by concept Qname, value is list of facts
|
|
201
|
-
deprecatedConceptContexts = defaultdict(list) # index by contextID, value is list of concept QNames of deprecated dimensions, members
|
|
202
|
-
|
|
203
|
-
if isEFM:
|
|
204
|
-
loadDeprecatedConceptDates(val, deprecatedConceptDates)
|
|
205
|
-
customAxesReplacements = loadCustomAxesReplacements(modelXbrl)
|
|
206
|
-
deiValidations = loadDeiValidations(modelXbrl, isInlineXbrl, attachmentDocumentType)
|
|
207
|
-
dqcRules = loadDqcRules(modelXbrl) # empty {} if no rules for filing
|
|
208
|
-
ugtRels = loadUgtRelQnames(modelXbrl, dqcRules) # None if no rels applicable
|
|
209
|
-
nonNegFacts = loadNonNegativeFacts(modelXbrl, dqcRules, ugtRels) # none if dqcRules are used after 2020
|
|
210
|
-
ixTrRegistries = loadIxTransformRegistries(modelXbrl)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
# inline doc set has multiple instance names to check
|
|
214
|
-
if modelXbrl.modelDocument.type == ModelDocument.Type.INLINEXBRLDOCUMENTSET:
|
|
215
|
-
instanceNames = [ixDoc.basename
|
|
216
|
-
for ixDoc in modelXbrl.modelDocument.referencesDocument.keys()
|
|
217
|
-
if ixDoc.type == ModelDocument.Type.INLINEXBRL]
|
|
218
|
-
xbrlInstRoots = modelXbrl.ixdsHtmlElements
|
|
219
|
-
else: # single instance document to check is the entry point document
|
|
220
|
-
instanceNames = [modelXbrl.modelDocument.basename]
|
|
221
|
-
xbrlInstRoots = [modelXbrl.modelDocument.xmlDocument.getroot()]
|
|
222
|
-
#6.3.3 filename check
|
|
223
|
-
for instanceName in instanceNames:
|
|
224
|
-
m = instanceFileNamePattern.match(instanceName)
|
|
225
|
-
if isInlineXbrl:
|
|
226
|
-
m = htmlFileNamePattern.match(instanceName)
|
|
227
|
-
if m:
|
|
228
|
-
val.fileNameBasePart = None # html file name not necessarily parseable.
|
|
229
|
-
val.fileNameDatePart = None
|
|
230
|
-
else:
|
|
231
|
-
modelXbrl.error(val.EFM60303,
|
|
232
|
-
_('Invalid inline xbrl document name in {base}.htm": %(filename)s'),
|
|
233
|
-
modelObject=modelXbrl.modelDocument, filename=instanceName,
|
|
234
|
-
messageCodes=("EFM.6.03.03",))
|
|
235
|
-
elif m:
|
|
236
|
-
val.fileNameBasePart = m.group(1)
|
|
237
|
-
val.fileNameDatePart = m.group(2)
|
|
238
|
-
if not val.fileNameBasePart:
|
|
239
|
-
modelXbrl.error((val.EFM60303, "GFM.1.01.01"),
|
|
240
|
-
_('Invalid instance document base name part (ticker or mnemonic name) in "{base}-{yyyymmdd}.xml": %(filename)s'),
|
|
241
|
-
modelObject=modelXbrl.modelDocument, filename=modelXbrl.modelDocument.basename,
|
|
242
|
-
messageCodes=("EFM.6.03.03", "EFM.6.58.01", "GFM.1.01.01"))
|
|
243
|
-
else:
|
|
244
|
-
try:
|
|
245
|
-
val.fileNameDate = datetime.datetime.strptime(val.fileNameDatePart,"%Y%m%d").date()
|
|
246
|
-
except ValueError:
|
|
247
|
-
modelXbrl.error((val.EFM60303, "GFM.1.01.01"),
|
|
248
|
-
_('Invalid instance document base name part (date) in "{base}-{yyyymmdd}.xml": %(filename)s'),
|
|
249
|
-
modelObject=modelXbrl.modelDocument, filename=modelXbrl.modelDocument.basename,
|
|
250
|
-
messageCodes=("EFM.6.03.03", "EFM.6.58.01", "GFM.1.01.01"))
|
|
251
|
-
elif not isFtJson:
|
|
252
|
-
modelXbrl.error((val.EFM60303, "GFM.1.01.01"),
|
|
253
|
-
_('Invalid instance document name, must match "{base}-{yyyymmdd}.xml": %(filename)s'),
|
|
254
|
-
modelObject=modelXbrl.modelDocument, filename=modelXbrl.modelDocument.basename,
|
|
255
|
-
messageCodes=("EFM.6.03.03", "EFM.6.58.01", "GFM.1.01.01"))
|
|
256
|
-
|
|
257
|
-
#6.5.1 scheme, 6.5.2, 6.5.3 identifier
|
|
258
|
-
entityIdentifierValue = None
|
|
259
|
-
entityIdentifierValueElt = None
|
|
260
|
-
if disclosureSystem.identifierValueName: # omit if no checks
|
|
261
|
-
for xbrlInstRoot in xbrlInstRoots: # check all inline docs in ix doc set
|
|
262
|
-
for entityIdentifierElt in xbrlInstRoot.iterdescendants("{http://www.xbrl.org/2003/instance}identifier"):
|
|
263
|
-
if isinstance(entityIdentifierElt,ModelObject):
|
|
264
|
-
schemeAttr = entityIdentifierElt.get("scheme","")
|
|
265
|
-
entityIdentifier = XmlUtil.text(entityIdentifierElt)
|
|
266
|
-
if not disclosureSystem.identifierSchemePattern.match(schemeAttr):
|
|
267
|
-
try:
|
|
268
|
-
contextId = entityIdentifierElt.getparent().getparent().id
|
|
269
|
-
except AttributeError:
|
|
270
|
-
contextId = "not available"
|
|
271
|
-
modelXbrl.error(("EFM.6.05.01", "GFM.1.02.01"),
|
|
272
|
-
_("Your identifier for the CIK code, %(identifier)s, or scheme %(scheme)s, in context %(context)s, did not adhere "
|
|
273
|
-
"to the standard naming convention of <identifier scheme='http://www.sec.gov/CIK'>xxxxxxxxxx</identifier>'. "
|
|
274
|
-
"Please recheck your submission and comply with the standard naming convention."),
|
|
275
|
-
edgarCode="cp-0501-Entity-Identifier-Scheme",
|
|
276
|
-
modelObject=entityIdentifierElt, scheme=schemeAttr,
|
|
277
|
-
context=contextId, identifier=entityIdentifier)
|
|
278
|
-
if not disclosureSystem.identifierValuePattern.match(entityIdentifier):
|
|
279
|
-
modelXbrl.error(("EFM.6.05.02", "GFM.1.02.02"),
|
|
280
|
-
_("Invalid entity identifier %(entityIdentifierName)s: %(entityIdentifer)s"),
|
|
281
|
-
modelObject=entityIdentifierElt,
|
|
282
|
-
entityIdentifierName=disclosureSystem.identifierValueName,
|
|
283
|
-
entityIdentifer=entityIdentifier)
|
|
284
|
-
if not entityIdentifierValue:
|
|
285
|
-
entityIdentifierValue = entityIdentifier
|
|
286
|
-
entityIdentifierValueElt = entityIdentifierElt
|
|
287
|
-
if isEFM and not efmCIKpattern.match(entityIdentifierValue):
|
|
288
|
-
val.modelXbrl.error("EFM.6.05.23.cikValue",
|
|
289
|
-
_("The context identifier CIK %(entityIdentifier)s is not 10 digits, for required context(s). "
|
|
290
|
-
"Please include a correct context identifier CIK in the filing."),
|
|
291
|
-
edgarCode="cp-0523-Non-Matching-Cik",
|
|
292
|
-
modelObject=entityIdentifierElt, entityIdentifier=entityIdentifierValue)
|
|
293
|
-
elif entityIdentifier != entityIdentifierValue:
|
|
294
|
-
modelXbrl.error(("EFM.6.05.03", "GFM.1.02.03"),
|
|
295
|
-
_("The submission CIK, %(filerIdentifier)s does not match either the EntityCentralIndexKey, %(entityIdentifer)s, "
|
|
296
|
-
"or context identifier CIK(s) %(entityIdentifer)s, %(entityIdentifer2)s, or is not 10 digits, for required context(s). "
|
|
297
|
-
"Please include a correct matching EntityCentralIndexKey and context identifier CIK(s) in the filing."),
|
|
298
|
-
edgarCode="cp-0523-Non-Matching-Cik",
|
|
299
|
-
modelObject=(entityIdentifierElt, entityIdentifierValueElt),
|
|
300
|
-
entityIdentifierName=disclosureSystem.identifierValueName,
|
|
301
|
-
entityIdentifer=entityIdentifierValue,
|
|
302
|
-
entityIdentifer2=entityIdentifier,
|
|
303
|
-
filerIdentifier=",".join(sorted(val.params["cikNameList"].keys()) if "cikNameList" in val.params else []))
|
|
304
|
-
val.modelXbrl.profileActivity("... filer identifier checks", minTimeToShow=1.0)
|
|
305
|
-
|
|
306
|
-
#6.5.7 duplicated contexts
|
|
307
|
-
contexts = modelXbrl.contexts.values()
|
|
308
|
-
contextIDs = set()
|
|
309
|
-
contextsWithNonNilFacts = set()
|
|
310
|
-
uniqueContextHashes = {}
|
|
311
|
-
contextsWithDisallowedOCEs = []
|
|
312
|
-
contextsWithDisallowedOCEcontent = []
|
|
313
|
-
nonStandardTypedDimensions = defaultdict(set)
|
|
314
|
-
nonStandardReplacableDimensions = defaultdict(set)
|
|
315
|
-
for context in contexts:
|
|
316
|
-
contextID = context.id
|
|
317
|
-
contextIDs.add(contextID)
|
|
318
|
-
h = context.contextDimAwareHash
|
|
319
|
-
if h in uniqueContextHashes:
|
|
320
|
-
if context.isEqualTo(uniqueContextHashes[h]):
|
|
321
|
-
modelXbrl.error(("EFM.6.05.07", "GFM.1.02.07"),
|
|
322
|
-
_("The instance document contained more than one context equivalent to %(context)s (%(context2)s). "
|
|
323
|
-
"Please remove duplicate contexts from the instance."),
|
|
324
|
-
edgarCode="du-0507-Duplicate-Contexts",
|
|
325
|
-
modelObject=(context, uniqueContextHashes[h]), context=contextID, context2=uniqueContextHashes[h].id)
|
|
326
|
-
else:
|
|
327
|
-
uniqueContextHashes[h] = context
|
|
328
|
-
|
|
329
|
-
#GFM no time in contexts
|
|
330
|
-
if isGFM:
|
|
331
|
-
for dateElt in XmlUtil.children(context, XbrlConst.xbrli, ("startDate", "endDate", "instant")):
|
|
332
|
-
dateText = XmlUtil.text(dateElt)
|
|
333
|
-
if not GFMcontextDatePattern.match(dateText):
|
|
334
|
-
modelXbrl.error("GFM.1.02.25",
|
|
335
|
-
_("Context id %(context)s %(elementName)s invalid content %(value)s"),
|
|
336
|
-
modelObject=dateElt, context=contextID,
|
|
337
|
-
elementName=dateElt.prefixedName, value=dateText)
|
|
338
|
-
#6.5.4 scenario
|
|
339
|
-
hasSegment = XmlUtil.hasChild(context, XbrlConst.xbrli, "segment")
|
|
340
|
-
hasScenario = XmlUtil.hasChild(context, XbrlConst.xbrli, "scenario")
|
|
341
|
-
notAllowed = None
|
|
342
|
-
if disclosureSystem.contextElement == "segment" and hasScenario:
|
|
343
|
-
notAllowed = _("Scenario")
|
|
344
|
-
elif disclosureSystem.contextElement == "scenario" and hasSegment:
|
|
345
|
-
notAllowed = _("Segment")
|
|
346
|
-
elif disclosureSystem.contextElement == "either" and hasSegment and hasScenario:
|
|
347
|
-
notAllowed = _("Both segment and scenario")
|
|
348
|
-
elif disclosureSystem.contextElement == "none" and (hasSegment or hasScenario):
|
|
349
|
-
notAllowed = _("Neither segment nor scenario")
|
|
350
|
-
if notAllowed:
|
|
351
|
-
if validateEFMpragmatic:
|
|
352
|
-
contextsWithDisallowedOCEs.append(context)
|
|
353
|
-
else:
|
|
354
|
-
modelXbrl.error(("EFM.6.05.04", "GFM.1.02.04"),
|
|
355
|
-
_("There must be no contexts with %(elementName)s, but %(count)s was(were) found: %(context)s."),
|
|
356
|
-
edgarCode="cp-0504-No-Scenario",
|
|
357
|
-
modelObject=context, elementName=notAllowed, context=contextID, count=1)
|
|
358
|
-
|
|
359
|
-
#6.5.5 segment only explicit dimensions
|
|
360
|
-
for contextName in {"segment": ("{http://www.xbrl.org/2003/instance}segment",),
|
|
361
|
-
"scenario": ("{http://www.xbrl.org/2003/instance}scenario",),
|
|
362
|
-
"either": ("{http://www.xbrl.org/2003/instance}segment","{http://www.xbrl.org/2003/instance}scenario"),
|
|
363
|
-
"both": ("{http://www.xbrl.org/2003/instance}segment","{http://www.xbrl.org/2003/instance}scenario"),
|
|
364
|
-
"none": [], None:[]
|
|
365
|
-
}[disclosureSystem.contextElement]:
|
|
366
|
-
for segScenElt in context.iterdescendants(contextName):
|
|
367
|
-
if isinstance(segScenElt,ModelObject):
|
|
368
|
-
_childTagNames = [child.prefixedName for child in segScenElt.iterchildren()
|
|
369
|
-
if isinstance(child,ModelObject) and
|
|
370
|
-
child.tag not in ("{http://xbrl.org/2006/xbrldi}explicitMember",
|
|
371
|
-
"{http://xbrl.org/2006/xbrldi}typedMember")]
|
|
372
|
-
childTags = ", ".join(_childTagNames)
|
|
373
|
-
if len(childTags) > 0:
|
|
374
|
-
if validateEFMpragmatic:
|
|
375
|
-
contextsWithDisallowedOCEcontent.append(context)
|
|
376
|
-
else:
|
|
377
|
-
modelXbrl.error(("EFM.6.05.05", "GFM.1.02.05"),
|
|
378
|
-
_("There must be no %(elementName)s with non-explicitDimension content, but %(count)s was(were) found: %(content)s."),
|
|
379
|
-
edgarCode="cp-0505-Segment-Child-Not-Explicit-Member",
|
|
380
|
-
modelObject=context, context=contextID, content=childTags, count=len(_childTagNames),
|
|
381
|
-
elementName=contextName.partition("}")[2].title())
|
|
382
|
-
for dim in context.qnameDims.values():
|
|
383
|
-
if isEFM and dim.dimension is not None and dim.dimensionQname.namespaceURI not in disclosureSystem.standardTaxonomiesDict:
|
|
384
|
-
if dim.isTyped:
|
|
385
|
-
nonStandardTypedDimensions[dim.dimensionQname].add(context)
|
|
386
|
-
if customAxesReplacements.customNamePatterns.match(dim.dimensionQname.localName):
|
|
387
|
-
nonStandardReplacableDimensions[dim.dimensionQname].add(context)
|
|
388
|
-
for _qname in (dim.dimensionQname, dim.memberQname):
|
|
389
|
-
if _qname in deprecatedConceptDates: # none if typed and then won't be in deprecatedConceptDates
|
|
390
|
-
deprecatedConceptContexts[contextID].append(_qname)
|
|
391
|
-
#6.5.38 period forever
|
|
392
|
-
if context.isForeverPeriod:
|
|
393
|
-
val.modelXbrl.error("EFM.6.05.38",
|
|
394
|
-
_("Context %(contextID)s uses period <xbrli:forever>. Please remove it and resubmit."),
|
|
395
|
-
edgarCode="du-0538-Context-Has-Period-Forever",
|
|
396
|
-
modelObject=context, contextID=contextID)
|
|
397
|
-
if validateEFMpragmatic: # output combined count message
|
|
398
|
-
if contextsWithDisallowedOCEs:
|
|
399
|
-
modelXbrl.error(("EFM.6.05.04", "GFM.1.02.04"),
|
|
400
|
-
_("There must be no contexts with %(elementName)s, but %(count)s was(were) found: %(context)s."),
|
|
401
|
-
edgarCode="cp-0504-No-Scenario",
|
|
402
|
-
modelObject=contextsWithDisallowedOCEs, elementName=notAllowed,
|
|
403
|
-
count=len(contextsWithDisallowedOCEs), context=', '.join(c.id for c in contextsWithDisallowedOCEs))
|
|
404
|
-
if contextsWithDisallowedOCEcontent:
|
|
405
|
-
modelXbrl.error(("EFM.6.05.05", "GFM.1.02.05"),
|
|
406
|
-
_("There must be no %(elementName)s with non-explicitDimension content, but %(count)s was(were) found: %(context)s."),
|
|
407
|
-
edgarCode="cp-0505-Segment-Child-Not-Explicit-Member",
|
|
408
|
-
modelObject=contextsWithDisallowedOCEcontent, elementName=disclosureSystem.contextElement,
|
|
409
|
-
count=len(contextsWithDisallowedOCEcontent), context=', '.join(c.id for c in contextsWithDisallowedOCEcontent))
|
|
410
|
-
if nonStandardTypedDimensions:
|
|
411
|
-
val.modelXbrl.error("EFM.6.05.39",
|
|
412
|
-
_("Typed dimensions must be defined in standard taxonomy schemas, contexts: %(contextIDs)s dimensions: %(dimensions)s."),
|
|
413
|
-
modelObject=set.union(*nonStandardTypedDimensions.values()),
|
|
414
|
-
edgarCode="cp-0539-Typed-Dimension-Not-Standard",
|
|
415
|
-
contextIDs=", ".join(sorted(cntx.id for cntx in set.union(*nonStandardTypedDimensions.values()))),
|
|
416
|
-
dimensions=", ".join(sorted(str(qn) for qn in nonStandardTypedDimensions.keys())))
|
|
417
|
-
for qn, contexts in sorted(nonStandardReplacableDimensions.items(), key=lambda i:str(i[0])):
|
|
418
|
-
try:
|
|
419
|
-
replacableAxisMatch = customAxesReplacements.customNamePatterns.match(qn.localName)
|
|
420
|
-
axis = [customAxesReplacements.standardAxes[k] for k,v in replacableAxisMatch.groupdict().items() if v is not None][0]
|
|
421
|
-
if replacableAxisMatch and any(v is not None for v in replacableAxisMatch.groupdict().values()):
|
|
422
|
-
val.modelXbrl.warning("EFM.6.05.44.customAxis",
|
|
423
|
-
_("Contexts %(contextIDs)s use dimension %(dimension)s in namespace %(namespace)s but %(axis)s in %(taxonomy)s is preferred."),
|
|
424
|
-
edgarCode="dq-0544-Custom-Axis",
|
|
425
|
-
modelObject=contexts, dimension=qn.localName, namespace=qn.namespaceURI,
|
|
426
|
-
axis=axis.partition(":")[2], taxonomy=axis.partition(":")[0],
|
|
427
|
-
contextIDs=", ".join(sorted(c.id for c in contexts)))
|
|
428
|
-
except (AttributeError, IndexError):
|
|
429
|
-
pass # something wrong with match table
|
|
430
|
-
del uniqueContextHashes, contextsWithDisallowedOCEs, contextsWithDisallowedOCEcontent, nonStandardTypedDimensions, nonStandardReplacableDimensions
|
|
431
|
-
val.modelXbrl.profileActivity("... filer context checks", minTimeToShow=1.0)
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
#fact items from standard context (no dimension)
|
|
435
|
-
amendmentFlag = None
|
|
436
|
-
amendmentFlagFact = None
|
|
437
|
-
documentPeriodEndDate = None # date or None
|
|
438
|
-
documentPeriodEndDateFact = None
|
|
439
|
-
documentTypeFact = None
|
|
440
|
-
documentTypeFactContextID = None
|
|
441
|
-
deiItems = {}
|
|
442
|
-
deiFacts = {}
|
|
443
|
-
def hasDeiFact(deiName):
|
|
444
|
-
return deiName in deiFacts and not deiFacts[deiName].isNil
|
|
445
|
-
|
|
446
|
-
extractedCoverFacts = defaultdict(list) # key concept localname
|
|
447
|
-
|
|
448
|
-
commonSharesItemsByStockClass = defaultdict(list)
|
|
449
|
-
commonSharesClassMembers = None
|
|
450
|
-
commonSharesClassAxisQName = None
|
|
451
|
-
deiSharesClassMembers = set()
|
|
452
|
-
|
|
453
|
-
# hasDefinedStockAxis = False
|
|
454
|
-
hasCommonSharesOutstandingDimensionedFactWithDefaultStockClass = False
|
|
455
|
-
# commonSharesClassUndefinedMembers = None
|
|
456
|
-
# commonStockMeasurementDatetime = None
|
|
457
|
-
|
|
458
|
-
deiNamespaceURI = None
|
|
459
|
-
deiCheckLocalNames = {
|
|
460
|
-
disclosureSystem.deiCurrentFiscalYearEndDateElement,
|
|
461
|
-
disclosureSystem.deiDocumentFiscalYearFocusElement,
|
|
462
|
-
"CurrentFiscalYearEndDate",
|
|
463
|
-
"DocumentFiscalPeriodFocus",
|
|
464
|
-
"EntityCommonStockSharesOutstanding",
|
|
465
|
-
"EntityCurrentReportingStatus",
|
|
466
|
-
"EntityEmergingGrowthCompany",
|
|
467
|
-
"EntityExTransitionPeriod",
|
|
468
|
-
"EntityFilerCategory",
|
|
469
|
-
"EntityInvCompanyType",
|
|
470
|
-
"EntityPublicFloat",
|
|
471
|
-
"EntityRegistrantName",
|
|
472
|
-
"EntityReportingCurrencyISOCode",
|
|
473
|
-
"EntityShellCompany",
|
|
474
|
-
"EntitySmallBusiness",
|
|
475
|
-
"EntityVoluntaryFilers",
|
|
476
|
-
"EntityWellKnownSeasonedIssuer"
|
|
477
|
-
}
|
|
478
|
-
#6.5.8 unused contexts
|
|
479
|
-
#candidateRequiredContexts = set()
|
|
480
|
-
for f in modelXbrl.facts:
|
|
481
|
-
factContextID = f.contextID
|
|
482
|
-
contextIDs.discard(factContextID)
|
|
483
|
-
|
|
484
|
-
context = f.context
|
|
485
|
-
factQname = f.qname # works for both inline and plain instances
|
|
486
|
-
factElementName = factQname.localName
|
|
487
|
-
if disclosureSystem.deiNamespacePattern is not None:
|
|
488
|
-
factInDeiNamespace = disclosureSystem.deiNamespacePattern.match(factQname.namespaceURI)
|
|
489
|
-
if factInDeiNamespace and deiNamespaceURI is None:
|
|
490
|
-
deiNamespaceURI = factQname.namespaceURI
|
|
491
|
-
deiADRmember = qname(deiNamespaceURI, "AdrMember")
|
|
492
|
-
else:
|
|
493
|
-
factInDeiNamespace = None
|
|
494
|
-
# standard dei items from required context
|
|
495
|
-
if context is not None and f.xValid >= VALID: # tests do not apply to tuples
|
|
496
|
-
if not context.hasSegment and not context.hasScenario:
|
|
497
|
-
#required context
|
|
498
|
-
if factInDeiNamespace and (
|
|
499
|
-
not f.concept.type.isWgnStringFactType or f.xmlLang.lower() == requiredFactLang):
|
|
500
|
-
value = f.xValue
|
|
501
|
-
if factElementName == disclosureSystem.deiAmendmentFlagElement:
|
|
502
|
-
amendmentFlag = value
|
|
503
|
-
amendmentFlagFact = f
|
|
504
|
-
elif factElementName == disclosureSystem.deiDocumentPeriodEndDateElement:
|
|
505
|
-
documentPeriodEndDate = value
|
|
506
|
-
documentPeriodEndDateFact = f
|
|
507
|
-
# commonStockMeasurementDatetime = context.endDatetime
|
|
508
|
-
#if (context.isStartEndPeriod and context.startDatetime is not None and context.endDatetime is not None):
|
|
509
|
-
# if context.endDatetime.time() == datetime.time(0): # midnight of subsequent day
|
|
510
|
-
# if context.endDatetime - datetime.timedelta(1) == f.xValue:
|
|
511
|
-
# candidateRequiredContexts.add(context)
|
|
512
|
-
# elif context.endDatetime.date() == f.xValue: # not midnight, only day portion matches
|
|
513
|
-
# candidateRequiredContexts.add(context)
|
|
514
|
-
elif factElementName == "DocumentType":
|
|
515
|
-
deiDocumentType = value # note that this may be different from attachmentDocumentType
|
|
516
|
-
documentTypeFact = f
|
|
517
|
-
documentTypeFactContextID = factContextID
|
|
518
|
-
elif factElementName == disclosureSystem.deiFilerIdentifierElement:
|
|
519
|
-
deiItems[factElementName] = value
|
|
520
|
-
deiFilerIdentifierFact = f
|
|
521
|
-
elif factElementName == disclosureSystem.deiFilerNameElement:
|
|
522
|
-
deiItems[factElementName] = value
|
|
523
|
-
deiFilerNameFact = f
|
|
524
|
-
elif factElementName in deiCheckLocalNames:
|
|
525
|
-
deiItems[factElementName] = value
|
|
526
|
-
deiFacts[factElementName] = f
|
|
527
|
-
if (val.requiredContext is None and context.isStartEndPeriod and
|
|
528
|
-
context.startDatetime is not None and context.endDatetime is not None):
|
|
529
|
-
val.requiredContext = context
|
|
530
|
-
else:
|
|
531
|
-
# segment present
|
|
532
|
-
isEntityCommonStockSharesOutstanding = factElementName == "EntityCommonStockSharesOutstanding"
|
|
533
|
-
hasClassOfStockMember = False
|
|
534
|
-
|
|
535
|
-
# note all concepts used in explicit dimensions
|
|
536
|
-
for dimValue in context.qnameDims.values():
|
|
537
|
-
if dimValue.isExplicit:
|
|
538
|
-
dimConcept = dimValue.dimension
|
|
539
|
-
memConcept = dimValue.member
|
|
540
|
-
for dConcept in (dimConcept, memConcept):
|
|
541
|
-
if dConcept is not None:
|
|
542
|
-
conceptsUsed[dConcept] = False
|
|
543
|
-
if (isEntityCommonStockSharesOutstanding and
|
|
544
|
-
dimConcept is not None and
|
|
545
|
-
dimConcept.name in ("StatementClassOfStockAxis", "ClassesOfShareCapitalAxis") and
|
|
546
|
-
dimConcept.modelDocument.targetNamespace in disclosureSystem.standardTaxonomiesDict):
|
|
547
|
-
commonSharesClassAxisQName = dimConcept.qname
|
|
548
|
-
commonSharesItemsByStockClass[memConcept.qname].append(f)
|
|
549
|
-
''' per discussion with Dean R, remove use of LB defined members from this test
|
|
550
|
-
if commonSharesClassMembers is None:
|
|
551
|
-
commonSharesClassMembers, hasDefinedStockAxis = val.getDimMembers(dimConcept)
|
|
552
|
-
if not hasDefinedStockAxis: # no def LB for stock axis, note observed members
|
|
553
|
-
commonSharesClassMembers.add(memConcept.qname)
|
|
554
|
-
#following is replacement:'''
|
|
555
|
-
if commonSharesClassMembers is None:
|
|
556
|
-
commonSharesClassMembers = set()
|
|
557
|
-
commonSharesClassMembers.add(memConcept.qname) # only note the actually used members, not any defined members
|
|
558
|
-
#end of replacement
|
|
559
|
-
hasClassOfStockMember = True
|
|
560
|
-
if factInDeiNamespace and dimConcept is not None and dimConcept.name in ("StatementClassOfStockAxis", "ClassesOfShareCapitalAxis") and memConcept is not None:
|
|
561
|
-
deiSharesClassMembers.add(memConcept.qname)
|
|
562
|
-
|
|
563
|
-
if isEntityCommonStockSharesOutstanding and not hasClassOfStockMember:
|
|
564
|
-
hasCommonSharesOutstandingDimensionedFactWithDefaultStockClass = True # absent dimension, may be no def LB
|
|
565
|
-
|
|
566
|
-
# 6.5.43 signs - applies to all facts having a context.
|
|
567
|
-
if (isEFM and nonNegFacts and f.qname in nonNegFacts.concepts and f.isNumeric and not f.isNil and f.xValue < 0 and (
|
|
568
|
-
all(dim.isTyped or (
|
|
569
|
-
(dim.dimensionQname not in nonNegFacts.excludedAxesMembers or
|
|
570
|
-
("*" not in nonNegFacts.excludedAxesMembers[dim.dimensionQname] and
|
|
571
|
-
dim.memberQname not in nonNegFacts.excludedAxesMembers[dim.dimensionQname])) and
|
|
572
|
-
dim.memberQname not in nonNegFacts.excludedMembers and
|
|
573
|
-
(nonNegFacts.excludedMemberNamesPattern is None or
|
|
574
|
-
not nonNegFacts.excludedMemberNamesPattern.search(dim.memberQname.localName)))
|
|
575
|
-
for dim in context.qnameDims.values()))):
|
|
576
|
-
modelXbrl.warning("EFM.6.05.43",
|
|
577
|
-
_("Concept %(element)s in %(taxonomy)s has a negative value %(value)s in context %(context)s. Correct the sign, use a more appropriate concept, or change the context."),
|
|
578
|
-
edgarCode="dq-0543-Negative-Fact-Value",
|
|
579
|
-
modelObject=f, element=f.qname.localName, taxonomy=abbreviatedNamespace(f.qname.namespaceURI),
|
|
580
|
-
value=f.value, context=f.contextID)
|
|
581
|
-
|
|
582
|
-
if not f.isNil:
|
|
583
|
-
contextsWithNonNilFacts.add(context)
|
|
584
|
-
if f.qname.localName in deiValidations.get("extraction-cover-tags", ()):
|
|
585
|
-
extractedCoverFacts[f.qname.localName].append(f)
|
|
586
|
-
|
|
587
|
-
if isEFM: # note that this is in the "if context is not None" region. It does receive nil facts.
|
|
588
|
-
for pluginXbrlMethod in pluginClassMethods("Validate.EFM.Fact"):
|
|
589
|
-
pluginXbrlMethod(val, f)
|
|
590
|
-
#6.5.17 facts with precision
|
|
591
|
-
concept = f.concept
|
|
592
|
-
if concept is not None:
|
|
593
|
-
# note fact concepts used
|
|
594
|
-
conceptsUsed[concept] = False
|
|
595
|
-
|
|
596
|
-
if concept.isNumeric:
|
|
597
|
-
if f.precision is not None:
|
|
598
|
-
modelXbrl.error(("EFM.6.05.17", "GFM.1.02.16"),
|
|
599
|
-
_("Your filing contained elements using the precision attribute. Please recheck your submission and replace "
|
|
600
|
-
"the precision attribute with the decimals attribute."),
|
|
601
|
-
edgarCode="fs-0517-Decimals-Not-Precision",
|
|
602
|
-
modelObject=f, fact=f.qname, contextID=factContextID, precision=f.precision)
|
|
603
|
-
|
|
604
|
-
#6.5.25 domain items as facts
|
|
605
|
-
if isEFM and concept.type is not None and concept.type.isDomainItemType:
|
|
606
|
-
modelXbrl.error("EFM.6.05.25",
|
|
607
|
-
_("The domain item %(fact)s cannot appear as a fact. Please remove the fact from context %(contextID)s."),
|
|
608
|
-
edgarCode="du-0525-Domain-As-Fact",
|
|
609
|
-
modelObject=f, fact=f.qname, contextID=factContextID)
|
|
610
|
-
|
|
611
|
-
if concept.qname in deprecatedConceptDates:
|
|
612
|
-
deprecatedConceptFacts[concept.qname].append(f)
|
|
613
|
-
|
|
614
|
-
if concept.isEnumeration and not f.isNil:
|
|
615
|
-
for qnEnum in flattenSequence(f.xValue):
|
|
616
|
-
if qnEnum in deprecatedConceptDates:
|
|
617
|
-
deprecatedConceptFacts[qnEnum].append(f)
|
|
618
|
-
|
|
619
|
-
if factContextID in deprecatedConceptContexts: # deprecated dimension and member qnames
|
|
620
|
-
for _qname in deprecatedConceptContexts[factContextID]:
|
|
621
|
-
deprecatedConceptFacts[_qname].append(f)
|
|
622
|
-
|
|
623
|
-
if validateInlineXbrlGFM:
|
|
624
|
-
if f.localName == "nonFraction" or f.localName == "fraction":
|
|
625
|
-
syms = signOrCurrencyPattern.findall(f.text)
|
|
626
|
-
if syms:
|
|
627
|
-
modelXbrl.error(("EFM.N/A", "GFM.1.10.18"),
|
|
628
|
-
'ix-numeric Fact %(fact)s of context %(contextID)s has a sign or currency symbol "%(value)s" in "%(text)s"',
|
|
629
|
-
modelObject=f, fact=f.qname, contextID=factContextID,
|
|
630
|
-
value="".join(s for t in syms for s in t), text=f.text)
|
|
631
|
-
|
|
632
|
-
val.entityRegistrantName = deiItems.get("EntityRegistrantName") # used for name check in 6.8.6
|
|
633
|
-
|
|
634
|
-
# 6.05..23,24 check (after dei facts read)
|
|
635
|
-
if not (isEFM and deiDocumentType == "L SDR"): # allow entityIdentifierValue == "0000000000" or any other CIK value
|
|
636
|
-
if disclosureSystem.deiFilerIdentifierElement in deiItems:
|
|
637
|
-
value = deiItems.get(disclosureSystem.deiFilerIdentifierElement)
|
|
638
|
-
if entityIdentifierValue != value:
|
|
639
|
-
val.modelXbrl.error(("EFM.6.05.23", "GFM.3.02.02"),
|
|
640
|
-
_("The EntityCentralIndexKey, %(value)s, does not match the context identifier CIK %(entityIdentifier)s. "
|
|
641
|
-
"Please include a correct matching EntityCentralIndexKey and context identifier CIK(s) in the filing."),
|
|
642
|
-
edgarCode="cp-0523-Non-Matching-Cik",
|
|
643
|
-
modelObject=deiFilerIdentifierFact, elementName=disclosureSystem.deiFilerIdentifierElement,
|
|
644
|
-
value=value, entityIdentifier=entityIdentifierValue)
|
|
645
|
-
if "cikNameList" in val.params:
|
|
646
|
-
if value not in val.params["cikNameList"]:
|
|
647
|
-
val.modelXbrl.error(("EFM.6.05.23.submissionIdentifier", "GFM.3.02.02"),
|
|
648
|
-
_("The submission CIK, %(filerIdentifier)s does not match the EntityCentralIndexKey. "
|
|
649
|
-
"Please include a correct matching EntityCentralIndexKey in the filing."),
|
|
650
|
-
edgarCode="cp-0523-Non-Matching-Cik",
|
|
651
|
-
modelObject=deiFilerIdentifierFact, elementName=disclosureSystem.deiFilerIdentifierElement,
|
|
652
|
-
value=value, filerIdentifier=",".join(sorted(val.params["cikNameList"].keys())))
|
|
653
|
-
elif val.params.get("cik") and value != val.params["cik"]:
|
|
654
|
-
val.modelXbrl.error(("EFM.6.05.23.submissionIdentifier", "GFM.3.02.02"),
|
|
655
|
-
_("The submission CIK, %(filerIdentifier)s does not match the %(elementName)s. "
|
|
656
|
-
"Please include a correct matching %(elementName)s in the filing."),
|
|
657
|
-
edgarCode="cp-0523-Non-Matching-Cik",
|
|
658
|
-
modelObject=deiFilerIdentifierFact, elementName=disclosureSystem.deiFilerIdentifierElement,
|
|
659
|
-
value=value, filerIdentifier=val.params["cik"])
|
|
660
|
-
if disclosureSystem.deiFilerNameElement in deiItems:
|
|
661
|
-
value = deiItems[disclosureSystem.deiFilerNameElement]
|
|
662
|
-
if "cikNameList" in val.params and entityIdentifierValue in val.params["cikNameList"]:
|
|
663
|
-
prefix = val.params["cikNameList"][entityIdentifierValue]
|
|
664
|
-
if prefix is not None:
|
|
665
|
-
if ((isInlineXbrl and not re.match(cleanedCompanyName(prefix).replace("-", r"[\s-]?"),
|
|
666
|
-
cleanedCompanyName(value), flags=re.IGNORECASE)) or
|
|
667
|
-
(not isInlineXbrl and not value.casefold().startswith(prefix.casefold()))): # casefold needed for some non-en languages
|
|
668
|
-
val.modelXbrl.error(("EFM.6.05.24", "GFM.3.02.02"),
|
|
669
|
-
_("The Official Registrant name, %(prefix)s, does not match the value %(value)s in the Required Context. "
|
|
670
|
-
"Please correct dei:%(elementName)s."),
|
|
671
|
-
edgarCode="cp-0524-Registrant-Name-Mismatch",
|
|
672
|
-
modelObject=deiFilerNameFact, elementName=disclosureSystem.deiFilerNameElement,
|
|
673
|
-
prefix=prefix, value=value)
|
|
674
|
-
|
|
675
|
-
if isEFM and disclosureSystem.deiNamespacePattern is not None:
|
|
676
|
-
if deiNamespaceURI is None:
|
|
677
|
-
deiNamespaceURI = modelXbrl.prefixedNamespaces.get("dei")
|
|
678
|
-
if deiNamespaceURI is None:
|
|
679
|
-
modelXbrl.error("EFM.6.05.20.deiFactsMissing",
|
|
680
|
-
_("DEI facts are missing."),
|
|
681
|
-
edgarCode="dq-{efmSection}-{tag}-Missing",
|
|
682
|
-
modelObject=modelXbrl, subType=submissionType, efmSection="0520", severityVerb="must", tag="DEI-Facts", context="Required Context")
|
|
683
|
-
|
|
684
|
-
val.modelXbrl.profileActivity("... filer fact checks", minTimeToShow=1.0)
|
|
685
|
-
|
|
686
|
-
if len(contextIDs) > 0: # check if contextID is on any undefined facts
|
|
687
|
-
for undefinedFact in modelXbrl.undefinedFacts:
|
|
688
|
-
contextIDs.discard(undefinedFact.get("contextRef"))
|
|
689
|
-
if len(contextIDs) > 0:
|
|
690
|
-
modelXbrl.error(("EFM.6.05.08", "GFM.1.02.08"),
|
|
691
|
-
_("The instance document contained a context %(contextIDs)s that was not used in any fact. Please remove the context from the instance."),
|
|
692
|
-
edgarCode="du-0508-Unused-Context",
|
|
693
|
-
modelXbrl=modelXbrl, contextIDs=", ".join(str(c) for c in contextIDs))
|
|
694
|
-
|
|
695
|
-
#6.5.9, .10 start-end durations
|
|
696
|
-
if disclosureSystem.GFM or \
|
|
697
|
-
disclosureSystemVersion[0] >= 27 or \
|
|
698
|
-
deiDocumentType in {
|
|
699
|
-
'20-F', '40-F', '10-Q', '10-QT', '10-K', '10-KT', '10', 'N-CSR', 'N-CSRS', 'N-Q',
|
|
700
|
-
'20-F/A', '40-F/A', '10-Q/A', '10-QT/A', '10-K/A', '10-KT/A', '10/A', 'N-CSR/A', 'N-CSRS/A', 'N-Q/A'}:
|
|
701
|
-
'''
|
|
702
|
-
for c1 in contexts:
|
|
703
|
-
if c1.isStartEndPeriod:
|
|
704
|
-
end1 = c1.endDatetime
|
|
705
|
-
start1 = c1.startDatetime
|
|
706
|
-
for c2 in contexts:
|
|
707
|
-
if c1 != c2 and c2.isStartEndPeriod:
|
|
708
|
-
duration = end1 - c2.startDatetime
|
|
709
|
-
if duration > datetime.timedelta(0) and duration <= datetime.timedelta(1):
|
|
710
|
-
modelXbrl.error(("EFM.6.05.09", "GFM.1.2.9"),
|
|
711
|
-
_("Context {0} endDate and {1} startDate have a duration of one day; that is inconsistent with document type {2}."),
|
|
712
|
-
c1.id, c2.id, deiDocumentType),
|
|
713
|
-
"err", )
|
|
714
|
-
if isEFM and c1 != c2 and c2.isInstantPeriod:
|
|
715
|
-
duration = c2.endDatetime - start1
|
|
716
|
-
if duration > datetime.timedelta(0) and duration <= datetime.timedelta(1):
|
|
717
|
-
modelXbrl.error(
|
|
718
|
-
_("Context {0} startDate and {1} end (instant) have a duration of one day; that is inconsistent with document type {2}."),
|
|
719
|
-
c1.id, c2.id, deiDocumentType),
|
|
720
|
-
"err", "EFM.6.05.10")
|
|
721
|
-
'''
|
|
722
|
-
durationCntxStartDatetimes = defaultdict(set)
|
|
723
|
-
for cntx in contexts:
|
|
724
|
-
if cntx.isStartEndPeriod and cntx.startDatetime is not None:
|
|
725
|
-
durationCntxStartDatetimes[cntx.startDatetime].add(cntx)
|
|
726
|
-
probStartEndCntxsByEnd = defaultdict(set)
|
|
727
|
-
startEndCntxsByEnd = defaultdict(set)
|
|
728
|
-
probInstantCntxsByEnd = defaultdict(set)
|
|
729
|
-
probCntxs = set()
|
|
730
|
-
for cntx in contexts:
|
|
731
|
-
end = cntx.endDatetime
|
|
732
|
-
if end is not None:
|
|
733
|
-
if cntx.isStartEndPeriod:
|
|
734
|
-
thisStart = cntx.startDatetime
|
|
735
|
-
for otherStart, otherCntxs in durationCntxStartDatetimes.items():
|
|
736
|
-
duration = end - otherStart
|
|
737
|
-
if duration > datetime.timedelta(0) and duration <= datetime.timedelta(1):
|
|
738
|
-
if disclosureSystemVersion[0] < 27:
|
|
739
|
-
probCntxs |= otherCntxs - {cntx}
|
|
740
|
-
elif thisStart is not None and end - thisStart > datetime.timedelta(1):
|
|
741
|
-
for otherCntx in otherCntxs:
|
|
742
|
-
if otherCntx is not cntx and otherCntx.endDatetime != end and otherStart != cntx.startDatetime:
|
|
743
|
-
probCntxs.add(otherCntx)
|
|
744
|
-
if probCntxs:
|
|
745
|
-
probStartEndCntxsByEnd[end] |= probCntxs
|
|
746
|
-
startEndCntxsByEnd[end] |= {cntx}
|
|
747
|
-
probCntxs.clear()
|
|
748
|
-
if isEFM and cntx.isInstantPeriod:
|
|
749
|
-
for otherStart, otherCntxs in durationCntxStartDatetimes.items():
|
|
750
|
-
duration = end - otherStart
|
|
751
|
-
if duration > datetime.timedelta(0) and duration <= datetime.timedelta(1):
|
|
752
|
-
probCntxs |= otherCntxs
|
|
753
|
-
if probCntxs:
|
|
754
|
-
probInstantCntxsByEnd[end] |= ( probCntxs | {cntx} )
|
|
755
|
-
probCntxs.clear()
|
|
756
|
-
del probCntxs
|
|
757
|
-
for end, probCntxs in probStartEndCntxsByEnd.items():
|
|
758
|
-
endCntxs = startEndCntxsByEnd[end]
|
|
759
|
-
modelXbrl.error(("EFM.6.05.09", "GFM.1.2.9"),
|
|
760
|
-
_("Context %(endContexts)s endDate and %(startContexts)s startDate have a duration of one day; that is inconsistent "
|
|
761
|
-
"with document type %(documentType)s."),
|
|
762
|
-
edgarCode="fs-0509-Start-And-End-Dates-Not-Distinct-Inconsistent-With-Document-Type",
|
|
763
|
-
modelObject=probCntxs, endDate=XmlUtil.dateunionValue(end, subtractOneDay=True),
|
|
764
|
-
endContexts=', '.join(sorted(c.id for c in endCntxs)),
|
|
765
|
-
startContexts=', '.join(sorted(c.id for c in probCntxs)),
|
|
766
|
-
documentType=deiDocumentType)
|
|
767
|
-
if disclosureSystemVersion[0] < 27:
|
|
768
|
-
for end, probCntxs in probInstantCntxsByEnd.items():
|
|
769
|
-
modelXbrl.error("EFM.6.05.10",
|
|
770
|
-
_("Contexts %(contexts)s have an overlap of one day; that is inconsistent with document type %(documentType)s."),
|
|
771
|
-
edgarCode="fs-0510-Start-And-Instant-Dates-Not-Distinct-Inconsistent-With-Document-Type",
|
|
772
|
-
modelObject=probCntxs, endDate=XmlUtil.dateunionValue(end, subtractOneDay=True),
|
|
773
|
-
contexts=', '.join(sorted(c.id for c in probCntxs)),
|
|
774
|
-
documentType=deiDocumentType)
|
|
775
|
-
del probStartEndCntxsByEnd, startEndCntxsByEnd, probInstantCntxsByEnd
|
|
776
|
-
del durationCntxStartDatetimes
|
|
777
|
-
val.modelXbrl.profileActivity("... filer instant-duration checks", minTimeToShow=1.0)
|
|
778
|
-
|
|
779
|
-
#6.5.19 required context
|
|
780
|
-
#for c in sorted(candidateRequiredContexts, key=lambda c: (c.endDatetime, c.endDatetime-c.startDatetime), reverse=True):
|
|
781
|
-
# val.requiredContext = c
|
|
782
|
-
# break # longest duration is first
|
|
783
|
-
|
|
784
|
-
# pre-16.1 code to accept any duration period as start-end (per WH/HF e-mails 2016-03-13)
|
|
785
|
-
if val.requiredContext is None: # possibly there is no document period end date with matching context
|
|
786
|
-
for c in contexts:
|
|
787
|
-
if c.isStartEndPeriod and not c.hasSegment and c.startDatetime is not None and c.endDatetime is not None:
|
|
788
|
-
val.requiredContext = c
|
|
789
|
-
break
|
|
790
|
-
|
|
791
|
-
if val.requiredContext is None:
|
|
792
|
-
modelXbrl.error(("EFM.6.05.19", "GFM.1.02.18"),
|
|
793
|
-
_("Required context (no segment) not found for document type %(documentType)s."),
|
|
794
|
-
edgarCode="cp-0519-Required-Context",
|
|
795
|
-
modelObject=modelXbrl, documentType=deiDocumentType)
|
|
796
|
-
|
|
797
|
-
#6.5.11 equivalent units
|
|
798
|
-
uniqueUnitHashes = {}
|
|
799
|
-
for unit in val.modelXbrl.units.values():
|
|
800
|
-
h = unit.hash
|
|
801
|
-
if h in uniqueUnitHashes:
|
|
802
|
-
if unit.isEqualTo(uniqueUnitHashes[h]):
|
|
803
|
-
modelXbrl.error(("EFM.6.05.11", "GFM.1.02.10"),
|
|
804
|
-
_("There is more than one unit equivalent to %(unitID)s (%(unitID2)s). Please remove all but one and resubmit."),
|
|
805
|
-
edgarCode="du-0511-Duplicate-Units",
|
|
806
|
-
modelObject=(unit, uniqueUnitHashes[h]), unitID=unit.id, unitID2=uniqueUnitHashes[h].id)
|
|
807
|
-
else:
|
|
808
|
-
uniqueUnitHashes[h] = unit
|
|
809
|
-
if isEFM: # 6.5.38
|
|
810
|
-
for measureElt in unit.iterdescendants(tag="{http://www.xbrl.org/2003/instance}measure"):
|
|
811
|
-
if isinstance(measureElt.xValue, ModelValue.QName) and len(measureElt.xValue.localName) > 65:
|
|
812
|
-
l = len(measureElt.xValue.localName.encode("utf-8"))
|
|
813
|
-
if l > 200:
|
|
814
|
-
modelXbrl.error("EFM.6.05.36",
|
|
815
|
-
_("Unit %(unitID)s contains a measure element whose local-name in UTF-8, length %(length)s, has more than 200 bytes: %(measure)s. Shorten the measure name."),
|
|
816
|
-
edgarCode="du-0536-Name-Length-Limit",
|
|
817
|
-
modelObject=measureElt, unitID=unit.id, measure=measureElt.xValue.localName, length=l)
|
|
818
|
-
del uniqueUnitHashes
|
|
819
|
-
|
|
820
|
-
# 6.5.42 deprecated concepts
|
|
821
|
-
if deprecatedConceptFacts:
|
|
822
|
-
for conceptQn, facts in sorted(deprecatedConceptFacts.items(), key=lambda i:[0]):
|
|
823
|
-
date = deprecatedConceptDates[conceptQn]
|
|
824
|
-
version1 = abbreviatedNamespace(conceptQn.namespaceURI)
|
|
825
|
-
modelXbrl.warning("EFM.6.05.42",
|
|
826
|
-
_("Concept %(element)s in %(version1)s used in %(count)s facts was deprecated in %(version2)s as of %(date)s and should not be used."),
|
|
827
|
-
edgarCode="dq-0542-Deprecated-Concept",
|
|
828
|
-
modelObject=facts, element=conceptQn.localName, count=len(facts), date=date,
|
|
829
|
-
version1=version1, version2=version1[:-4]+date[0:4])
|
|
830
|
-
|
|
831
|
-
del deprecatedConceptContexts, deprecatedConceptFacts, deprecatedConceptDates, nonNegFacts
|
|
832
|
-
val.modelXbrl.profileActivity("... filer unit checks", minTimeToShow=1.0)
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
# EFM.6.05.14, GFM.1.02.13 xml:lang tests, as of v-17, full default lang is compared
|
|
836
|
-
#if val.validateEFM:
|
|
837
|
-
# factLangStartsWith = disclosureSystem.defaultXmlLang[:2]
|
|
838
|
-
#else:
|
|
839
|
-
# factLangStartsWith = disclosureSystem.defaultXmlLang
|
|
840
|
-
|
|
841
|
-
#6.5.12 equivalent facts
|
|
842
|
-
factsForLang = {}
|
|
843
|
-
factForConceptContextUnitHash = defaultdict(list)
|
|
844
|
-
keysNotDefaultLang = {}
|
|
845
|
-
for f1 in modelXbrl.facts:
|
|
846
|
-
if f1.context is not None and f1.concept is not None and f1.concept.type is not None and getattr(f1,"xValid", 0) >= VALID:
|
|
847
|
-
# build keys table for 6.5.14
|
|
848
|
-
if not f1.isNil:
|
|
849
|
-
langTestKey = "{0},{1},{2}".format(f1.qname, f1.contextID, f1.unitID)
|
|
850
|
-
factsForLang.setdefault(langTestKey, []).append(f1)
|
|
851
|
-
lang = f1.xmlLang
|
|
852
|
-
if lang and lang.lower() != requiredFactLang: # not lang.startswith(factLangStartsWith):
|
|
853
|
-
keysNotDefaultLang[langTestKey] = f1
|
|
854
|
-
|
|
855
|
-
# 6.5.37 test (insignificant digits due to rounding)
|
|
856
|
-
if f1.isNumeric and f1.decimals and f1.decimals != "INF":
|
|
857
|
-
try:
|
|
858
|
-
insignificance = insignificantDigits(f1.xValue, decimals=f1.decimals)
|
|
859
|
-
if insignificance: # if not None, returns (truncatedDigits, insiginficantDigits)
|
|
860
|
-
modelXbrl.error(("EFM.6.05.37", "GFM.1.02.26"),
|
|
861
|
-
_("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s has insignificant digits %(insignificantDigits)s. "
|
|
862
|
-
"Please correct the fact value and resubmit."),
|
|
863
|
-
edgarCode="du-0537-Nonzero-Digits-Truncated",
|
|
864
|
-
modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals,
|
|
865
|
-
value=f1.xValue, truncatedDigits=insignificance[0], insignificantDigits=insignificance[1])
|
|
866
|
-
except (ValueError,TypeError):
|
|
867
|
-
modelXbrl.error(("EFM.6.05.37", "GFM.1.02.26"),
|
|
868
|
-
_("Fact %(fact)s of context %(contextID)s decimals %(decimals)s value %(value)s causes a Value Error exception. "
|
|
869
|
-
"Please correct the fact value and resubmit."),
|
|
870
|
-
edgarCode="du-0537-Nonzero-Digits-Truncated",
|
|
871
|
-
modelObject=f1, fact=f1.qname, contextID=f1.contextID, decimals=f1.decimals, value=f1.value)
|
|
872
|
-
# 6.5.12 test
|
|
873
|
-
factForConceptContextUnitHash[f1.conceptContextUnitHash].append(f1)
|
|
874
|
-
# 6.5.12 test
|
|
875
|
-
aspectEqualFacts = defaultdict(list)
|
|
876
|
-
decVals = {}
|
|
877
|
-
for hashEquivalentFacts in factForConceptContextUnitHash.values():
|
|
878
|
-
if len(hashEquivalentFacts) > 1:
|
|
879
|
-
for f in hashEquivalentFacts:
|
|
880
|
-
aspectEqualFacts[(f.qname,f.contextID,f.unitID,
|
|
881
|
-
f.xmlLang.lower() if f.concept.type.isWgnStringFactType else None)].append(f)
|
|
882
|
-
for fList in aspectEqualFacts.values():
|
|
883
|
-
f0 = fList[0]
|
|
884
|
-
if f0.concept.isNumeric:
|
|
885
|
-
if any(f.isNil for f in fList):
|
|
886
|
-
_inConsistent = not all(f.isNil for f in fList)
|
|
887
|
-
else: # not all have same decimals
|
|
888
|
-
_d = inferredDecimals(f0)
|
|
889
|
-
_v = f0.xValue
|
|
890
|
-
_inConsistent = isnan(_v) # NaN is incomparable, always makes dups inconsistent
|
|
891
|
-
decVals[_d] = _v
|
|
892
|
-
aMax, bMin, _inclA, _inclB = rangeValue(_v, _d)
|
|
893
|
-
for f in fList[1:]:
|
|
894
|
-
_d = inferredDecimals(f)
|
|
895
|
-
_v = f.xValue
|
|
896
|
-
if isnan(_v):
|
|
897
|
-
_inConsistent = True
|
|
898
|
-
break
|
|
899
|
-
if _d in decVals:
|
|
900
|
-
_inConsistent |= _v != decVals[_d]
|
|
901
|
-
else:
|
|
902
|
-
decVals[_d] = _v
|
|
903
|
-
a, b, _inclA, _inclB = rangeValue(_v, _d)
|
|
904
|
-
if a > aMax: aMax = a
|
|
905
|
-
if b < bMin: bMin = b
|
|
906
|
-
if not _inConsistent:
|
|
907
|
-
_inConsistent = (bMin < aMax)
|
|
908
|
-
decVals.clear()
|
|
909
|
-
else:
|
|
910
|
-
_inConsistent = any(not f.isVEqualTo(f0) for f in fList[1:])
|
|
911
|
-
if _inConsistent:
|
|
912
|
-
modelXbrl.error(("EFM.6.05.12", "GFM.1.02.11"),
|
|
913
|
-
"The instance document contained an element, %(fact)s that was used more than once in contexts equivalent to %(contextID)s: values %(values)s. "
|
|
914
|
-
"Please ensure there are no duplicate combinations of concept and context in the instance.",
|
|
915
|
-
edgarCode="du-0512-Duplicate-Facts",
|
|
916
|
-
modelObject=fList, fact=f0.qname, contextID=f0.contextID, values=", ".join(strTruncate(f.value, 128) for f in fList))
|
|
917
|
-
aspectEqualFacts.clear()
|
|
918
|
-
del factForConceptContextUnitHash, aspectEqualFacts
|
|
919
|
-
val.modelXbrl.profileActivity("... filer fact checks", minTimeToShow=1.0)
|
|
920
|
-
|
|
921
|
-
#6.5.14 facts without english text
|
|
922
|
-
for keyNotDefaultLang, factNotDefaultLang in keysNotDefaultLang.items():
|
|
923
|
-
anyDefaultLangFact = False
|
|
924
|
-
for fact in factsForLang[keyNotDefaultLang]:
|
|
925
|
-
if fact.xmlLang.lower() == requiredFactLang: #.startswith(factLangStartsWith):
|
|
926
|
-
anyDefaultLangFact = True
|
|
927
|
-
break
|
|
928
|
-
if not anyDefaultLangFact:
|
|
929
|
-
val.modelXbrl.error(("EFM.6.05.14", "GFM.1.02.13"),
|
|
930
|
-
_("Element %(fact)s in context %(contextID)s has text with xml:lang other than '%(lang2)s' (%(lang)s) without matching English text. "
|
|
931
|
-
"Please provide a fact with xml:lang equal to '%(lang2)s'."),
|
|
932
|
-
edgarCode="du-0514-English-Text-Missing",
|
|
933
|
-
modelObject=factNotDefaultLang, fact=factNotDefaultLang.qname, contextID=factNotDefaultLang.contextID,
|
|
934
|
-
lang=factNotDefaultLang.xmlLang, lang2=disclosureSystem.defaultXmlLang) # report lexical format default lang
|
|
935
|
-
|
|
936
|
-
#label validations
|
|
937
|
-
if not labelsRelationshipSet:
|
|
938
|
-
val.modelXbrl.error(("EFM.6.10.01.missingLabelLinkbase", "GFM.1.05.01"),
|
|
939
|
-
_("A label linkbase is required but was not found"),
|
|
940
|
-
modelXbrl=modelXbrl)
|
|
941
|
-
elif disclosureSystem.defaultXmlLang: # cannot check if no defaultXmlLang specified
|
|
942
|
-
for concept in conceptsUsed.keys():
|
|
943
|
-
checkConceptLabels(val, modelXbrl, labelsRelationshipSet, disclosureSystem, concept)
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
#6.5.15 facts with xml in text blocks
|
|
947
|
-
ValidateFilingText.validateTextBlockFacts(modelXbrl)
|
|
948
|
-
|
|
949
|
-
isDei2018orLater = any(doc.targetNamespace.startswith("http://xbrl.sec.gov/dei/") and doc.targetNamespace >= "http://xbrl.sec.gov/dei/2018"
|
|
950
|
-
for doc in modelXbrl.urlDocs.values() if doc.targetNamespace)
|
|
951
|
-
|
|
952
|
-
isRR = any(doc.targetNamespace.startswith("http://xbrl.sec.gov/rr/")
|
|
953
|
-
for doc in modelXbrl.urlDocs.values() if doc.targetNamespace)
|
|
954
|
-
isOEF = any(doc.targetNamespace.startswith("http://xbrl.sec.gov/oef/")
|
|
955
|
-
for doc in modelXbrl.urlDocs.values() if doc.targetNamespace)
|
|
956
|
-
isRRorOEF = isRR or isOEF
|
|
957
|
-
|
|
958
|
-
# seriesId 6.5.41
|
|
959
|
-
if submissionType in submissionTypesAllowingSeriesClasses and deiItems.get("EntityInvCompanyType") in invCompanyTypesAllowingSeriesClasses:
|
|
960
|
-
legalEntityAxis = modelXbrl.nameConcepts.get("LegalEntityAxis",())
|
|
961
|
-
if len(legalEntityAxis) > 0:
|
|
962
|
-
legalEntityAxisQname = legalEntityAxis[0].qname
|
|
963
|
-
if legalEntityAxisQname.namespaceURI.startswith("http://xbrl.sec.gov/dei/"):
|
|
964
|
-
legalEntityAxisRelationshipSet = modelXbrl.relationshipSet("XBRL-dimensions")
|
|
965
|
-
if val.params.get("rptIncludeAllSeriesFlag") in (True, "Yes", "yes", "Y", "y"):
|
|
966
|
-
seriesIds = val.params.get("newClass2.seriesIds", ())
|
|
967
|
-
else:
|
|
968
|
-
seriesIds = val.params.get("rptSeriesClassInfo.seriesIds", ())
|
|
969
|
-
for seriesId in sorted(set(seriesIds)): # series Ids are a hierarchy and need to be de-duplicated and ordered
|
|
970
|
-
seriesIdMemberName = seriesId + "Member"
|
|
971
|
-
seriesIdMember = None
|
|
972
|
-
for c in modelXbrl.nameConcepts.get(seriesIdMemberName, ()):
|
|
973
|
-
if c.type.isDomainItemType:
|
|
974
|
-
seriesIdMember = c
|
|
975
|
-
break
|
|
976
|
-
if seriesIdMember is None:
|
|
977
|
-
xsds = [doc for url, doc in modelXbrl.urlDocs.items() # all filer schemas
|
|
978
|
-
if doc.type == ModelDocument.Type.SCHEMA and
|
|
979
|
-
url not in disclosureSystem.standardTaxonomiesDict]
|
|
980
|
-
modelXbrl.warning("EFM.6.05.41.seriesIdMemberNotDeclared",
|
|
981
|
-
_("Submission type %(subType)s should have %(seriesIdMember)s declared as a domainItemType element."),
|
|
982
|
-
edgarCode="dq-0541-Series-Id-Member-Not-Declared",
|
|
983
|
-
modelObject=xsds, seriesIdMember=seriesIdMemberName, subType=submissionType)
|
|
984
|
-
elif not legalEntityAxisRelationshipSet.isRelated(legalEntityAxis[0],"descendant", seriesIdMember):
|
|
985
|
-
defLBs = [doc for url, doc in modelXbrl.urlDocs.items() # all filer def LBs
|
|
986
|
-
if doc.type == ModelDocument.Type.LINKBASE and
|
|
987
|
-
url not in disclosureSystem.standardTaxonomiesDict and
|
|
988
|
-
url.endswith("_def.xml")]
|
|
989
|
-
modelXbrl.warning("EFM.6.05.41.seriesIdMemberNotAxisMember",
|
|
990
|
-
_("Submission type %(subType)s should have %(seriesIdMember)s as a member of the Legal Entity Axis."),
|
|
991
|
-
edgarCode="dq-0541-Series-Id-Member-Not-Axis-Member",
|
|
992
|
-
modelObject=[seriesIdMember, defLBs], seriesIdMember=seriesIdMemberName, subType=submissionType)
|
|
993
|
-
elif not any(cntx.hasDimension(legalEntityAxisQname) and seriesIdMember == cntx.qnameDims[legalEntityAxisQname].member
|
|
994
|
-
for cntx in contextsWithNonNilFacts):
|
|
995
|
-
modelXbrl.warning("EFM.6.05.41.seriesIdMemberNotInContext",
|
|
996
|
-
_("Submission type %(subType)s should have a context with %(seriesIdMember)s as a member of the Legal Entity Axis."),
|
|
997
|
-
edgarCode="dq-0541-Series-Id-Member-Not-In-Context",
|
|
998
|
-
modelObject=(modelXbrl,seriesIdMember), seriesIdMember=seriesIdMemberName, subType=submissionType)
|
|
999
|
-
# seriesId 6.5.57 OEF Classes
|
|
1000
|
-
if submissionType in submissionTypesRequiringOefClasses and val.params.get("invCompanyType") in invCompanyTypesRequiringOefClasses:
|
|
1001
|
-
classAxis = modelXbrl.nameConcepts.get("ClassAxis",())
|
|
1002
|
-
if len(classAxis) > 0:
|
|
1003
|
-
classAxisQname = classAxis[0].qname
|
|
1004
|
-
if classAxisQname.namespaceURI in disclosureSystem.standardTaxonomiesDict:
|
|
1005
|
-
classAxisRelationshipSet = modelXbrl.modelXbrl.relationshipSet("XBRL-dimensions", "http://xbrl.sec.gov/oef/role/ClassOnly")
|
|
1006
|
-
if val.params.get("rptIncludeAllClassesFlag") in (True, "Yes", "yes", "Y", "y"):
|
|
1007
|
-
classIds = val.params.get("newClass2.classIds", ())
|
|
1008
|
-
else:
|
|
1009
|
-
classIds = val.params.get("rptSeriesClassInfo.classIds", ())
|
|
1010
|
-
for classId in sorted(set(classIds)): # series Ids are a hierarchy and need to be de-duplicated and ordered
|
|
1011
|
-
classIdMemberName = classId + "Member"
|
|
1012
|
-
classIdMember = None
|
|
1013
|
-
for c in modelXbrl.nameConcepts.get(classIdMemberName, ()):
|
|
1014
|
-
if c.type.isDomainItemType:
|
|
1015
|
-
classIdMember = c
|
|
1016
|
-
break
|
|
1017
|
-
if classIdMember is None:
|
|
1018
|
-
xsds = [doc for url, doc in modelXbrl.urlDocs.items() # all filer schemas
|
|
1019
|
-
if doc.type == ModelDocument.Type.SCHEMA and
|
|
1020
|
-
url not in disclosureSystem.standardTaxonomiesDict]
|
|
1021
|
-
modelXbrl.warning("EFM.6.05.57.classIdMemberNotDeclared",
|
|
1022
|
-
_("Submission type %(subType)s should have %(classIdMember)s declared as a domainItemType element."),
|
|
1023
|
-
edgarCode="dq-0557-Class-Id-Member-Not-Declared",
|
|
1024
|
-
modelObject=xsds, classIdMember=classIdMemberName, subType=submissionType)
|
|
1025
|
-
elif not classAxisRelationshipSet.isRelated(classAxis[0],"descendant", classIdMember):
|
|
1026
|
-
defLBs = [doc for url, doc in modelXbrl.urlDocs.items() # all filer def LBs
|
|
1027
|
-
if doc.type == ModelDocument.Type.LINKBASE and
|
|
1028
|
-
url not in disclosureSystem.standardTaxonomiesDict and
|
|
1029
|
-
url.endswith("_def.xml")]
|
|
1030
|
-
modelXbrl.warning("EFM.6.05.57.classIdMemberNotAxisMember",
|
|
1031
|
-
_("Submission type %(subType)s should have %(classIdMember)s as a member of the Class Axis."),
|
|
1032
|
-
edgarCode="dq-0557-Class-Id-Member-Not-Axis-Member",
|
|
1033
|
-
modelObject=[classIdMember, defLBs], classIdMember=classIdMemberName, subType=submissionType)
|
|
1034
|
-
elif not any(cntx.hasDimension(classAxisQname) and classIdMember == cntx.qnameDims[classAxisQname].member
|
|
1035
|
-
for cntx in contextsWithNonNilFacts):
|
|
1036
|
-
modelXbrl.warning("EFM.6.05.57.classIdMemberNotInContext",
|
|
1037
|
-
_("Submission type %(subType)s should have a context with %(classIdMember)s as a member of the Class Axis."),
|
|
1038
|
-
edgarCode="dq-0557-Class-Id-Member-Not-In-Context",
|
|
1039
|
-
modelObject=(modelXbrl,seriesIdMember), classIdMember=classIdMemberName, subType=submissionType)
|
|
1040
|
-
val.modelXbrl.profileActivity("... filer label and text checks", minTimeToShow=1.0)
|
|
1041
|
-
|
|
1042
|
-
if isEFM:
|
|
1043
|
-
if attachmentDocumentType and deiDocumentType is not None:
|
|
1044
|
-
if (deiDocumentType in ("2.01 SD",)) != (attachmentDocumentType == "EX-2.01"):
|
|
1045
|
-
modelXbrl.error("EFM.6.05.58.exhibitDocumentType",
|
|
1046
|
-
_("The value for dei:DocumentType, %(deiDocumentType)s, is not allowed for %(exhibitDocumentType)s attachments."),
|
|
1047
|
-
modelObject=documentTypeFact, contextID=documentTypeFactContextID, deiDocumentType=deiDocumentType, exhibitDocumentType=attachmentDocumentType,
|
|
1048
|
-
edgarCode="rxp-0558-Exhibit-Document-Type")
|
|
1049
|
-
elif (((deiDocumentType == "K SDR") != (attachmentDocumentType in ("EX-99.K SDR", "EX-99.K SDR.INS"))) or
|
|
1050
|
-
((deiDocumentType == "L SDR") != (attachmentDocumentType in ("EX-99.L SDR", "EX-99.L SDR.INS")))):
|
|
1051
|
-
modelXbrl.error("EFM.6.05.20.exhibitDocumentType",
|
|
1052
|
-
_("The value for dei:DocumentType, '%(deiDocumentType)s' is not allowed for %(exhibitDocumentType)s attachments."),
|
|
1053
|
-
modelObject=documentTypeFact, contextID=documentTypeFactContextID, deiDocumentType=deiDocumentType, exhibitDocumentType=attachmentDocumentType)
|
|
1054
|
-
|
|
1055
|
-
# Table driven validations
|
|
1056
|
-
def sevMessage(sev, messageKey=None, **kwargs):
|
|
1057
|
-
# skip these messages when loadedFromFtJson
|
|
1058
|
-
# Specific use case: EDGAR will not store the business address detail or send it to EFMS as part of BR4, no validation for BR6.
|
|
1059
|
-
if sev.get("skip-if-ft-json") == True:
|
|
1060
|
-
if hasattr(modelXbrl, 'loadedFromFtJson') and modelXbrl.loadedFromFtJson == True:
|
|
1061
|
-
return
|
|
1062
|
-
|
|
1063
|
-
logArgs = kwargs.copy()
|
|
1064
|
-
validation = deiValidations["validations"][sev["validation"]]
|
|
1065
|
-
severity = kwargs.get("severity", sev.get("severity", validation["severity"]))
|
|
1066
|
-
if "severity" not in logArgs:
|
|
1067
|
-
logArgs["severity"] = severity
|
|
1068
|
-
for validationParam, validationParamValue in validation.items():
|
|
1069
|
-
if validationParam not in ("message", "severity", "comment"):
|
|
1070
|
-
logArgs[validationParam] = validationParamValue
|
|
1071
|
-
severity = severity.upper()
|
|
1072
|
-
if severity == "WARNINGIFPRAGMATICELSEERROR":
|
|
1073
|
-
severity = "WARNING" if validateEFMpragmatic else "ERROR"
|
|
1074
|
-
if messageKey is None:
|
|
1075
|
-
messageKey = sev.get("message") or validation[kwargs.get("validationMessage", "message")]
|
|
1076
|
-
if messageKey is None:
|
|
1077
|
-
return # no message for this validation
|
|
1078
|
-
|
|
1079
|
-
# These store-db-actions are only done when validation fails
|
|
1080
|
-
for k, v in sev.get('store-db-on-validation-unsuccessful', {}).items():
|
|
1081
|
-
storeDbActions.setdefault(storeDbObject,{}).setdefault((),{})[k] = getStoreDBValue(k, v)
|
|
1082
|
-
|
|
1083
|
-
logArgs["severityVerb"] = (sev.get("severityVerb", validation.get("severityVerb")) or
|
|
1084
|
-
{"WARNING":"should","ERROR":"must"}[severity])
|
|
1085
|
-
for n, v in logArgs.items(): # clean up tag arguments
|
|
1086
|
-
if n.lower().endswith("tag"):
|
|
1087
|
-
if isinstance(v, list):
|
|
1088
|
-
logArgs[n] = "".join(v)
|
|
1089
|
-
if "efmSection" not in logArgs:
|
|
1090
|
-
logArgs["efmSection"] = sev.get("efm")
|
|
1091
|
-
if logArgs.get("efmSection"):
|
|
1092
|
-
efm = logArgs["efmSection"].split(".")
|
|
1093
|
-
logArgs["efmSection"] = ""
|
|
1094
|
-
logArgs["arelleCode"] = "EFM"
|
|
1095
|
-
for i, e in enumerate(efm):
|
|
1096
|
-
if i > 0 :
|
|
1097
|
-
if e.isnumeric(): # e.g. [6,5,2] -> "6.05.02"
|
|
1098
|
-
e = e.zfill(2)
|
|
1099
|
-
logArgs["efmSection"] += e
|
|
1100
|
-
logArgs["arelleCode"] += "." + e
|
|
1101
|
-
logArgs["edgarCode"] = messageKey # edgar code is the un-expanded key for message with {...}'s
|
|
1102
|
-
try:
|
|
1103
|
-
m = messageKeySectionPattern.match(messageKey or "")
|
|
1104
|
-
if m:
|
|
1105
|
-
keyAfterSection = m.group(2)
|
|
1106
|
-
else:
|
|
1107
|
-
keyAfterSection = ""
|
|
1108
|
-
arelleCode = "{arelleCode}.".format(**logArgs) + keyAfterSection.format(**logArgs) \
|
|
1109
|
-
.replace(",", "").replace(".","").replace(" ","") # replace commas in names embedded in message code portion
|
|
1110
|
-
if arelleCode.endswith("."):
|
|
1111
|
-
arelleCode = arelleCode[:-1]
|
|
1112
|
-
except KeyError as err:
|
|
1113
|
-
modelXbrl.error("arelle:loadDeiValidations",
|
|
1114
|
-
_("Missing field %(field)s from messageKey %(messageKey)s, validation %(validation)s."),
|
|
1115
|
-
field=err, messageKey=messageKey, validation=sev)
|
|
1116
|
-
return
|
|
1117
|
-
arelleCodeSections = arelleCode.split("-")
|
|
1118
|
-
if len(arelleCodeSections) > 1 and arelleCodeSections[1]:
|
|
1119
|
-
arelleCodeSections[1] = arelleCodeSections[1][0].lower() + arelleCodeSections[1][1:] # start with lowercase
|
|
1120
|
-
arelleCode = "".join(arelleCodeSections)
|
|
1121
|
-
axisKey = sev.get("axis","")
|
|
1122
|
-
axesValidations = deiValidations["axis-validations"][axisKey]
|
|
1123
|
-
logArgs["axis"] = " or ".join(axesValidations.get("names") or axesValidations.get("axes")) # names in ft-validations axes in dei-validations
|
|
1124
|
-
logArgs["member"] = " or ".join(axesValidations.get("members",()))
|
|
1125
|
-
for validationParam, validationParamValue in axesValidations.items():
|
|
1126
|
-
if validationParam not in ("axes", "members", "message", "comment"):
|
|
1127
|
-
logArgs[validationParam] = validationParamValue
|
|
1128
|
-
if "context" in logArgs:
|
|
1129
|
-
pass # custom content for context argument
|
|
1130
|
-
elif not isFeeTagging and "contextID" in logArgs:
|
|
1131
|
-
logArgs["context"] = f"context {logArgs['contextID']}"
|
|
1132
|
-
elif not axisKey:
|
|
1133
|
-
logArgs["context"] = "Required Context"
|
|
1134
|
-
elif axisKey == "c":
|
|
1135
|
-
if not commonSharesClassMembers or len(commonSharesClassMembers) == 1:
|
|
1136
|
-
logArgs["context"] = "Required Context (one class of stock axis)"
|
|
1137
|
-
else:
|
|
1138
|
-
logArgs["context"] = "context corresponding to the Required Context with at least one of {}".format(
|
|
1139
|
-
logArgs["axis"])
|
|
1140
|
-
else:
|
|
1141
|
-
logArgs["context"] = "context with {} and {}".format(
|
|
1142
|
-
logArgs["axis"], logArgs["member"])
|
|
1143
|
-
pf = None # prototype Fact for typing and unit display
|
|
1144
|
-
if "modelObject" in logArgs:
|
|
1145
|
-
modelObjects = logArgs["modelObject"]
|
|
1146
|
-
for f in modelObjects if isinstance(modelObjects, (tuple, set, list)) else (modelObjects,):
|
|
1147
|
-
if isinstance(f, ModelFact):
|
|
1148
|
-
pf = f
|
|
1149
|
-
if "contextID" not in logArgs:
|
|
1150
|
-
logArgs["contextID"] = f.contextID
|
|
1151
|
-
break
|
|
1152
|
-
if logArgs.get("modelObject") is None: # no modelObject, default to the entry document
|
|
1153
|
-
logArgs["modelObject"] = modelXbrl
|
|
1154
|
-
for n, v in logArgs.items(): # clean up values arguments
|
|
1155
|
-
if "value" in n.lower():
|
|
1156
|
-
if isinstance(v, set):
|
|
1157
|
-
v = sorted(v)
|
|
1158
|
-
if isinstance(v, list):
|
|
1159
|
-
if len(v) == 1:
|
|
1160
|
-
logArgs[n] = sevMessageArgValue(v[0], pf)
|
|
1161
|
-
elif len(v) == 2 and v[0] == "!not!":
|
|
1162
|
-
val = "not " if "severityVerb" not in sev else ""
|
|
1163
|
-
logArgs[n] = f"{val}{sevMessageArgValue(v[1], pf)}"
|
|
1164
|
-
elif len(v) > 2 and v[0] == "!not!":
|
|
1165
|
-
logArgs[n] = f"not any of ( {', '.join(sevMessageArgValue(_v, pf) for _v in v[1:])} )"
|
|
1166
|
-
else:
|
|
1167
|
-
logArgs[n] = f"one of {', '.join(sevMessageArgValue(_v, pf) for _v in v)}"
|
|
1168
|
-
elif isinstance(v, re.Pattern):
|
|
1169
|
-
logArgs[n] = f"pattern {v.pattern}"
|
|
1170
|
-
else:
|
|
1171
|
-
logArgs[n] = sevMessageArgValue(v, pf)
|
|
1172
|
-
if "subType" in logArgs: # provide item 5.03 friendly format for submission type
|
|
1173
|
-
logArgs["subType"] = logArgs["subType"].replace("+5.03", " (with item 5.03)")
|
|
1174
|
-
message = deiValidations["messages"][messageKey]
|
|
1175
|
-
if "{msgCoda}" in message:
|
|
1176
|
-
msgCoda = logArgs["msgCoda"] = sev.get("msgCoda", logArgs.get("msgCoda", ""))
|
|
1177
|
-
# if message ends with period and msgCoda doesn't start a new sentence, get rid of period and string on the words in same sentence
|
|
1178
|
-
if msgCoda and msgCoda[0].islower() and ".{msgCoda}" in message:
|
|
1179
|
-
message = message.replace(".{msgCoda}", " " + msgCoda)
|
|
1180
|
-
else:
|
|
1181
|
-
message = message.replace("{msgCoda}", msgCoda)
|
|
1182
|
-
modelXbrl.log(severity, arelleCode, logMsg(message), **logArgs)
|
|
1183
|
-
|
|
1184
|
-
sevs = deiValidations["sub-type-element-validations"]
|
|
1185
|
-
sevCoveredFacts = set()
|
|
1186
|
-
deiCAxes = deiValidations["axis-validations"].get("c",EMPTY_DICT).get("axes",EMPTY_LIST)
|
|
1187
|
-
# Its possible that extension concepts could have prefixes that match `cef` or `vip`
|
|
1188
|
-
# and EFM.6.5.55 or EFM.6.5.56 validations so we exclude all extension namespaces by
|
|
1189
|
-
# filtering out prefix namespace combos where the namespace matches known SEC domains.
|
|
1190
|
-
deiDefaultPrefixedNamespaces = {
|
|
1191
|
-
prefix: namespace for prefix, namespace in deiValidations["prefixed-namespaces"].items() if namespace in disclosureSystem.standardTaxonomiesDict
|
|
1192
|
-
}
|
|
1193
|
-
messageRuleAxesOrdering = deiValidations.get("message-rule-axes-ordering", ())
|
|
1194
|
-
messageRuleAxesDefaults = []
|
|
1195
|
-
for i, axisName in enumerate(messageRuleAxesOrdering):
|
|
1196
|
-
messageRuleAxesDefaults.append("") # default to string
|
|
1197
|
-
for axisConcept in modelXbrl.nameConcepts[axisName]:
|
|
1198
|
-
if axisConcept.isTypedDimension and axisConcept.typedDomainElement.isNumeric:
|
|
1199
|
-
messageRuleAxesDefaults[i] = 0 # override with numeric
|
|
1200
|
-
|
|
1201
|
-
class HeaderValuePsuedoFact:
|
|
1202
|
-
def __init__(self, value):
|
|
1203
|
-
self.xValue = value
|
|
1204
|
-
|
|
1205
|
-
def __repr__(self):
|
|
1206
|
-
return str(self.xValue)
|
|
1207
|
-
|
|
1208
|
-
# called with sev, returns iterator of sev facts for names and axes matching
|
|
1209
|
-
# called with sev and name, returns single fact for name matching axesMembers (if any)
|
|
1210
|
-
def sevFacts(sev=None, name=None, otherFact=None, matchDims=None, requiredContext=False, axisKey=None, deduplicate=False, whereKey=None, fallback=None, sevCovered=True):
|
|
1211
|
-
if deduplicate:
|
|
1212
|
-
previouslyYieldedFacts = set()
|
|
1213
|
-
def notdup(f):
|
|
1214
|
-
dedupKey = (f.qname, f.context.contextDimAwareHash, f.xmlLang if f.isMultiLanguage else None)
|
|
1215
|
-
if dedupKey not in previouslyYieldedFacts:
|
|
1216
|
-
previouslyYieldedFacts.add(dedupKey)
|
|
1217
|
-
return True
|
|
1218
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1219
|
-
return False
|
|
1220
|
-
if isinstance(sev, int):
|
|
1221
|
-
sev = sevs[sev] # convert index to sev object
|
|
1222
|
-
where = sev.get(whereKey, EMPTY_DICT)
|
|
1223
|
-
if isinstance(name, list):
|
|
1224
|
-
names = name
|
|
1225
|
-
elif name:
|
|
1226
|
-
names = (name,)
|
|
1227
|
-
else:
|
|
1228
|
-
names = sev.get("xbrl-names", ())
|
|
1229
|
-
langPattern = sev.get("langPattern")
|
|
1230
|
-
if axisKey is None:
|
|
1231
|
-
axisKey = sev.get("axis","")
|
|
1232
|
-
elif axisKey != sev.get("axis",""):
|
|
1233
|
-
otherFact = None # block other fact comparison when axis key is for a different axis binding
|
|
1234
|
-
axesValidations = deiValidations["axis-validations"][axisKey]
|
|
1235
|
-
axes = axesValidations["axes"]
|
|
1236
|
-
excludesAxes = "!not!" in axes
|
|
1237
|
-
axisOperator = axesValidations.get("axes-operator", "any")
|
|
1238
|
-
matchCubes = axesValidations.get("cubes")
|
|
1239
|
-
axesQNs = []
|
|
1240
|
-
for axis in axes:
|
|
1241
|
-
if axis is None:
|
|
1242
|
-
axesQNs.append(None)
|
|
1243
|
-
elif axis.startswith("!std!:"):
|
|
1244
|
-
for c in modelXbrl.nameConcepts.get(axis[6:],()):
|
|
1245
|
-
if c.qname.namespaceURI in disclosureSystem.standardTaxonomiesDict:
|
|
1246
|
-
axesQNs.append(c.qname)
|
|
1247
|
-
elif axis.startswith("*:"):
|
|
1248
|
-
for c in modelXbrl.nameConcepts.get(axis[2:],()):
|
|
1249
|
-
axesQNs.append(c.qname)
|
|
1250
|
-
elif axis != "!not!":
|
|
1251
|
-
qn = qname(axis, deiDefaultPrefixedNamespaces)
|
|
1252
|
-
if qn is not None:
|
|
1253
|
-
axesQNs.append(qn)
|
|
1254
|
-
|
|
1255
|
-
members = axesValidations.get("members")
|
|
1256
|
-
|
|
1257
|
-
def whereConditionIsFalse(wValue, wCond):
|
|
1258
|
-
wOp = wCond[0]
|
|
1259
|
-
if ((wOp == "~" and not re.search(wCond[1], str(wValue))) or
|
|
1260
|
-
(wOp == "~*" and not re.search(wCond[1], str(wValue), re.IGNORECASE)) or
|
|
1261
|
-
(wOp == "!~" and re.search(wCond[1], str(wValue))) or
|
|
1262
|
-
(wOp == "!~*" and re.search(wCond[1], str(wValue))) or
|
|
1263
|
-
((wOp not in {"~", "~*", "!~", "!~*", "less than or equal"}) and
|
|
1264
|
-
(wValue not in wCond) == ("!not!" not in wCond)) or
|
|
1265
|
-
((wValue != "absent" and wOp == "less than or equal" and (wValue > wCond[1]) == ("!not!" not in wCond)))
|
|
1266
|
-
):
|
|
1267
|
-
return True
|
|
1268
|
-
return False
|
|
1269
|
-
|
|
1270
|
-
def comparison(sev, otherFact):
|
|
1271
|
-
names = sev.get("comparison-names")
|
|
1272
|
-
refNames = sev.get("comparison-ref-names")
|
|
1273
|
-
comparisonOperator = sev.get("comparison-operator")
|
|
1274
|
-
tolerance = sev.get("comparison-tolerance", 0)
|
|
1275
|
-
items1 = []
|
|
1276
|
-
items2 = []
|
|
1277
|
-
for name1 in names:
|
|
1278
|
-
for f in sevFacts(sev, name1, otherFact=otherFact, deduplicate=True):
|
|
1279
|
-
items1.append(f)
|
|
1280
|
-
for name2 in refNames:
|
|
1281
|
-
for g in sevFacts(sev, name2, otherFact=otherFact, deduplicate=True):
|
|
1282
|
-
items2.append(g)
|
|
1283
|
-
item1Vals = [f.xValue if f is not None else 0 for f in items1]
|
|
1284
|
-
item2Vals = [g.xValue if g is not None else 0 for g in items2]
|
|
1285
|
-
sum1 = sum(item1Vals)
|
|
1286
|
-
sum2 = sum(item2Vals)
|
|
1287
|
-
if ((comparisonOperator == "equal" and abs(sum1 - sum2) <= tolerance) or
|
|
1288
|
-
(comparisonOperator == "not-equal" and abs(sum1 - sum2) >= tolerance) or
|
|
1289
|
-
(comparisonOperator == "less than or equal" and (sum1 - sum2) <= tolerance) or
|
|
1290
|
-
(comparisonOperator == "less than" and (sum1 - sum2) < tolerance) or
|
|
1291
|
-
(comparisonOperator == "greater than or equal" and (sum1 - sum2) > tolerance) or
|
|
1292
|
-
(comparisonOperator == "greater" and (sum1 - sum2) > tolerance)):
|
|
1293
|
-
return True
|
|
1294
|
-
return False
|
|
1295
|
-
|
|
1296
|
-
for name in names:
|
|
1297
|
-
yielded = False
|
|
1298
|
-
skipF = False
|
|
1299
|
-
for f in (modelXbrl.factsByQname.get(qname(name, deiDefaultPrefixedNamespaces)) or
|
|
1300
|
-
(NONE_SET if fallback else EMPTY_SET)):
|
|
1301
|
-
if f is not None: # not fallback
|
|
1302
|
-
if langPattern is not None and not langPattern.match(f.xmlLang):
|
|
1303
|
-
continue
|
|
1304
|
-
context = f.context
|
|
1305
|
-
if (f is None) or (context is not None and f.xValid >= VALID and not f.isNil):
|
|
1306
|
-
skipF = False
|
|
1307
|
-
for wName, wCond in where.items():
|
|
1308
|
-
if wName.startswith("function:"):
|
|
1309
|
-
# Need to name functions to make it visible to eval scope
|
|
1310
|
-
getNumberofDaysLate
|
|
1311
|
-
functionName = wName[9:]
|
|
1312
|
-
evalString, functionArgs = getEvalFunctionStringAndArgs(sev, functionName)
|
|
1313
|
-
wValue = eval(evalString) if evalString else 0
|
|
1314
|
-
elif wName == "comparison":
|
|
1315
|
-
wValue = comparison(sev, f)
|
|
1316
|
-
elif " axisSum " in wName:
|
|
1317
|
-
_wName, _sep, _axisKey = wName.partition(" axisSum ")
|
|
1318
|
-
items = []
|
|
1319
|
-
for fw in sevFacts(sev, _wName, axisKey=_axisKey, deduplicate=True, sevCovered=False):
|
|
1320
|
-
items.append(fw)
|
|
1321
|
-
itemVals = [g.xValue if g is not None else 0 for g in items]
|
|
1322
|
-
wValue = sum(itemVals)
|
|
1323
|
-
else:
|
|
1324
|
-
fw = sevFact(sev, wName, f, sevCovered=False)
|
|
1325
|
-
wValue = "absent" if fw is None else fw.xValue
|
|
1326
|
-
if "!anotherLine!" in wCond: # allow axis axisKey for !anotherLine!
|
|
1327
|
-
if " axis " in wName:
|
|
1328
|
-
_wName, _sep, _axisKey = wName.partition(" axis ")
|
|
1329
|
-
else:
|
|
1330
|
-
_wName = wName; _axisKey = axisKey
|
|
1331
|
-
otherLinesConditionFalse = list(whereConditionIsFalse(fw.xValue, wCond)
|
|
1332
|
-
for fw in sevFacts(sev, _wName, axisKey=_axisKey, sevCovered=False)
|
|
1333
|
-
if fw.context.dimsHash != (f.context.dimsHash if f is not None else None)
|
|
1334
|
-
)
|
|
1335
|
-
if ( (otherLinesConditionFalse and all(otherLinesConditionFalse)) or
|
|
1336
|
-
( otherLinesConditionFalse and ("!not!" in wCond) == (any(otherLinesConditionFalse)) ) or
|
|
1337
|
-
( (len(otherLinesConditionFalse) == 0) and ("absent" in wCond) == ("!not!" in wCond) )
|
|
1338
|
-
):
|
|
1339
|
-
skipF = True
|
|
1340
|
-
break
|
|
1341
|
-
elif wName == "period":
|
|
1342
|
-
if ("required-context" in wCond and deiDocumentType and
|
|
1343
|
-
context.isPeriodEqualTo(documentTypeFact.context) == ("!not!" in wCond)):
|
|
1344
|
-
skipF = True
|
|
1345
|
-
break
|
|
1346
|
-
else:
|
|
1347
|
-
if whereConditionIsFalse(wValue, wCond):
|
|
1348
|
-
skipF = True
|
|
1349
|
-
break
|
|
1350
|
-
if skipF:
|
|
1351
|
-
continue # skip this fact
|
|
1352
|
-
if f is None:
|
|
1353
|
-
yielded = True
|
|
1354
|
-
yield f # fallback
|
|
1355
|
-
elif otherFact is not None:
|
|
1356
|
-
if context.isEqualTo(otherFact.context):
|
|
1357
|
-
if not deduplicate or notdup(f):
|
|
1358
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1359
|
-
yielded = True
|
|
1360
|
-
yield f
|
|
1361
|
-
elif requiredContext and deiDocumentType:
|
|
1362
|
-
if ((context.isInstantPeriod and not context.qnameDims) or
|
|
1363
|
-
(context.isStartEndPeriod and context.isEqualTo(documentTypeFact.context))):
|
|
1364
|
-
if not deduplicate or notdup(f):
|
|
1365
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1366
|
-
yielded = True
|
|
1367
|
-
yield f
|
|
1368
|
-
elif not context.qnameDims and not axes:
|
|
1369
|
-
if not deduplicate or notdup(f):
|
|
1370
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1371
|
-
yielded = True
|
|
1372
|
-
yield f
|
|
1373
|
-
elif axisOperator == "any":
|
|
1374
|
-
hasDimMatch = False
|
|
1375
|
-
for dim in context.qnameDims.values():
|
|
1376
|
-
if dim.dimensionQname in axesQNs:
|
|
1377
|
-
if (not members or
|
|
1378
|
-
dim.memberQname.localName in members):
|
|
1379
|
-
hasDimMatch = True
|
|
1380
|
-
if not deduplicate or notdup(f):
|
|
1381
|
-
if not excludesAxes:
|
|
1382
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1383
|
-
yielded = True
|
|
1384
|
-
yield f
|
|
1385
|
-
break
|
|
1386
|
-
if not context.qnameDims and None in axesQNs:
|
|
1387
|
-
hasDimMatch = True
|
|
1388
|
-
if not deduplicate or notdup(f):
|
|
1389
|
-
if not excludesAxes:
|
|
1390
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1391
|
-
yielded = True
|
|
1392
|
-
yield f
|
|
1393
|
-
if excludesAxes and not hasDimMatch:
|
|
1394
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1395
|
-
yielded = True
|
|
1396
|
-
yield f
|
|
1397
|
-
|
|
1398
|
-
elif axisOperator == "all" and (
|
|
1399
|
-
len(context.qnameDims) == len(axes) and
|
|
1400
|
-
len(axes) == len(axesQNs)) and all(
|
|
1401
|
-
context.hasDimension(qn) and
|
|
1402
|
-
(not matchDims or qn not in matchDims or context.qnameDims[qn].isEqualTo(matchDims[qn])) and
|
|
1403
|
-
(not matchCubes or any(modelXbrl.relationshipSet("XBRL-dimensions",elr).isRelated(qn, "descendant", context.dimValue(qn).member) for elr in matchCubes))
|
|
1404
|
-
for qn in axesQNs): # no extra dimensions
|
|
1405
|
-
if not deduplicate or notdup(f):
|
|
1406
|
-
if not excludesAxes:
|
|
1407
|
-
if sevCovered: sevCoveredFacts.add(f)
|
|
1408
|
-
yielded = True
|
|
1409
|
-
yield f
|
|
1410
|
-
if name.startswith("header:") and name[7:] in val.params:
|
|
1411
|
-
yielded = True
|
|
1412
|
-
yield HeaderValuePsuedoFact(val.params[name[7:]])
|
|
1413
|
-
if not yielded and fallback and not skipF:
|
|
1414
|
-
yield None
|
|
1415
|
-
|
|
1416
|
-
# return first of matching facts or None
|
|
1417
|
-
def sevFact(sev=None, name=None, otherFact=None, requiredContext=False, axisKey=None, whereKey=None, sevCovered=True):
|
|
1418
|
-
if isinstance(name, list):
|
|
1419
|
-
for _name in name:
|
|
1420
|
-
f = sevFact(sev, _name, otherFact, requiredContext, axisKey=axisKey, whereKey=whereKey, sevCovered=sevCovered)
|
|
1421
|
-
if f is not None:
|
|
1422
|
-
return f
|
|
1423
|
-
elif isinstance(name, dict): # dict has name, where-key, and optional axis (else inherits axisKey)
|
|
1424
|
-
if "name" in name and "where-key" in name:
|
|
1425
|
-
return sevFact(sev, name["name"], otherFact, requiredContext, name.get("axis",axisKey), name["where-key"], sevCovered)
|
|
1426
|
-
else:
|
|
1427
|
-
for f in sevFacts(sev, name, otherFact, requiredContext, axisKey=axisKey, whereKey=whereKey, sevCovered=sevCovered):
|
|
1428
|
-
return f
|
|
1429
|
-
return None
|
|
1430
|
-
|
|
1431
|
-
def axesValsKey(axisKey, cntx):
|
|
1432
|
-
axesValidations = deiValidations["axis-validations"][axisKey]
|
|
1433
|
-
if ("required-context-period" in axesValidations and deiDocumentType and
|
|
1434
|
-
cntx.isPeriodEqualTo(documentTypeFact.context) != axesValidations["required-context-period"]):
|
|
1435
|
-
return None # context period doesn't match required context
|
|
1436
|
-
axesQNs = []
|
|
1437
|
-
for axis in axesValidations["axes"]:
|
|
1438
|
-
if axis is not None and axis != "!not!":
|
|
1439
|
-
if axis.startswith("!std!:"):
|
|
1440
|
-
for c in modelXbrl.nameConcepts.get(axis[6:],()):
|
|
1441
|
-
if c.qname.namespaceURI in disclosureSystem.standardTaxonomiesDict:
|
|
1442
|
-
axesQNs.append(c.qname)
|
|
1443
|
-
elif axis.startswith("*:"):
|
|
1444
|
-
for c in modelXbrl.nameConcepts.get(axis[2:],()):
|
|
1445
|
-
axesQNs.append(c.qname)
|
|
1446
|
-
else:
|
|
1447
|
-
qn = qname(axis, deiDefaultPrefixedNamespaces)
|
|
1448
|
-
if qn is not None:
|
|
1449
|
-
axesQNs.append(qn)
|
|
1450
|
-
members = axesValidations.get("members")
|
|
1451
|
-
cubes = axesValidations.get("cubes")
|
|
1452
|
-
presentAxisQN = [axisQN for axisQN in axesQNs if axisQN in cntx.qnameDims]
|
|
1453
|
-
if len(axesQNs) == len(cntx.qnameDims):
|
|
1454
|
-
if len(axesQNs) == 0:
|
|
1455
|
-
return ()
|
|
1456
|
-
if all(axisQN in cntx.qnameDims and (not cubes or (any(modelXbrl.relationshipSet("XBRL-dimensions",elr).isRelated(axisQN, "descendant", cntx.dimMemberQname(axisQN)) for elr in cubes)))
|
|
1457
|
-
for axisQN in axesQNs
|
|
1458
|
-
if (not members or cntx.dimMemberQname(axisQN).localName in members)):
|
|
1459
|
-
return tuple(
|
|
1460
|
-
dim.typedMember.xValue if dim.isTyped else dim.memberQname.localName
|
|
1461
|
-
for axisQN in axesQNs
|
|
1462
|
-
for dim in (cntx.qnameDims[axisQN],))
|
|
1463
|
-
elif presentAxisQN:
|
|
1464
|
-
return tuple(
|
|
1465
|
-
dim.typedMember.xValue if dim.isTyped else dim.memberQname.localName
|
|
1466
|
-
for axisQN in presentAxisQN
|
|
1467
|
-
for dim in (cntx.qnameDims[axisQN],))
|
|
1468
|
-
return None # context doesn't match expected dimensions
|
|
1469
|
-
|
|
1470
|
-
def ftContext(axisKey, axesValsOrF):
|
|
1471
|
-
axesValidations = deiValidations["axis-validations"][axisKey]
|
|
1472
|
-
axes = axesValidations["axes"]
|
|
1473
|
-
c = []
|
|
1474
|
-
if isinstance(axesValsOrF,tuple):
|
|
1475
|
-
axesVals = axesValsOrF
|
|
1476
|
-
elif isinstance(axesValsOrF, ModelFact): # axesValsOrF is a fact
|
|
1477
|
-
if not isFeeTagging:
|
|
1478
|
-
return axesValsOrF.contextID
|
|
1479
|
-
axesVals = axesValsKey(axisKey, axesValsOrF.context)
|
|
1480
|
-
axes = [axisQN for axisQN in axes if qname(axisQN, deiDefaultPrefixedNamespaces) in axesValsOrF.context.qnameDims]
|
|
1481
|
-
else:
|
|
1482
|
-
axesVals = None
|
|
1483
|
-
if len(axes) == 0:
|
|
1484
|
-
return "Submission / Fees Summary"
|
|
1485
|
-
if axesVals:
|
|
1486
|
-
try:
|
|
1487
|
-
for i, name in enumerate(axes):
|
|
1488
|
-
if name is None:
|
|
1489
|
-
if (c): c[-1] += ","
|
|
1490
|
-
c.append("Submission / Fees Summary")
|
|
1491
|
-
else:
|
|
1492
|
-
axisConcepts = modelXbrl.nameConcepts.get(name.rpartition(":")[2], ())
|
|
1493
|
-
if axisConcepts:
|
|
1494
|
-
axisConcept = axisConcepts[0]
|
|
1495
|
-
if (c): c[-1] += ","
|
|
1496
|
-
c.append(axisConcept.label(XbrlConst.terseLabel))
|
|
1497
|
-
c.append(str(axesVals[i]))
|
|
1498
|
-
for f in sorted(modelXbrl.factsByDimMemQname(axisConcept.qname, str(axesVals[i])),
|
|
1499
|
-
key=lambda f:f.qname.localName):
|
|
1500
|
-
if f.qname.localName.endswith("Flg") and "Rule" in f.qname.localName and f.xValue == True:
|
|
1501
|
-
c[-1] += ","
|
|
1502
|
-
c.append(f.concept.label(XbrlConst.terseLabel))
|
|
1503
|
-
except IndexError: # variable expression for dimension arguments
|
|
1504
|
-
c = f"Axes {' or '.join(axesValidations.get('names') or axesValidations.get('axes'))} values {axesVals}"
|
|
1505
|
-
return " ".join(c or ["Submission / Fees Summary"])
|
|
1506
|
-
|
|
1507
|
-
def ftName(factOrName):
|
|
1508
|
-
if isinstance(factOrName, list):
|
|
1509
|
-
return ", ".join(ftName(n) for n in factOrName)
|
|
1510
|
-
if isinstance(factOrName, ModelFact):
|
|
1511
|
-
return str(factOrName.concept.qname)
|
|
1512
|
-
if isinstance(factOrName, str): # name of dei or ffd concept
|
|
1513
|
-
#if factOrName.startswith("ffd:"):
|
|
1514
|
-
# return factOrName[4:]
|
|
1515
|
-
return factOrName
|
|
1516
|
-
return "(none)"
|
|
1517
|
-
|
|
1518
|
-
def ftLabel(factOrName):
|
|
1519
|
-
if isinstance(factOrName, list):
|
|
1520
|
-
return ", ".join(ftName(n) for n in factOrName)
|
|
1521
|
-
if isinstance(factOrName, ModelFact):
|
|
1522
|
-
return factOrName.concept.label(XbrlConst.terseLabel)
|
|
1523
|
-
if isinstance(factOrName, str): # name of dei or ffd concept
|
|
1524
|
-
if factOrName.startswith("header:"):
|
|
1525
|
-
return factOrName[7:]
|
|
1526
|
-
concepts = modelXbrl.nameConcepts.get(factOrName.rpartition(":")[2], ())
|
|
1527
|
-
if concepts:
|
|
1528
|
-
return concepts[0].label(XbrlConst.terseLabel)
|
|
1529
|
-
return "(none)"
|
|
1530
|
-
|
|
1531
|
-
def isADR(f):
|
|
1532
|
-
return f is not None and f.context is not None and (
|
|
1533
|
-
any(d.dimensionQname.localName in deiValidations["axis-validations"]["c"]["axes"]
|
|
1534
|
-
and d.memberQname == deiADRmember
|
|
1535
|
-
for d in f.context.qnameDims.values()))
|
|
1536
|
-
|
|
1537
|
-
def getStoreDBValue(key, value, otherFact=None):
|
|
1538
|
-
if type(value) is dict:
|
|
1539
|
-
if "subtract" in value:
|
|
1540
|
-
items = []
|
|
1541
|
-
for name in value.get('xbrl-names', []):
|
|
1542
|
-
f = sevFact(value, name, otherFact=otherFact, whereKey="where")
|
|
1543
|
-
if f is not None:
|
|
1544
|
-
items.append(f.xValue)
|
|
1545
|
-
else:
|
|
1546
|
-
items.append(0)
|
|
1547
|
-
for i, subtract in enumerate(value.get("subtract", [])):
|
|
1548
|
-
if subtract:
|
|
1549
|
-
items[i] =- items[i]
|
|
1550
|
-
result = str(max(sum(items), 0)) # non-negative values only
|
|
1551
|
-
return result
|
|
1552
|
-
elif "calculateDaysLate" in value:
|
|
1553
|
-
return getNumberofDaysLate(otherFact.xValue)
|
|
1554
|
-
# this will get the first matching fact
|
|
1555
|
-
f = sevFact(value, otherFact=otherFact, whereKey="where")
|
|
1556
|
-
if f is not None:
|
|
1557
|
-
if ftName(f) in deiValidations['form-fields']:
|
|
1558
|
-
return deiValidations['form-mapping'].get(f.value, f.value)
|
|
1559
|
-
return f.value
|
|
1560
|
-
elif key in deiValidations.get('form-fields', EMPTY_DICT):
|
|
1561
|
-
return deiValidations['form-mapping'].get(value, value)
|
|
1562
|
-
return value
|
|
1563
|
-
|
|
1564
|
-
def getNumberofDaysLate(fiscalYearEnd, lateAfter=90):
|
|
1565
|
-
dueDate = fiscalYearEnd + datetime.timedelta(days=lateAfter)
|
|
1566
|
-
# if due date falls on a weekend or holiday the due date will be the next business day
|
|
1567
|
-
# Monday = 0, Sunday = 6
|
|
1568
|
-
while dueDate.weekday() > 4 or dueDate in upcomingSECHolidays:
|
|
1569
|
-
dueDate += datetime.timedelta(days=1)
|
|
1570
|
-
|
|
1571
|
-
return max((datetimeNowAtSEC - dueDate).days, 0)
|
|
1572
|
-
|
|
1573
|
-
unexpectedDeiNameEfmSects = defaultdict(set) # name and sev(s)
|
|
1574
|
-
expectedDeiNames = defaultdict(set)
|
|
1575
|
-
coverVisibleQNames = {} # true if error, false if warning when not visible
|
|
1576
|
-
unexpectedEloParams = set()
|
|
1577
|
-
expectedEloParams = set()
|
|
1578
|
-
storeDbObjectFacts = defaultdict(dict)
|
|
1579
|
-
storeDbActions = {}
|
|
1580
|
-
eloValueFactNames = set(n
|
|
1581
|
-
for sev in sevs
|
|
1582
|
-
if "store-db-name" in sev and "subTypeSet" in sev
|
|
1583
|
-
for n in sev.get("xbrl-names", ())) # fact names producing elo values
|
|
1584
|
-
missingReqInlineTag = False
|
|
1585
|
-
reportDate = val.params.get("periodOfReport")
|
|
1586
|
-
if reportDate:
|
|
1587
|
-
reportDate = "{2}-{0}-{1}".format(*str(reportDate).split('-')) # mm-dd-yyyy
|
|
1588
|
-
elif documentPeriodEndDate:
|
|
1589
|
-
reportDate = str(documentPeriodEndDate)
|
|
1590
|
-
elif val.requiredContext is not None:
|
|
1591
|
-
reportDate = str(XmlUtil.dateunionValue(val.requiredContext.endDatetime, subtractOneDay=True))
|
|
1592
|
-
for sevIndex, sev in enumerate(sevs):
|
|
1593
|
-
subTypes = sev.get("subTypeSet", EMPTY_SET) # compiled set of sub-types
|
|
1594
|
-
subTypesPattern = sev.get("subTypesPattern")
|
|
1595
|
-
names = sev.get("xbrl-names", ())
|
|
1596
|
-
eloName = sev.get("elo-name")
|
|
1597
|
-
storeDbName = sev.get("store-db-name")
|
|
1598
|
-
storeDbObject = sev.get("store-db-object")
|
|
1599
|
-
storeDbAction = sev.get("store-db-action")
|
|
1600
|
-
storeDbInnerTextTruncate = sev.get("store-db-inner-text-truncate")
|
|
1601
|
-
efmSection = sev.get("efm")
|
|
1602
|
-
validation = sev.get("validation")
|
|
1603
|
-
checkAfter = sev.get("check-after")
|
|
1604
|
-
bindIfAbsent = sev.get("bind-if-absent")
|
|
1605
|
-
axisKey = sev.get("axis","")
|
|
1606
|
-
value = sev.get("value")
|
|
1607
|
-
isCoverVisible = {"cover":False, "COVER":True, "dei": None, None: None
|
|
1608
|
-
}[sev.get("dei/cover")]
|
|
1609
|
-
referenceTag = sev.get("references")
|
|
1610
|
-
referenceValue = sev.get("reference-value")
|
|
1611
|
-
if checkAfter and reportDate and checkAfter >= reportDate:
|
|
1612
|
-
continue
|
|
1613
|
-
subFormTypesCheck = {submissionType, "{}§{}".format(submissionType, deiDocumentType)}
|
|
1614
|
-
if (subTypes not in ({"all"}, {"n/a"})
|
|
1615
|
-
and (subFormTypesCheck.isdisjoint(subTypes) ^ ("!not!" in subTypes))
|
|
1616
|
-
and (not subTypesPattern or not subTypesPattern.match(submissionType))):
|
|
1617
|
-
if validation not in (None, "fany"): # don't process name for sev's which only store-db-field
|
|
1618
|
-
for name in names:
|
|
1619
|
-
if name.endswith(":*") and validation == "(supported-taxonomy)": # taxonomy-prefix filter
|
|
1620
|
-
txPrefix = name[:-2]
|
|
1621
|
-
ns = deiDefaultPrefixedNamespaces.get(txPrefix)
|
|
1622
|
-
if ns:
|
|
1623
|
-
unexpectedFacts = set()
|
|
1624
|
-
for qn, facts in modelXbrl.factsByQname.items():
|
|
1625
|
-
if qn.namespaceURI == ns:
|
|
1626
|
-
unexpectedFacts |= facts
|
|
1627
|
-
if unexpectedFacts:
|
|
1628
|
-
sevMessage(sev, subType=submissionType, modelObject=unexpectedFacts, taxonomy=txPrefix)
|
|
1629
|
-
try:
|
|
1630
|
-
if sevFact(sev, name, sevCovered=False) is not None:
|
|
1631
|
-
unexpectedDeiNameEfmSects[name,axisKey].add(sevIndex)
|
|
1632
|
-
except Exception as ex:
|
|
1633
|
-
print(ex)
|
|
1634
|
-
if eloName:
|
|
1635
|
-
unexpectedEloParams.add(eloName)
|
|
1636
|
-
continue
|
|
1637
|
-
# name is expected for this form
|
|
1638
|
-
if validation is not None and not validation.startswith("fdep") and subTypes != "n/a": # don't expect name for fdep validations or sev's which only store-db-field
|
|
1639
|
-
for name in names:
|
|
1640
|
-
expectedDeiNames[name,axisKey].add(sevIndex)
|
|
1641
|
-
if isCoverVisible is not None:
|
|
1642
|
-
coverVisibleQNames[qname(name, deiDefaultPrefixedNamespaces)] = isCoverVisible
|
|
1643
|
-
# last validation for unexpected items which were not bound to a validation for submission form type
|
|
1644
|
-
if validation in ("(blank)", "(blank-error)"):
|
|
1645
|
-
includeNames = sev.get("include-xbrl-names")
|
|
1646
|
-
excludeNames = sev.get("exclude-xbrl-names")
|
|
1647
|
-
for nameAxisKey, sevIndices in unexpectedDeiNameEfmSects.items():
|
|
1648
|
-
efmSection = sevs[sorted(sevIndices)[0]].get("efm") # use first section
|
|
1649
|
-
if nameAxisKey not in expectedDeiNames:
|
|
1650
|
-
name, axisKey = nameAxisKey
|
|
1651
|
-
if (includeNames is None or name in includeNames) and (excludeNames is None or name not in excludeNames):
|
|
1652
|
-
unexpectedFacts = set(f for i in sevIndices for f in sevFacts(i, name, sevCovered=False)) - sevCoveredFacts
|
|
1653
|
-
if unexpectedFacts:
|
|
1654
|
-
facts = sorted(unexpectedFacts, key=lambda f:f.objectIndex)
|
|
1655
|
-
sevMessage(sev, subType=submissionType, efmSection=efmSection, tag=name,
|
|
1656
|
-
label=ftLabel(name),
|
|
1657
|
-
modelObject=facts, ftContext=", ".join(ftContext(axisKey,axesValsKey(axisKey, f.context)) for f in facts),
|
|
1658
|
-
contextID=", ".join(f.contextID for f in facts),
|
|
1659
|
-
typeOfContext="Required Context")
|
|
1660
|
-
elif validation == "(elo-unexpected)":
|
|
1661
|
-
for eloName in sorted(unexpectedEloParams - expectedEloParams):
|
|
1662
|
-
if eloName in val.params:
|
|
1663
|
-
sevMessage(sev, subType=submissionType, efmSection="6.5.40",
|
|
1664
|
-
modelObject=modelXbrl, headerTag=eloName, value=val.params[eloName])
|
|
1665
|
-
elif validation == "(earliest-taxonomy)":
|
|
1666
|
-
for et in sev.get("earliest-taxonomies", ()):
|
|
1667
|
-
txPrefix = et.partition("/")[0]
|
|
1668
|
-
ns = deiDefaultPrefixedNamespaces.get(txPrefix)
|
|
1669
|
-
if ns:
|
|
1670
|
-
foundVersion = abbreviatedNamespace(ns)
|
|
1671
|
-
if foundVersion and foundVersion < et:
|
|
1672
|
-
sevMessage(sev, subType=submissionType, modelObject=modelXbrl, taxonomy=txPrefix, earliestVersion=et)
|
|
1673
|
-
elif validation == "taxonomy-version-required":
|
|
1674
|
-
if len(names) != value:
|
|
1675
|
-
et = sev["earliest-taxonomy"]
|
|
1676
|
-
sevMessage(sev, subType=submissionType, efmSection=efmSection, taxonomy=et.partition('/')[0], earliestTaxonomy=et)
|
|
1677
|
-
elif validation in ("taxonomy-url-required-in-dts", "taxonomy-url-unexpected-in-dts"):
|
|
1678
|
-
# value may have multiple fnmatch patterns with "|" separator
|
|
1679
|
-
# if multiple fnmatch patterns only one of them may have matches otherwise message
|
|
1680
|
-
patternMatchCount = dict((p,0) for p in value.split("|"))
|
|
1681
|
-
et = sev.get("earliest-taxonomy", "")
|
|
1682
|
-
foundVersion = abbreviatedNamespace(deiDefaultPrefixedNamespaces.get(et.partition("/")[0]))
|
|
1683
|
-
for pattern in patternMatchCount.keys():
|
|
1684
|
-
for url in modelXbrl.urlDocs.keys():
|
|
1685
|
-
if fnmatch.fnmatch(url, pattern):
|
|
1686
|
-
patternMatchCount[pattern] += 1
|
|
1687
|
-
if ((validation == "taxonomy-url-unexpected-in-dts" and any(count > 0 for count in patternMatchCount.values()))
|
|
1688
|
-
or (validation == "taxonomy-url-required-in-dts" and
|
|
1689
|
-
(not foundVersion or foundVersion >= et) and sum(
|
|
1690
|
-
count > 0 for count in patternMatchCount.values()) == 0)):
|
|
1691
|
-
sevMessage(sev, subType=submissionType, efmSection=efmSection, docType=deiDocumentType,
|
|
1692
|
-
taxonomyPattern=" or ".join(sorted(patternMatchCount.keys())))
|
|
1693
|
-
elif validation == "noDups":
|
|
1694
|
-
axes = deiValidations["axis-validations"][axisKey]["axes"]
|
|
1695
|
-
axesQNs = [qname(axis, deiDefaultPrefixedNamespaces) for axis in axes]
|
|
1696
|
-
axesKeys = axisKey.split('-')
|
|
1697
|
-
for index, axisQN in enumerate(axesQNs):
|
|
1698
|
-
currentAxisKey = axesKeys[index]
|
|
1699
|
-
axisContexts = {}
|
|
1700
|
-
for f in modelXbrl.factsByDimMemQname(axisQN):
|
|
1701
|
-
if f.context.dimsHash in axisContexts:
|
|
1702
|
-
axisContexts[f.context.dimsHash]["data"][f.concept.qname] = f.xValue
|
|
1703
|
-
else:
|
|
1704
|
-
axisContexts[f.context.dimsHash] = {
|
|
1705
|
-
"data": {f.concept.qname: f.xValue},
|
|
1706
|
-
"refFact": f
|
|
1707
|
-
}
|
|
1708
|
-
found = []
|
|
1709
|
-
for contextID, groupData in axisContexts.items():
|
|
1710
|
-
for otherContextID, otherGroupData in axisContexts.items():
|
|
1711
|
-
if otherContextID != contextID:
|
|
1712
|
-
if groupData["data"] == otherGroupData["data"]:
|
|
1713
|
-
matchingPair = set([contextID, otherContextID])
|
|
1714
|
-
if matchingPair not in found:
|
|
1715
|
-
sevMessage(sev, ftContext=ftContext(currentAxisKey, otherGroupData["refFact"]), otherftContext=ftContext(currentAxisKey, groupData["refFact"]))
|
|
1716
|
-
found.append(matchingPair)
|
|
1717
|
-
# type-specific validations
|
|
1718
|
-
elif len(names) == 0:
|
|
1719
|
-
pass # no name entries if all dei names of this validation weren't in the loaded dei taxonomy (i.e., pre 2019)
|
|
1720
|
-
elif validation == "tf3": # exactly one of names should have value if inline or if noninline and any present
|
|
1721
|
-
numFactWithValue = numFactsNotValue = 0
|
|
1722
|
-
for name in names:
|
|
1723
|
-
f = sevFact(sev, name) # these all are required context
|
|
1724
|
-
if f is not None:
|
|
1725
|
-
if f.xValue == value[0]: # first value is exclusive fact, second is other facts
|
|
1726
|
-
numFactWithValue += 1
|
|
1727
|
-
elif f.xValue == value[1]:
|
|
1728
|
-
numFactsNotValue += 1
|
|
1729
|
-
if (isInlineXbrl or numFactWithValue or numFactsNotValue) and (numFactWithValue != 1 or numFactsNotValue != 2):
|
|
1730
|
-
sevMessage(sev, subType=submissionType,
|
|
1731
|
-
modelObject=sevFacts(sev), tags=", ".join(names), value=value[0], otherValue=value[1])
|
|
1732
|
-
elif validation in ("ws", "wv"): # only one of names should have value
|
|
1733
|
-
numFactWithValue = 0
|
|
1734
|
-
for name in names:
|
|
1735
|
-
f = sevFact(sev, name) # these all are required context
|
|
1736
|
-
if f is not None:
|
|
1737
|
-
if f.xValue in value: # List of values which may be Yes, true, etc...
|
|
1738
|
-
numFactWithValue += 1
|
|
1739
|
-
if numFactWithValue > 1:
|
|
1740
|
-
sevMessage(sev, subType=submissionType,
|
|
1741
|
-
modelObject=sevFacts(sev), tags=", ".join(names), value=value)
|
|
1742
|
-
elif validation in ("o2", "o3"): # at least one present
|
|
1743
|
-
f2 = None
|
|
1744
|
-
numFacts = 0
|
|
1745
|
-
if referenceTag:
|
|
1746
|
-
f2 = sevFact(sev, referenceTag) # f and dependent fact are in same context
|
|
1747
|
-
if f2 is None:
|
|
1748
|
-
numFacts = 999 # block following message because no dependent (e.g., addressLine1)
|
|
1749
|
-
for name in names:
|
|
1750
|
-
f = sevFact(sev, name, f2)
|
|
1751
|
-
if f is not None:
|
|
1752
|
-
f2 = f # align next fact to this context
|
|
1753
|
-
numFacts += 1
|
|
1754
|
-
if numFacts == 0:
|
|
1755
|
-
sevMessage(sev, subType=submissionType, modelObject=sevFacts(sev), tags=", ".join(names))
|
|
1756
|
-
elif validation == "op": # all or neither must have a value
|
|
1757
|
-
if 0 < sum(sevFact(sev, name) is not None for name in names) < len(names): # default context for all
|
|
1758
|
-
sevMessage(sev, subType=submissionType, modelObject=sevFacts(sev), tags=", ".join(names))
|
|
1759
|
-
elif validation == "et1": # "og":
|
|
1760
|
-
ogfacts = set()
|
|
1761
|
-
for fr in sevFacts(sev, referenceTag, deduplicate=True):
|
|
1762
|
-
if fr.xValue == referenceValue:
|
|
1763
|
-
numOgFacts = 0
|
|
1764
|
-
for f in sevFacts(sev, names, fr):
|
|
1765
|
-
ogfacts.add(f)
|
|
1766
|
-
numOgFacts += 1
|
|
1767
|
-
if numOgFacts == 0:
|
|
1768
|
-
sevMessage(sev, subType=submissionType, modelObject=fr, tag=names[0], value=referenceValue, otherTag=referenceTag, contextID=fr.contextID)
|
|
1769
|
-
if any(name in eloValueFactNames for name in names):
|
|
1770
|
-
missingReqInlineTag = True
|
|
1771
|
-
# find any facts without a referenceTag fact = value, note these are warning severity
|
|
1772
|
-
for f in sevFacts(sev, names, deduplicate=True):
|
|
1773
|
-
if f not in ogfacts:
|
|
1774
|
-
fr = sevFact(sev, referenceTag, f)
|
|
1775
|
-
if (fr is None or fr.xValue != referenceValue):
|
|
1776
|
-
sevMessage(sev, severity="warning", subType=submissionType, modelObject=f, tag=names[0], value=referenceValue, otherTag=referenceTag, contextID=f.contextID)
|
|
1777
|
-
del ogfacts # dereference
|
|
1778
|
-
elif validation == "f2":
|
|
1779
|
-
f = sevFact(sev, referenceTag) # f and dependent fact are in same context
|
|
1780
|
-
if f is not None and not any(sevFact(sev, name, f) is not None for name in names):
|
|
1781
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=referenceTag, otherTags=", ".join(names))
|
|
1782
|
-
elif validation in ("ol1", "ol2"):
|
|
1783
|
-
for name in names:
|
|
1784
|
-
f = sevFact(sev, name) # referenced fact must be same context as this fact
|
|
1785
|
-
if f is not None and sevFact(sev, referenceTag, f) is None:
|
|
1786
|
-
sevMessage(sev, subType=submissionType, modelObject=sevFacts(sev), tag=name, otherTag=referenceTag, contextID=f.contextID)
|
|
1787
|
-
elif validation == "oph":
|
|
1788
|
-
f = sevFact(sev, referenceTag)
|
|
1789
|
-
for name in names:
|
|
1790
|
-
if f is None:
|
|
1791
|
-
f2 = sevFact(sev, name)
|
|
1792
|
-
if ((f is not None and sevFact(sev, name, f) is None) or
|
|
1793
|
-
(f is None and f2 is not None and sevFact(sev, referenceTag, f2) is None)):
|
|
1794
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=name, otherTag=referenceTag,
|
|
1795
|
-
contextID=f.contextID if f is not None else f2.contextID)
|
|
1796
|
-
elif validation in ("a", "sr", "oth", "tb", "n2e"): #, "et1"):
|
|
1797
|
-
for name in names:
|
|
1798
|
-
f = sevFact(sev, name)
|
|
1799
|
-
fr = sevFact(sev, referenceTag, f) # dependent fact is of context of f or for "c" inherited context (less disaggregatedd)
|
|
1800
|
-
if ((fr is not None and ((f is not None and fr.xValue != referenceValue) or
|
|
1801
|
-
(f is None and fr.xValue == referenceValue))) or
|
|
1802
|
-
(fr is None and f is not None)):
|
|
1803
|
-
sevMessage(sev, subType=submissionType, modelObject=sevFacts(sev), tag=name, otherTag=referenceTag, value=referenceValue,
|
|
1804
|
-
contextID=f.contextID if f is not None else fr.contextID if fr is not None else "N/A")
|
|
1805
|
-
elif validation in ("rt",):
|
|
1806
|
-
for name in names:
|
|
1807
|
-
f = sevFact(sev, name)
|
|
1808
|
-
fr = sevFact(sev, referenceTag, f) # dependent fact is of context of f or for "c" inherited context (less disaggregatedd)
|
|
1809
|
-
if ((fr is not None and ((fr.xValue == referenceValue) ^ (f is not None))) or
|
|
1810
|
-
(fr is None and f is not None)):
|
|
1811
|
-
_facts = [_f for _f in (f, fr) if _f is not None]
|
|
1812
|
-
sevMessage(sev, subType=submissionType, modelObject=_facts, tag=name, otherTag=referenceTag, value=referenceValue, contextID=_facts[0].contextID )
|
|
1813
|
-
elif validation in ("n2e",):
|
|
1814
|
-
for name in names:
|
|
1815
|
-
f = sevFact(sev, name)
|
|
1816
|
-
if f is not None and f.xValue == referenceValue:
|
|
1817
|
-
fr = sevFact(sev, referenceTag, f) # dependent fact is of context of f or for "c" inherited context (less disaggregatedd)
|
|
1818
|
-
if ((fr is not None and fr.xValue != referenceValue) or
|
|
1819
|
-
fr is None):
|
|
1820
|
-
sevMessage(sev, subType=submissionType, modelObject=sevFacts(sev), tag=name, otherTag=referenceTag, value=referenceValue,
|
|
1821
|
-
contextID=f.contextID if f is not None else fr.contextID if fr is not None else "N/A")
|
|
1822
|
-
elif validation == "ra":
|
|
1823
|
-
fr = sevFact(sev, referenceTag)
|
|
1824
|
-
for name in names:
|
|
1825
|
-
f = sevFact(sev, name, fr)
|
|
1826
|
-
if fr is not None and fr.xValue in referenceValue and f is None:
|
|
1827
|
-
sevMessage(sev, subType=submissionType, modelObject=sevFacts(sev), tag=referenceTag, otherTag=name, value=fr.xValue, contextID=fr.contextID)
|
|
1828
|
-
elif validation == "t":
|
|
1829
|
-
frs = [f for f in sevFacts(sev, referenceTag)] # all reference facts from generator
|
|
1830
|
-
for name in names:
|
|
1831
|
-
for f in sevFacts(sev, name):
|
|
1832
|
-
fr = sevFact(sev, referenceTag, f) # dependent fact is of context of f or for "c" inherited context (less disaggregated)
|
|
1833
|
-
if fr is not None:
|
|
1834
|
-
frs.remove(fr) # this referenced object has been covered by a referencing fact
|
|
1835
|
-
if ((fr is not None and f is None) or
|
|
1836
|
-
(fr is None and f is not None)):
|
|
1837
|
-
sevMessage(sev, subType=submissionType, modelObject=(f,fr), tag=name, otherTag=referenceTag)
|
|
1838
|
-
for fr in frs:
|
|
1839
|
-
for name in names:
|
|
1840
|
-
if sevFact(sev, name, fr) is None: # no corresponding fact to an unreferenced reference fact
|
|
1841
|
-
sevMessage(sev, subType=submissionType, modelObject=fr, tag=referenceTag, otherTag=name)
|
|
1842
|
-
elif validation == "te":
|
|
1843
|
-
tefacts = set()
|
|
1844
|
-
for fr in sevFacts(sev, referenceTag, deduplicate=True):
|
|
1845
|
-
flist = [f for f in sevFacts(sev, names, fr, deduplicate=True)] # just 1 name for te
|
|
1846
|
-
tefacts.update(flist)
|
|
1847
|
-
#revision of 2019-07-16, no warning if no trading symbol (it's now "may" exist)
|
|
1848
|
-
#if len(flist) < 1 and (fr.qname.localName == "TradingSymbol"):
|
|
1849
|
-
# sevMessage(sev, subType=submissionType, modelObject=[fr]+flist, tag=fr.qname.localName, otherTag=names[0],
|
|
1850
|
-
# validationMessage="message-missing-exchange")
|
|
1851
|
-
# find any facts without a securities12b
|
|
1852
|
-
for f in sevFacts(sev, names, deduplicate=True): # just 1 name for te
|
|
1853
|
-
if f not in tefacts:
|
|
1854
|
-
if sevFact(sev, referenceTag, f) is None:
|
|
1855
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=names[0], otherTags=", ".join(referenceTag), severityVerb="may")
|
|
1856
|
-
del tefacts # dereference
|
|
1857
|
-
elif validation in ("ot1", "n2bn1"):
|
|
1858
|
-
for i, name1 in enumerate(names):
|
|
1859
|
-
for fr in sevFacts(sev, name1, deduplicate=True):
|
|
1860
|
-
flist = [sevFact(sev, name2, fr) for name2 in names[i+1:]]
|
|
1861
|
-
if sum(f is not None for f in flist) > 0:
|
|
1862
|
-
sevMessage(sev, subType=submissionType, modelObject=[fr]+flist, tags=", ".join(names))
|
|
1863
|
-
elif validation == "t1":
|
|
1864
|
-
t1facts = set()
|
|
1865
|
-
for fr in sevFacts(sev, referenceTag, deduplicate=True):
|
|
1866
|
-
flist = [f for f in sevFacts(sev, names, fr, deduplicate=True)]
|
|
1867
|
-
t1facts.update(flist)
|
|
1868
|
-
if len(flist) > 1: # note that reference tag is a list here
|
|
1869
|
-
sevMessage(sev, subType=submissionType, modelObject=[fr]+flist, tags=", ".join(names), otherTags=", ".join(referenceTag),
|
|
1870
|
-
severityVerb="may")
|
|
1871
|
-
"""
|
|
1872
|
-
if isADR(fr):
|
|
1873
|
-
f = sevFact(sev, "TradingSymbol", fr)
|
|
1874
|
-
if f is not None and sevFact(sev, "SecurityExchangeName", fr) is None:
|
|
1875
|
-
sevMessage(sev, subType=submissionType, modelObject=f,
|
|
1876
|
-
tag="TradingSymbol", otherTag="SecurityExchangeName", contextID=f.contextID,
|
|
1877
|
-
validationMessage="message-ADR-no-exchange")
|
|
1878
|
-
"""
|
|
1879
|
-
# find any facts without a securities12b
|
|
1880
|
-
for f in sevFacts(sev, names, deduplicate=True):
|
|
1881
|
-
if f not in t1facts:
|
|
1882
|
-
if sevFact(sev, referenceTag, f) is None: # note that reference tag is a list here
|
|
1883
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tags=", ".join(names), otherTags=", ".join(referenceTag), severityVerb="may")
|
|
1884
|
-
del t1facts # dereference
|
|
1885
|
-
elif validation in ("de", "de5pm"):
|
|
1886
|
-
t = datetimeNowAtSEC
|
|
1887
|
-
if validation == "de5pm" and (17,31) <= (t.hour, t.minute) <= (23,0):
|
|
1888
|
-
while True: # add 1 day until on a business day
|
|
1889
|
-
t += datetime.timedelta(1)
|
|
1890
|
-
if t.weekday() < 5 and t not in upcomingSECHolidays: # break when not holiday and not weekend
|
|
1891
|
-
break
|
|
1892
|
-
for f in sevFacts(sev, names, deduplicate=True):
|
|
1893
|
-
if not (MIN_DOC_PER_END_DATE <= f.xValue <= t): # f.xValue is a date only, not a date-time
|
|
1894
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=name, value=f.xValue,
|
|
1895
|
-
expectedValue="!do-not-quote!between 1980-01-01 and {}".format(t.date().isoformat()))
|
|
1896
|
-
elif validation == "e503" and "itemsList" in val.params: # don't validate if no itemList (e.g. stand alone)
|
|
1897
|
-
e503facts = set()
|
|
1898
|
-
for f in sevFacts(sev, names, deduplicate=True):
|
|
1899
|
-
e503facts.add(f)
|
|
1900
|
-
if "5.03" not in val.params["itemsList"]:
|
|
1901
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=name, headerTag="5.03")
|
|
1902
|
-
if "5.03" in val.params["itemsList"] and not e503facts: # missing a required fact
|
|
1903
|
-
sevMessage(sev, subType=submissionType, modelObject=modelXbrl, tag=names[0], headerTag="5.03")
|
|
1904
|
-
elif validation == "503-header-field":
|
|
1905
|
-
if "5.03" not in val.params.get("itemsList",()):
|
|
1906
|
-
eloName = None # cancel validation "elo-name": "submissionHeader.fyEnd"
|
|
1907
|
-
elif validation == "sb":
|
|
1908
|
-
_fileNum = val.params.get("entity.repFileNum", "")
|
|
1909
|
-
for f in sevFacts(sev):
|
|
1910
|
-
if f.xValue and (_fileNum.startswith("811-") or _fileNum.startswith("814-")):
|
|
1911
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=f.qname.localName, otherTag="entity file number",
|
|
1912
|
-
value="not starting with 811- or 814-", contextID=f.contextID)
|
|
1913
|
-
elif validation in ("x", "xv", "r", "y", "n") or (validation and validation.startswith("ov")):
|
|
1914
|
-
for name in names:
|
|
1915
|
-
for f in sevFacts(sev, name, requiredContext=not axisKey, whereKey="where", fallback=True):
|
|
1916
|
-
# always fallback to None for these validations
|
|
1917
|
-
if validation.startswith("ov") and f is None:
|
|
1918
|
-
continue
|
|
1919
|
-
if f is None or (((f.xValue not in value) ^ ("!not!" in value)) if isinstance(value, (set,list))
|
|
1920
|
-
else (not value.search(str(f.xValue))) if isinstance(value, re.Pattern)
|
|
1921
|
-
else (not value.inRange(f.xValue)) if isinstance(value, ValueRange)
|
|
1922
|
-
else (value is not None and f.xValue != value)):
|
|
1923
|
-
sevMessage(sev, subType=submissionType, modelObject=f, efmSection=efmSection, tag=ftName(name), label=ftLabel(name), value=("(none)" if f is None else f.xValue), expectedValue=value, ftContext=ftContext(axisKey,f))
|
|
1924
|
-
if f is None and name in eloValueFactNames:
|
|
1925
|
-
missingReqInlineTag = True
|
|
1926
|
-
elif validation == "not-in-future":
|
|
1927
|
-
for name in names:
|
|
1928
|
-
for f in sevFacts(sev, name):
|
|
1929
|
-
if deiDocumentType and f.context.endDatetime > documentTypeFact.context.endDatetime:
|
|
1930
|
-
sevMessage(sev, subType=submissionType, modelObject=f, efmSection=efmSection, tag=name, context="context " + f.contextID)
|
|
1931
|
-
|
|
1932
|
-
elif validation in ("ru", "ou"):
|
|
1933
|
-
foundNonUS = None # false means found a us state, true means found a non-us state
|
|
1934
|
-
for name in names:
|
|
1935
|
-
f = sevFact(sev, name)
|
|
1936
|
-
if f is not None:
|
|
1937
|
-
foundNonUS = f.xValue not in value # value is set
|
|
1938
|
-
if foundNonUS == True or (validation == "ru" and foundNonUS is None):
|
|
1939
|
-
sevMessage(sev, subType=submissionType, modelObject=f, efmSection=efmSection, tag=name, value="U.S. state codes")
|
|
1940
|
-
elif validation in ("o", "ov"):
|
|
1941
|
-
for name in names:
|
|
1942
|
-
f = sevFact(sev, name)
|
|
1943
|
-
if f is not None and (((f.xValue not in value) ^ ("!not!" in value)) if isinstance(value, (set,list))
|
|
1944
|
-
else (value is not None and f.xValue != value)):
|
|
1945
|
-
sevMessage(sev, subType=submissionType, modelObject=f, efmSection=efmSection, tag=ftName(name), label=ftLabel(f), value=value, ftContext=ftContext(axisKey,f))
|
|
1946
|
-
elif validation == "security-axis":
|
|
1947
|
-
for name in names:
|
|
1948
|
-
facts = [f for f in sevFacts(sev, name, deduplicate=True)]
|
|
1949
|
-
hasNonDimContext = any((not f.context.qnameDims) for f in facts)
|
|
1950
|
-
hasADRmember = any(isADR(f) for f in facts)
|
|
1951
|
-
if (len(facts) == 1 and not hasNonDimContext and not hasADRmember) or (len(facts) > 1 and hasNonDimContext):
|
|
1952
|
-
sevMessage(sev, subType=submissionType, modelObject=facts, tag=name,
|
|
1953
|
-
contextIDs=", ".join(sorted(f.contextID for f in facts)))
|
|
1954
|
-
elif validation in ("md", "n2c"):
|
|
1955
|
-
mdfacts = defaultdict(set)
|
|
1956
|
-
for f in sevFacts(sev, names, deduplicate=True):
|
|
1957
|
-
if f is not None and (value is None or (
|
|
1958
|
-
((f.xValue not in value) ^ ("!not!" in value)) if isinstance(value, (set,list))
|
|
1959
|
-
else (value is not None and f.xValue != value))):
|
|
1960
|
-
mdfacts[f.context.contextDimAwareHash].add(f)
|
|
1961
|
-
for mdfactset in mdfacts.values():
|
|
1962
|
-
if len(mdfactset) != (1 if validation == "n2c" else len(names)):
|
|
1963
|
-
sevMessage(sev, subType=submissionType, modelObject=mdfactset, tags=", ".join(names), contextID=f.contextID)
|
|
1964
|
-
del mdfacts # dereference
|
|
1965
|
-
elif validation == "md-unexpected":
|
|
1966
|
-
for f in sevFacts(sev, names, deduplicate=True):
|
|
1967
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=f.qname.localName, context=f.contextID)
|
|
1968
|
-
elif validation == "n2bn2":
|
|
1969
|
-
for f in sevFacts(sev):
|
|
1970
|
-
if not f.xValue.startswith("814-"):
|
|
1971
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=f.qname.localName,
|
|
1972
|
-
value="a value starting with 814-", contextID=f.contextID)
|
|
1973
|
-
elif validation == "n2d":
|
|
1974
|
-
for name in names:
|
|
1975
|
-
f = sevFact(sev, name)
|
|
1976
|
-
fr = sevFact(sev, referenceTag, f)
|
|
1977
|
-
if fr is None and f is not None:
|
|
1978
|
-
sevMessage(sev, subType=submissionType, modelObject=sevFacts(sev), tag=name, otherTag=referenceTag, value=fr.xValue, contextID=fr.contextID)
|
|
1979
|
-
elif validation == "required-context-duration":
|
|
1980
|
-
monthsDuration = (val.requiredContext.endDatetime - val.requiredContext.startDatetime).days / 30.4375 # 30.4375 specified by DERA to use in the transforms for days to months
|
|
1981
|
-
if not value - 1 < monthsDuration < value + 1: # fractional months likely due to days per month
|
|
1982
|
-
sevMessage(sev, subType=submissionType, modelObject=val.requiredContext, tag="Required Context Period Duration",
|
|
1983
|
-
value=f"{monthsDuration:.1f} months", expectedValue=f"{value} months", contextID=val.requiredContext.id)
|
|
1984
|
-
# fee tagging
|
|
1985
|
-
elif validation in ("fe", "fw","fo"): # note where clauses are incompatible with this validation
|
|
1986
|
-
instDurNames = defaultdict(list)
|
|
1987
|
-
for name in names:
|
|
1988
|
-
concept = modelXbrl.qnameConcepts.get(qname(name, deiDefaultPrefixedNamespaces))
|
|
1989
|
-
if concept is not None:
|
|
1990
|
-
instDurNames[concept.periodType == "instant"].append(name)
|
|
1991
|
-
for isInstPeriod, instDurNames in instDurNames.items():
|
|
1992
|
-
mbrValCntxIds = {}
|
|
1993
|
-
for cntx in modelXbrl.contexts.values():
|
|
1994
|
-
mbrValKey = axesValsKey(axisKey, cntx)
|
|
1995
|
-
if mbrValKey is not None and cntx.isInstantPeriod == isInstPeriod:
|
|
1996
|
-
mbrValCntxIds[mbrValKey] = cntx.id
|
|
1997
|
-
for name in instDurNames:
|
|
1998
|
-
usedMbrVals = set(axesValsKey(axisKey, f.context)
|
|
1999
|
-
for f in sevFacts(sev, name, whereKey="where"))
|
|
2000
|
-
for mbrVal, cntxId in mbrValCntxIds.items():
|
|
2001
|
-
if mbrVal not in usedMbrVals and validation in ("fe", "fw"):
|
|
2002
|
-
sevMessage(sev, subType=submissionType, modelObject=None, tag=ftName(name), label=ftLabel(name), ftContext=ftContext(axisKey,mbrVal), contextID=cntxId)
|
|
2003
|
-
elif validation in ("of-rule",):
|
|
2004
|
-
mbfValFacts = defaultdict(list)
|
|
2005
|
-
requiredContextPeriod = sev.get("period") == "required-context" and deiDocumentType
|
|
2006
|
-
for name in names:
|
|
2007
|
-
for f in sevFacts(sev, name, deduplicate=True, whereKey="where"):
|
|
2008
|
-
fMbrVals = axesValsKey(axisKey, f.context)
|
|
2009
|
-
if isinstance(value, (set, list)) and value:
|
|
2010
|
-
appendFact = f.xValue in value
|
|
2011
|
-
elif isinstance(value, (str, bool, int, float)) and value != "":
|
|
2012
|
-
appendFact = f.xValue == value
|
|
2013
|
-
else:
|
|
2014
|
-
appendFact = True
|
|
2015
|
-
if appendFact: mbfValFacts[fMbrVals].append(f)
|
|
2016
|
-
for cntx in modelXbrl.contexts.values():
|
|
2017
|
-
mbrValKey = axesValsKey(axisKey, cntx)
|
|
2018
|
-
if mbrValKey is not None and mbrValKey != () and (
|
|
2019
|
-
not requiredContextPeriod or cntx.isPeriodEqualTo(documentTypeFact.context)):
|
|
2020
|
-
if len(mbfValFacts.get(mbrValKey,())) != 1:
|
|
2021
|
-
sevMessage(sev, subType=submissionType, modelObject=mbfValFacts.get(mbrValKey, None), tags=ftName(names), labels=ftLabel(names), ftContext=ftContext(axisKey,mbrValKey), contextID=cntx.id)
|
|
2022
|
-
for localName, facts in modelXbrl.factsByLocalName.items():
|
|
2023
|
-
# avoid duplicate messages about of-rule for this context
|
|
2024
|
-
if localName.endswith("Flg") and "Rule" in localName:
|
|
2025
|
-
for f in facts:
|
|
2026
|
-
if f.context == cntx:
|
|
2027
|
-
sevCoveredFacts.add(f)
|
|
2028
|
-
mbfValFacts.clear()
|
|
2029
|
-
elif validation and validation.startswith("fdep"):
|
|
2030
|
-
#if efmSection == "ft.oClmSrc":
|
|
2031
|
-
# print("trace") # uncomment for debug tracing specific validation rules
|
|
2032
|
-
refFactsFound = set()
|
|
2033
|
-
isAnotherLine = validation.endswith("anotherLine")
|
|
2034
|
-
referenceComparison = sev.get("references-comparison")
|
|
2035
|
-
for name in names:
|
|
2036
|
-
for f in sevFacts(sev, name, deduplicate=True, whereKey="where", fallback=bindIfAbsent, sevCovered=False):
|
|
2037
|
-
flagFactsFound = set()
|
|
2038
|
-
if f is None:
|
|
2039
|
-
fMbrVals = () # process reference values
|
|
2040
|
-
fValue = "absent"
|
|
2041
|
-
else:
|
|
2042
|
-
fMbrVals = axesValsKey(axisKey, f.context)
|
|
2043
|
-
fValue = f.xValue
|
|
2044
|
-
if ((value is None or ((fValue in value) == ("!not!" not in value) ))
|
|
2045
|
-
and fMbrVals is not None): # dimensions match
|
|
2046
|
-
for rName in referenceTag:
|
|
2047
|
-
if isAnotherLine:
|
|
2048
|
-
otherLinesFacts = list(
|
|
2049
|
-
fr for fr in sevFacts(sev, rName, axisKey=sev.get("references-axes"), whereKey="references-where", sevCovered=False)
|
|
2050
|
-
if fr.context.dimsHash != (f.context.dimsHash if f is not None else None) and
|
|
2051
|
-
(referenceComparison is None or
|
|
2052
|
-
(referenceComparison == "equal" and fValue == "absent" if fr is None else fValue == fr.xValue)
|
|
2053
|
-
)
|
|
2054
|
-
)
|
|
2055
|
-
fr = otherLinesFacts[0] if any(otherFact is not None for otherFact in otherLinesFacts) else None
|
|
2056
|
-
else:
|
|
2057
|
-
fr = sevFact(sev, rName, f, axisKey=sev.get("references-axes"), whereKey="references-where", sevCovered=False) # dependent fact is of context of f or for "c" inherited context (less disaggregated)
|
|
2058
|
-
items = [f]
|
|
2059
|
-
if fr is None:
|
|
2060
|
-
frValue = "absent"
|
|
2061
|
-
else:
|
|
2062
|
-
frValue = fr.xValue
|
|
2063
|
-
items.append(fr)
|
|
2064
|
-
refFactsFound.add(fr)
|
|
2065
|
-
flagFactsFound.add(fr)
|
|
2066
|
-
if (frValue not in referenceValue) ^ ("!not!" in referenceValue) and "flag-any" not in validation:
|
|
2067
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=ftName(name), otherTag=ftName(rName), label=ftLabel(rName), ftContext=ftContext(axisKey,fMbrVals), value=fValue, otherValue=frValue, expectedValue=referenceValue)
|
|
2068
|
-
if "flag-any" in validation and ((not flagFactsFound) == ("-not-" not in validation)):
|
|
2069
|
-
sevMessage(sev, subType=submissionType, modelObject=None, tag=ftName(name), otherTag=ftName(referenceTag), ftContext=ftContext(axisKey,fMbrVals), value=fValue)
|
|
2070
|
-
flagFactsFound.clear() # deref
|
|
2071
|
-
# find dependent facts without corresponding named fact
|
|
2072
|
-
if "flag" not in validation:
|
|
2073
|
-
for rName in referenceTag:
|
|
2074
|
-
for fr in sevFacts(sev, rName, deduplicate=True, sevCovered=False):
|
|
2075
|
-
fMbrVals = axesValsKey(axisKey, fr.context)
|
|
2076
|
-
if fMbrVals is not None and fr not in refFactsFound: # dimensions match, ref fact not matched to a name fact
|
|
2077
|
-
if (fr.xValue in referenceValue) ^ ("!not!" in referenceValue):
|
|
2078
|
-
sevMessage(sev, subType=submissionType, modelObject=fr, tag=ftName(name), label=ftLabel(name), otherTag=ftName(rName), otherLabel=ftLabel(rName), ftContext=ftContext(axisKey,fMbrVals))
|
|
2079
|
-
refFactsFound.clear() # deref
|
|
2080
|
-
elif validation and validation.startswith("fany"):
|
|
2081
|
-
#if efmSection == "ft.dbtVal6":
|
|
2082
|
-
# print("trace") # uncomment for debug tracing specific validation rules
|
|
2083
|
-
numFacts = 0
|
|
2084
|
-
for name in names:
|
|
2085
|
-
for f in sevFacts(sev, name, deduplicate=True):
|
|
2086
|
-
numFacts += 1
|
|
2087
|
-
if numFacts == 0:
|
|
2088
|
-
for rName in referenceTag or (): # if any reference facts bind skip the message
|
|
2089
|
-
fr = sevFact(sev, rName, axisKey=sev.get("references-axes"), whereKey="references-where") # dependent fact is of context of f or for "c" inherited context (less disaggregated)
|
|
2090
|
-
if fr is None:
|
|
2091
|
-
frValue = "absent"
|
|
2092
|
-
else:
|
|
2093
|
-
frValue = fr.xValue
|
|
2094
|
-
if (frValue in referenceValue) == ("!not!" not in referenceValue):
|
|
2095
|
-
numFacts = -1 # exclusion: skip message
|
|
2096
|
-
break
|
|
2097
|
-
if numFacts == 0:
|
|
2098
|
-
sevMessage(sev, subType=submissionType, modelObject=modelXbrl,
|
|
2099
|
-
tag=ftName(names), tags=ftName(names), label=ftLabel(names), ftContext="Summary Table or Offering")
|
|
2100
|
-
mbfValFacts.clear() # deref
|
|
2101
|
-
elif validation and validation.startswith("fsetdep"):
|
|
2102
|
-
mbfValFacts = defaultdict(list)
|
|
2103
|
-
for name in names:
|
|
2104
|
-
for f in sevFacts(sev, name, deduplicate=True):
|
|
2105
|
-
fMbrVals = axesValsKey(axisKey, f.context)
|
|
2106
|
-
mbfValFacts[fMbrVals].append(f)
|
|
2107
|
-
for cntx in modelXbrl.contexts.values():
|
|
2108
|
-
mbrValKey = axesValsKey(axisKey, cntx)
|
|
2109
|
-
if len(mbfValFacts.get(mbrValKey,())) == len(names):
|
|
2110
|
-
for rName in referenceTag:
|
|
2111
|
-
rf = sevFact(sev, rName, f)
|
|
2112
|
-
if rf is None:
|
|
2113
|
-
sevMessage(sev, subType=submissionType, modelObject=mbfValFacts.get(mbrValKey,()),
|
|
2114
|
-
tags=ftName(names), labels=ftLabel(names),
|
|
2115
|
-
otherTag=ftName(rName), otherLabel=ftLabel(rName), ftContext=ftContext(axisKey,mbrValKey))
|
|
2116
|
-
mbfValFacts.clear() # deref
|
|
2117
|
-
elif validation == "f3yrs":
|
|
2118
|
-
for name in names:
|
|
2119
|
-
fFound = False
|
|
2120
|
-
for f in sevFacts(sev, name, deduplicate=True):
|
|
2121
|
-
fMbrVals = axesValsKey(axisKey, f.context)
|
|
2122
|
-
if fMbrVals is not None: # dimensions match
|
|
2123
|
-
fr = sevFact(sev, referenceTag, f) # dependent fact is of context of f or for "c" inherited context (less disaggregated)
|
|
2124
|
-
t = datetime.date.today(); y = t.year; m = t.month; d = t.day
|
|
2125
|
-
if m == 2 and d == 29: # no 29 of feb 3 yrs ago
|
|
2126
|
-
m = 3; d = 1 # use march 1st
|
|
2127
|
-
if f.xValue < DateTime(y-3, m, d, dateOnly=True) and fr is None:
|
|
2128
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=ftName(f), label=ftLabel(f), otherTag=ftName(referenceTag), otherLabel=ftLabel(rName), ftContext=ftContext(axisKey,fMbrVals))
|
|
2129
|
-
elif validation == "future":
|
|
2130
|
-
for name in names:
|
|
2131
|
-
fFound = False
|
|
2132
|
-
for f in sevFacts(sev, name, deduplicate=True, whereKey="where"):
|
|
2133
|
-
fMbrVals = axesValsKey(axisKey, f.context)
|
|
2134
|
-
if fMbrVals is not None: # dimensions match
|
|
2135
|
-
t = datetime.date.today(); y = t.year; m = t.month; d = t.day
|
|
2136
|
-
if f.xValue > DateTime(y, m, d, dateOnly=True):
|
|
2137
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=ftName(f), label=ftLabel(f), value=f.xValue, expectedValue=t, ftContext=ftContext(axisKey,fMbrVals))
|
|
2138
|
-
elif validation and validation.startswith("tsum-"): # total-to-axis-sum
|
|
2139
|
-
# fee tagging summations, products
|
|
2140
|
-
tolerance = sev.get("tolerance",0)
|
|
2141
|
-
for totalName in names:
|
|
2142
|
-
for f in sevFacts(sev, totalName, deduplicate=True, whereKey="where"): # these all are sum facts
|
|
2143
|
-
items = [f]
|
|
2144
|
-
for contributingName in referenceTag:
|
|
2145
|
-
for g in sevFacts(sev, contributingName, axisKey=sev.get("references-axes"), matchDims=f.context.qnameDims, deduplicate=True, whereKey="references-where"):
|
|
2146
|
-
items.append(g)
|
|
2147
|
-
itemVals = [g.xValue if g is not None else 0 for g in items]
|
|
2148
|
-
if len(items) >= 2:
|
|
2149
|
-
expectedValue = sum(itemVals[1:])
|
|
2150
|
-
if abs(itemVals[0] - expectedValue) > tolerance:
|
|
2151
|
-
sevMessage(sev, subType=submissionType, modelObject=items, ftContext=ftContext(axisKey,f),
|
|
2152
|
-
tag=ftName(totalName), label=ftLabel(totalName), value=items[0], expectedValue=expectedValue,
|
|
2153
|
-
item=ftName(referenceTag[0]), itemLabel=ftLabel(referenceTag[0]),
|
|
2154
|
-
values=items[1:])
|
|
2155
|
-
elif validation and validation.startswith("asum-"): # axis-sum-to-axis-sum
|
|
2156
|
-
# fee tagging summations, products
|
|
2157
|
-
tolerance = sev.get("tolerance",0)
|
|
2158
|
-
comparison = sev.get("comparison")
|
|
2159
|
-
# identify if arguments are optional (default to zero if absent) or required (check doesn't bind if no arg)
|
|
2160
|
-
opt = sev.get("binding", "opt-opt")
|
|
2161
|
-
o1 = opt[0:3] == "opt"
|
|
2162
|
-
o2 = opt[4:7] == "opt"
|
|
2163
|
-
items1 = []
|
|
2164
|
-
items2 = []
|
|
2165
|
-
for name1 in names:
|
|
2166
|
-
for f in sevFacts(sev, name1, deduplicate=True, whereKey="where"): # these all are sum facts
|
|
2167
|
-
items1.append(f)
|
|
2168
|
-
for name2 in referenceTag:
|
|
2169
|
-
for g in sevFacts(sev, name2, axisKey=sev.get("references-axes"), deduplicate=True, whereKey="references-where"):
|
|
2170
|
-
items2.append(g)
|
|
2171
|
-
item1Vals = [f.xValue if f is not None else 0 for f in items1]
|
|
2172
|
-
item2Vals = [g.xValue if g is not None else 0 for g in items2]
|
|
2173
|
-
if (item1Vals or o1) and (item2Vals or o2): # at least one axis has items summed
|
|
2174
|
-
sum1 = sum(item1Vals)
|
|
2175
|
-
sum2 = sum(item2Vals)
|
|
2176
|
-
if ((comparison == "equal" and abs(sum1 - sum2) > tolerance) or
|
|
2177
|
-
(comparison == "not-equal" and abs(sum1 - sum2) <= tolerance) or
|
|
2178
|
-
(comparison == "less than or equal" and (sum1 - sum2) > tolerance)):
|
|
2179
|
-
sevMessage(sev, subType=submissionType, modelObject=items, ftContext=ftContext(axisKey,f),
|
|
2180
|
-
tag=ftName(name1), label=ftLabel(name1), otherTag=ftName(name2), otherLabel=ftLabel(name2), sumValue=sum1, otherSumValue=sum2, comparison=comparison,
|
|
2181
|
-
values=items1, otherValues=items2)
|
|
2182
|
-
elif validation in ("tmult", "tdiff", "tnotGt", "tequals", "tnotLs"):
|
|
2183
|
-
tolerance = sev.get("tolerance",0)
|
|
2184
|
-
referencesSubtract = sev.get("references-subtract", ())
|
|
2185
|
-
for name in names:
|
|
2186
|
-
for f in sevFacts(sev, name, deduplicate=True, whereKey="where"): # these all are sum facts
|
|
2187
|
-
items = [f]
|
|
2188
|
-
for i, contributingName in enumerate(referenceTag):
|
|
2189
|
-
for g in sevFacts(sev, contributingName, f, deduplicate=True, whereKey="references-where"):
|
|
2190
|
-
items.append(g)
|
|
2191
|
-
if len(items) < i + 2:
|
|
2192
|
-
items.append(None) # need at least 2 items
|
|
2193
|
-
itemVals = [g.xValue if g is not None else 0 for g in items]
|
|
2194
|
-
if validation == "tmult" and items[1] is not None and items[2] is not None and abs(
|
|
2195
|
-
f.xValue - (itemVals[1] * itemVals[2])) > tolerance:
|
|
2196
|
-
sevMessage(sev, subType=submissionType, modelObject=[f]+items, ftContext=ftContext(axisKey,f),
|
|
2197
|
-
tag=ftName(name), label=ftLabel(name), value=f.xValue, expectedValue=itemVals[1] * itemVals[2],
|
|
2198
|
-
term1=referenceTag[0], term1Label=ftLabel(referenceTag[0]), value1=items[1],
|
|
2199
|
-
term2=referenceTag[1], term2Label=ftLabel(referenceTag[1]), value2=items[2])
|
|
2200
|
-
elif validation in ("tdiff", "tnotGt", "tnotLs"):
|
|
2201
|
-
for i, subtractThisTerm in enumerate(referencesSubtract):
|
|
2202
|
-
if subtractThisTerm:
|
|
2203
|
-
itemVals[i+1] = - itemVals[i+1]
|
|
2204
|
-
expectedValue = sum(itemVals[1:])
|
|
2205
|
-
if ((validation == "tdiff" and abs(itemVals[0] - expectedValue) > tolerance) or
|
|
2206
|
-
(validation == "tnotGt" and itemVals[0] > expectedValue) or
|
|
2207
|
-
(validation == "tnotLs" and itemVals[0] < expectedValue)):
|
|
2208
|
-
termValues = "!do-not-quote!"
|
|
2209
|
-
for i, subtractThisTerm in enumerate(referencesSubtract):
|
|
2210
|
-
if i == 0 or (items[i + 1] is not None and items[i + 1].xValue != 0):
|
|
2211
|
-
# only append to termValues when the xValue is not 0 or
|
|
2212
|
-
# when it is the first reference item.
|
|
2213
|
-
if i > 0:
|
|
2214
|
-
termValues += " minus " if subtractThisTerm else " plus "
|
|
2215
|
-
termValues += f"{ftName(referenceTag[i])} {sevMessageArgValue(items[i+1])}"
|
|
2216
|
-
sevMessage(sev, subType=submissionType, modelObject=[f]+items, ftContext=ftContext(axisKey,f),
|
|
2217
|
-
tag=ftName(name), label=ftLabel(name), value=items[0], expectedValue=expectedValue,
|
|
2218
|
-
termValues=termValues)
|
|
2219
|
-
elif validation == "tequals" and items[1] is not None and abs(
|
|
2220
|
-
f.xValue - itemVals[1]) > tolerance:
|
|
2221
|
-
sevMessage(sev, subType=submissionType, modelObject=[f]+items, ftContext=ftContext(axisKey,f),
|
|
2222
|
-
tag=ftName(name), label=ftLabel(name), expectedValue=items[1],
|
|
2223
|
-
term=referenceTag[0], value=f)
|
|
2224
|
-
elif validation and validation.startswith("comparison"): # value comparison
|
|
2225
|
-
comparison = sev.get("comparison")
|
|
2226
|
-
for name1 in names:
|
|
2227
|
-
for f in sevFacts(sev, name1, deduplicate=True, whereKey="where"): # these all are sum facts
|
|
2228
|
-
for name2 in referenceTag:
|
|
2229
|
-
for g in sevFacts(sev, name2, f, axisKey=sev.get("references-axes"), deduplicate=True, whereKey="references-where"):
|
|
2230
|
-
if "references-date-format" in sev:
|
|
2231
|
-
referenceDate = datetime.datetime.strptime(g.xValue, sev.get("references-date-format"))
|
|
2232
|
-
if "%y" not in sev.get("references-date-format").lower():
|
|
2233
|
-
referenceDate = referenceDate.replace(year=f.xValue.year)
|
|
2234
|
-
if "%m" not in sev.get("references-date-format").lower():
|
|
2235
|
-
referenceDate = referenceDate.replace(year=f.xValue.month)
|
|
2236
|
-
if "%d" not in sev.get("references-date-format").lower():
|
|
2237
|
-
referenceDate = referenceDate.replace(year=f.xValue.day)
|
|
2238
|
-
g.xValue = ModelValue.dateTime(referenceDate.date().isoformat(), type=ModelValue.DATE)
|
|
2239
|
-
if ((comparison == "equal" and f.xValue != g.xValue) or
|
|
2240
|
-
(comparison == "not equal" and f.xValue == g.xValue) or
|
|
2241
|
-
(comparison in ("less than or equal", "not greater") and f.xValue > g.xValue)):
|
|
2242
|
-
comparisonText = sev.get("comparisonText", deiValidations["validations"][sev["validation"]].get("comparisonText", comparison)).format(comparison=comparison)
|
|
2243
|
-
sevMessage(sev, subType=submissionType, modelObject=(f,g), ftContext=ftContext(axisKey,g), comparison=comparisonText,
|
|
2244
|
-
tag=ftName(name1), label=ftLabel(name1), otherTag=ftName(name2), otherLabel=ftLabel(name2), value=f.xValue, otherValue=g.xValue)
|
|
2245
|
-
elif validation == "calculation":
|
|
2246
|
-
comparison = sev.get("comparison")
|
|
2247
|
-
operators = sev.get("references-operators")
|
|
2248
|
-
operatorsQualifiers = {
|
|
2249
|
-
"*": " multiplied by ",
|
|
2250
|
-
"/": " divided by ",
|
|
2251
|
-
"+": " plus ",
|
|
2252
|
-
"-": " minus ",
|
|
2253
|
-
"(": "(",
|
|
2254
|
-
")": ")"
|
|
2255
|
-
}
|
|
2256
|
-
tolerance = sev.get("tolerance", 0)
|
|
2257
|
-
def getEvalFunctionStringAndArgs(sev, functionName, argumentsKey="function-arguments"):
|
|
2258
|
-
functionArgsFacts = [sevFact(sev, argName, None if sev.get("function-arguments-exclude-otherFact") == True else f, axisKey=sev.get("references-axes"), whereKey="references-where") for argName in sev.get(argumentsKey)]
|
|
2259
|
-
if all([fact is not None for fact in functionArgsFacts]):
|
|
2260
|
-
functionArgs = [fact.xValue for fact in functionArgsFacts]
|
|
2261
|
-
functionEvalString = f"{functionName}(*functionArgs)"
|
|
2262
|
-
return functionEvalString, functionArgs
|
|
2263
|
-
elif argumentsKey != "function-arguments-alt" and sev.get("function-arguments-alt"):
|
|
2264
|
-
return getEvalFunctionStringAndArgs(sev, functionName, argumentsKey="function-arguments-alt")
|
|
2265
|
-
return None, None
|
|
2266
|
-
|
|
2267
|
-
for name1 in names:
|
|
2268
|
-
for f in sevFacts(sev, name1, deduplicate=True, whereKey="where", fallback=bindIfAbsent):
|
|
2269
|
-
fValue = 0 if f is None else f.xValue
|
|
2270
|
-
stringToEvaluate = ""
|
|
2271
|
-
termValues = "!do-not-quote!"
|
|
2272
|
-
operators = sev.get("references-operators").copy()
|
|
2273
|
-
for i, name2 in enumerate(referenceTag):
|
|
2274
|
-
if name2.startswith("function:"):
|
|
2275
|
-
functionName = name2[9:]
|
|
2276
|
-
evalString, functionArgs = getEvalFunctionStringAndArgs(sev, functionName)
|
|
2277
|
-
value = eval(evalString) if evalString else 0
|
|
2278
|
-
termValue = sevMessageArgValue(value)
|
|
2279
|
-
else:
|
|
2280
|
-
refFact = sevFact(sev, name2, f, axisKey=sev.get("references-axes"), whereKey="references-where")
|
|
2281
|
-
value = refFact.xValue if refFact is not None else 0
|
|
2282
|
-
termValue = sevMessageArgValue(refFact if refFact is not None else 0)
|
|
2283
|
-
|
|
2284
|
-
if i > 0:
|
|
2285
|
-
operator = operators.pop(0)
|
|
2286
|
-
stringToEvaluate = f"{stringToEvaluate}{operator}"
|
|
2287
|
-
for char in operator:
|
|
2288
|
-
termValues += operatorsQualifiers.get(char, char)
|
|
2289
|
-
|
|
2290
|
-
stringToEvaluate = f"{stringToEvaluate}{value}"
|
|
2291
|
-
|
|
2292
|
-
if name2.startswith("function:"):
|
|
2293
|
-
termName = sev.get("function-term-name")
|
|
2294
|
-
else:
|
|
2295
|
-
termName = ftName(name2)
|
|
2296
|
-
termValues += f"{termName} {termValue}"
|
|
2297
|
-
|
|
2298
|
-
while operators:
|
|
2299
|
-
operator = operators.pop(0)
|
|
2300
|
-
stringToEvaluate += operator
|
|
2301
|
-
for char in operator:
|
|
2302
|
-
termValues += operatorsQualifiers.get(char, char)
|
|
2303
|
-
|
|
2304
|
-
expectedValue = eval(stringToEvaluate)
|
|
2305
|
-
expectedValue = decimal.Decimal(f"{expectedValue:.2f}")
|
|
2306
|
-
expectedValueString = sevMessageArgValue(expectedValue, f)
|
|
2307
|
-
expectedValueString = f"!do-not-quote!{expectedValueString}"
|
|
2308
|
-
|
|
2309
|
-
if ((comparison == "equal" and abs(fValue-expectedValue) > tolerance) or
|
|
2310
|
-
(comparison == "not equal" and abs(fValue - expectedValue) <= tolerance ) or
|
|
2311
|
-
(comparison in ("less than or equal", "not greater") and (fValue - expectedValue) > tolerance)):
|
|
2312
|
-
comparisonText = sev.get("comparisonText", deiValidations["validations"][sev["validation"]].get("comparisonText", comparison)).format(comparison=comparison)
|
|
2313
|
-
sevMessage(sev, subType=submissionType, modelObject=[f], ftContext=ftContext(axisKey,f),
|
|
2314
|
-
tag=ftName(name), label=ftLabel(name), value=f, expectedValue=expectedValueString,
|
|
2315
|
-
termValues=termValues, comparison=comparisonText)
|
|
2316
|
-
elif validation == "skip-if-absent":
|
|
2317
|
-
#if efmSection == "ft.r011Flg":
|
|
2318
|
-
# print("trace") # uncomment for debug tracing specific validation rules
|
|
2319
|
-
# if no fact binds to sevFacts skip so store-db or store-db-action is not executed
|
|
2320
|
-
# if bind-if-absent and there is no fact and where clause fails, ski;
|
|
2321
|
-
# if bind-if-absent and no fact and where clause passes, don't skip
|
|
2322
|
-
if all(f is None
|
|
2323
|
-
for name1 in names
|
|
2324
|
-
for f in sevFacts(sev, name1, deduplicate=True, whereKey="where", fallback=bindIfAbsent)
|
|
2325
|
-
):
|
|
2326
|
-
continue # dont process store-to-db or other following actions
|
|
2327
|
-
elif validation == "fw-unexpected":
|
|
2328
|
-
for f in sevFacts(sev, names, whereKey="where"):
|
|
2329
|
-
sevMessage(sev, subType=submissionType, modelObject=f, tag=ftName(f), label=ftLabel(name), value=f.xValue, ftContext=ftContext(axisKey,f), contextID=f.contextID)
|
|
2330
|
-
if eloName:
|
|
2331
|
-
expectedEloParams.add(eloName)
|
|
2332
|
-
for name in names:
|
|
2333
|
-
f = sevFact(sev, name)
|
|
2334
|
-
if f is not None and eloName in val.params and not deiParamEqual(name, f.xValue, val.params[eloName]):
|
|
2335
|
-
sevMessage(sev, messageKey=sev.get("elo-match-message", "dq-0540-{tag}-Value"),
|
|
2336
|
-
subType=submissionType, modelObject=f, efmSection="6.5.40",
|
|
2337
|
-
tag=ftName(name), label=ftLabel(name), value=f.xValue, headerTag=eloName, valueOfHeaderTag=val.params[eloName])
|
|
2338
|
-
|
|
2339
|
-
if storeDbName or storeDbAction:
|
|
2340
|
-
for f in sevFacts(sev, names, whereKey="where", sevCovered=False):
|
|
2341
|
-
_storeDbName = lcStr(f.qname.localName) if storeDbName == "@lcName" else storeDbName
|
|
2342
|
-
axesValidations = deiValidations["axis-validations"][axisKey]
|
|
2343
|
-
axes = axesValidations["axes"]
|
|
2344
|
-
members = axesValidations.get("members",())
|
|
2345
|
-
if f is not None:
|
|
2346
|
-
_axisKey = tuple(
|
|
2347
|
-
(lcStr(dim.dimensionQname.localName.replace("Axis","")),
|
|
2348
|
-
str(dim.typedMember.xValue) if dim.isTyped else dim.memberQname.localName)
|
|
2349
|
-
for _axis in axes
|
|
2350
|
-
for dim in f.context.qnameDims.values()
|
|
2351
|
-
if qname(_axis, deiDefaultPrefixedNamespaces) == dim.dimensionQname
|
|
2352
|
-
)
|
|
2353
|
-
if storeDbName:
|
|
2354
|
-
storeDbObjectFacts.setdefault(storeDbObject,{}).setdefault(_axisKey,{})[
|
|
2355
|
-
_storeDbName] = getStoreDBValue(ftName(f), eloValueOfFact(names[0], f.xValue))
|
|
2356
|
-
if storeDbInnerTextTruncate:
|
|
2357
|
-
storeDbObjectFacts.setdefault(storeDbObject,{}).setdefault(_axisKey,{})[
|
|
2358
|
-
f"{_storeDbName}InnerText"] = strTruncate(normalizeSpace(XmlUtil.innerText(f,
|
|
2359
|
-
ixExclude="html",
|
|
2360
|
-
ixEscape=False,
|
|
2361
|
-
ixContinuation=(f.elementQname == XbrlConst.qnIXbrl11NonNumeric),
|
|
2362
|
-
ixResolveUris=False,
|
|
2363
|
-
strip=True)), storeDbInnerTextTruncate) # transforms are whitespace-collapse, otherwise it is preserved.
|
|
2364
|
-
if storeDbAction:
|
|
2365
|
-
for k, v in storeDbAction.items():
|
|
2366
|
-
storeDbActions.setdefault(storeDbObject,{}).setdefault(_axisKey,{})[k] = getStoreDBValue(k, v, otherFact=f)
|
|
2367
|
-
|
|
2368
|
-
elif not axes:
|
|
2369
|
-
if storeDbName and _storeDbName not in storeDbObjectFacts:
|
|
2370
|
-
storeDbObjectFacts.setdefault(storeDbObject,{}).setdefault((),{})[_storeDbName] = eloValueOfFact(names[0], f.xValue)
|
|
2371
|
-
if storeDbAction:
|
|
2372
|
-
for k, v in storeDbAction.items():
|
|
2373
|
-
storeDbActions.setdefault(storeDbObject,{}).setdefault((),{})[k] = getStoreDBValue(k, v)
|
|
2374
|
-
|
|
2375
|
-
del unexpectedDeiNameEfmSects, expectedDeiNames, sevCoveredFacts # dereference
|
|
2376
|
-
val.modelXbrl.profileActivity("... submission type element validations", minTimeToShow=0.1)
|
|
2377
|
-
|
|
2378
|
-
if deiDocumentType in ("2.01 SD",): # wch - change ultimately to 2.01 SD only
|
|
2379
|
-
val.modelXbrl.profileActivity("... filer required facts checks (other than SD)", minTimeToShow=1.0)
|
|
2380
|
-
class Rxp(): # fake class of rxp qnames based on discovered rxp namespace
|
|
2381
|
-
def __init__(self): # wch temporarily list actual element names here as a check
|
|
2382
|
-
# PmtAxis
|
|
2383
|
-
# ProjectAxis
|
|
2384
|
-
# ResourceAxis
|
|
2385
|
-
# SubnationalJurisdictionsAxis
|
|
2386
|
-
# CountryAxis
|
|
2387
|
-
# GovernmentAxis
|
|
2388
|
-
# PaymentTypeAxis
|
|
2389
|
-
# SegmentAxis
|
|
2390
|
-
# AllProjectsMember
|
|
2391
|
-
# AllResourcesMember
|
|
2392
|
-
# AllGovernmentsMember
|
|
2393
|
-
# AllPaymentTypesMember
|
|
2394
|
-
# AllSegmentsMember
|
|
2395
|
-
# Royalties
|
|
2396
|
-
# Fees
|
|
2397
|
-
# ProductionEntitlements
|
|
2398
|
-
# Dividends
|
|
2399
|
-
# Bonuses
|
|
2400
|
-
# InfrastructureImprovements
|
|
2401
|
-
# CommunityAndSocial
|
|
2402
|
-
# OtherPayments
|
|
2403
|
-
# TotalPayments
|
|
2404
|
-
# Taxes
|
|
2405
|
-
# Sg
|
|
2406
|
-
# P
|
|
2407
|
-
# Gv
|
|
2408
|
-
# Co
|
|
2409
|
-
# Sn
|
|
2410
|
-
# R
|
|
2411
|
-
# Pr
|
|
2412
|
-
# M
|
|
2413
|
-
# A
|
|
2414
|
-
# Cm
|
|
2415
|
-
# K
|
|
2416
|
-
# Km
|
|
2417
|
-
for name in ("CountryAxis", "GovernmentAxis", "PaymentTypeAxis", "ProjectAxis","PmtAxis",
|
|
2418
|
-
"AllGovernmentsMember", "AllProjectsMember","SegmentAxis", "AllResourcesMember", "EntityDomain",
|
|
2419
|
-
"A", "Co", "Cm", "E", "Gv", "M", "K", "Km", "Sn", "P", "Pr", "R", "Sg", "TotalPayments"):
|
|
2420
|
-
setattr(self, name, qname(f"rxp:{name}", deiDefaultPrefixedNamespaces))
|
|
2421
|
-
|
|
2422
|
-
rxp = Rxp()
|
|
2423
|
-
f1 = deiFacts.get(disclosureSystem.deiCurrentFiscalYearEndDateElement)
|
|
2424
|
-
if f1 is not None and documentPeriodEndDateFact is not None and f1.xValid >= VALID and documentPeriodEndDateFact.xValid >= VALID:
|
|
2425
|
-
d = ModelValue.dateunionDate(documentPeriodEndDateFact.xValue)# is an end date, convert back to a start date without midnight part
|
|
2426
|
-
if f1.xValue.month != d.month or f1.xValue.day != d.day:
|
|
2427
|
-
modelXbrl.error("EFM.6.05.58", # wch we might not care
|
|
2428
|
-
_("The financial period %(reportingPeriod)s does not match the fiscal year end %(fyEndDate)s."),
|
|
2429
|
-
edgarCode="rxp-0558-Fiscal-Year-End-Date-Value",
|
|
2430
|
-
modelObject=(f1,documentPeriodEndDateFact), fyEndDate=f1.value, reportingPeriod=documentPeriodEndDateFact.value)
|
|
2431
|
-
# if (documentPeriodEndDateFact is not None and documentPeriodEndDateFact.xValid >= VALID and
|
|
2432
|
-
# not any(f2.xValue == documentPeriodEndDateFact.xValue
|
|
2433
|
-
# for f2 in modelXbrl.factsByQname[rxp.D]
|
|
2434
|
-
# if f2.xValid >= VALID)):
|
|
2435
|
-
# modelXbrl.error("EFM.6.58.27",
|
|
2436
|
-
# _("The financial period %(reportingPeriod)s does not match rxp:D in any facts."),
|
|
2437
|
-
# edgarCode="rxp-2327-Payment-Financial-Period-Existence",
|
|
2438
|
-
# modelObject=documentPeriodEndDateFact, reportingPeriod=documentPeriodEndDateFact.value)
|
|
2439
|
-
|
|
2440
|
-
#no longer in EFM or RXP taxonomy guide
|
|
2441
|
-
#for url,doc in modelXbrl.urlDocs.items():
|
|
2442
|
-
# if (url not in disclosureSystem.standardTaxonomiesDict and
|
|
2443
|
-
# doc.inDTS and # ignore EdgarRenderer-loaded non-DTS schemas
|
|
2444
|
-
# doc.type == ModelDocument.Type.SCHEMA):
|
|
2445
|
-
# for concept in XmlUtil.children(doc.xmlRootElement, XbrlConst.xsd, "element"):
|
|
2446
|
-
# name = concept.name
|
|
2447
|
-
# if not concept.isAbstract or concept.isHypercubeItem or concept.isDimensionItem:
|
|
2448
|
-
# modelXbrl.error("EFM.6.05.58.customElementDeclaration",
|
|
2449
|
-
# _("%(schemaName)s contained a disallowed %(disallowance)s declaration for element %(concept)s. "
|
|
2450
|
-
# "Use a standard RXP element instead."),
|
|
2451
|
-
# edgarCode="rxp-2312-Custom-Element-Declaration",
|
|
2452
|
-
# modelObject=concept, schemaName=doc.basename, concept=concept.qname,
|
|
2453
|
-
# disallowance="non-abstract" if not concept.isAbstract
|
|
2454
|
-
# else "hypercube" if concept.isHypercubeItem
|
|
2455
|
-
# else "dimension")
|
|
2456
|
-
|
|
2457
|
-
val.modelXbrl.profileActivity("... SD checks 6-13, 26-27", minTimeToShow=1.0)
|
|
2458
|
-
dimDefRelSet = modelXbrl.relationshipSet(XbrlConst.dimensionDefault)
|
|
2459
|
-
dimDomRelSet = modelXbrl.relationshipSet(XbrlConst.dimensionDomain)
|
|
2460
|
-
hypDimRelSet = modelXbrl.relationshipSet(XbrlConst.hypercubeDimension)
|
|
2461
|
-
hasHypRelSet = modelXbrl.relationshipSet(XbrlConst.all)
|
|
2462
|
-
for rel in dimDomRelSet.modelRelationships:
|
|
2463
|
-
if (isinstance(rel.fromModelObject, ModelConcept) and isinstance(rel.toModelObject, ModelConcept) and
|
|
2464
|
-
not dimDefRelSet.isRelated(rel.fromModelObject, "child", rel.toModelObject)):
|
|
2465
|
-
modelXbrl.error("EFM.6.58.14",
|
|
2466
|
-
_("In %(linkbaseName)s the target of the dimension-domain relationship in role %(linkrole)s from "
|
|
2467
|
-
"%(source)s to %(target)s must be the default member of %(source)s."),
|
|
2468
|
-
edgarCode="rxp-2314-Dimension-Domain-Relationship-Existence",
|
|
2469
|
-
modelObject=(rel, rel.fromModelObject, rel.toModelObject),
|
|
2470
|
-
linkbaseName=rel.modelDocument.basename, linkrole=rel.linkrole,
|
|
2471
|
-
source=rel.fromModelObject.qname, target=rel.toModelObject.qname)
|
|
2472
|
-
domMemRelSet = modelXbrl.relationshipSet(XbrlConst.domainMember)
|
|
2473
|
-
memDim = {}
|
|
2474
|
-
def checkMemMultDims(memRel, dimRel, elt, ELR, visited):
|
|
2475
|
-
if elt not in visited:
|
|
2476
|
-
visited.add(elt)
|
|
2477
|
-
for rel in domMemRelSet.toModelObject(elt):
|
|
2478
|
-
if rel.consecutiveLinkrole == ELR and isinstance(rel.fromModelObject, ModelConcept):
|
|
2479
|
-
checkMemMultDims(memRel, None, rel.fromModelObject, rel.linkrole, visited)
|
|
2480
|
-
for rel in hypDimRelSet.toModelObject(elt):
|
|
2481
|
-
if rel.consecutiveLinkrole == ELR and isinstance(rel.fromModelObject, ModelConcept):
|
|
2482
|
-
checkMemMultDims(memRel, dimRel, rel.fromModelObject, rel.linkrole, visited)
|
|
2483
|
-
for rel in hasHypRelSet.toModelObject(elt):
|
|
2484
|
-
if rel.consecutiveLinkrole == ELR and isinstance(rel.fromModelObject, ModelConcept):
|
|
2485
|
-
linkrole = rel.linkrole
|
|
2486
|
-
mem = memRel.toModelObject
|
|
2487
|
-
if (mem,linkrole) not in memDim:
|
|
2488
|
-
memDim[mem,linkrole] = (dimRel, memRel)
|
|
2489
|
-
else:
|
|
2490
|
-
otherDimRel, otherMemRel = memDim[mem,linkrole]
|
|
2491
|
-
modelXbrl.error("EFM.6.58.16",
|
|
2492
|
-
_("Member element %(member)s appears in more than one axis: %(dimension1)s and %(dimension2)s. "
|
|
2493
|
-
"Use distinct members for the Project, Country, Government, Legal Entity, Subnational Jurisdiction, Segment and Payment Type axes."),
|
|
2494
|
-
edgarCode="rxp-2316-Member-Multiple-Axis-Existence",
|
|
2495
|
-
modelObject=(dimRel, otherDimRel, memRel, otherMemRel, dimRel.fromModelObject, otherDimRel.fromModelObject),
|
|
2496
|
-
member=mem.qname, dimension1=dimRel.fromModelObject.qname, linkrole1=linkrole,
|
|
2497
|
-
dimension2=otherDimRel.fromModelObject.qname, linkrole2=otherDimRel.linkrole)
|
|
2498
|
-
visited.discard(elt)
|
|
2499
|
-
# TODO: wch this needs revisiting, because it's only relevant in a very few roles.
|
|
2500
|
-
# revisit because there may be a use case among projects/governments/entities/resources that requires enum value check across roles
|
|
2501
|
-
#
|
|
2502
|
-
# for rel in domMemRelSet.modelRelationships:
|
|
2503
|
-
# if isinstance(rel.fromModelObject, ModelConcept) and isinstance(rel.toModelObject, ModelConcept):
|
|
2504
|
-
# for rel2 in modelXbrl.relationshipSet(XbrlConst.domainMember, rel.consecutiveLinkrole).fromModelObject(rel.toModelObject):
|
|
2505
|
-
# if isinstance(rel2.fromModelObject, ModelConcept) and isinstance(rel2.toModelObject, ModelConcept):
|
|
2506
|
-
# modelXbrl.error("EFM.6.58.15",
|
|
2507
|
-
# _("The domain-member relationship in %(linkrole)s from %(source)s to %(target)s is consecutive with domain-member relationship in %(linkrole2)s to %(target2)s."),
|
|
2508
|
-
# edgarCode="rxp-2315-Consecutive-Domain-Member-Relationships-Existence",
|
|
2509
|
-
# modelObject=(rel, rel.fromModelObject, rel.toModelObject),
|
|
2510
|
-
# linkrole=rel.linkrole, linkrole2=rel2.linkrole,
|
|
2511
|
-
# source=rel.fromModelObject.qname, target=rel.toModelObject.qname, target2=rel2.toModelObject.qname)
|
|
2512
|
-
# checkMemMultDims(rel, None, rel.fromModelObject, rel.linkrole, set())
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
cntxEqualFacts = defaultdict(list)
|
|
2516
|
-
for f in modelXbrl.facts:
|
|
2517
|
-
if f.xValid >= VALID and f.context is not None:
|
|
2518
|
-
cntxEqualFacts[f.context.contextDimAwareHash].append(f)
|
|
2519
|
-
val.modelXbrl.profileActivity("... Form SD prepare facts by context", minTimeToShow=1.0)
|
|
2520
|
-
|
|
2521
|
-
qnCurrencyMeasure = XbrlConst.qnIsoCurrency(deiItems.get("EntityReportingCurrencyISOCode"))
|
|
2522
|
-
currencyMeasures = (tuple([qnCurrencyMeasure]),())
|
|
2523
|
-
hasRxpAwithCurAndYr = None # an rxp:A found matching currency measure and 1 yr per to doc end date
|
|
2524
|
-
if documentPeriodEndDateFact is not None and documentPeriodEndDateFact.xValid >= VALID:
|
|
2525
|
-
rxpAendDatetime = dateTime(documentPeriodEndDateFact.xValue, addOneDay=True)
|
|
2526
|
-
rxpAstartDatetime = rxpAendDatetime.replace(year=rxpAendDatetime.year-1)
|
|
2527
|
-
hasRxpAwithCurAndYr = False
|
|
2528
|
-
for cntxFacts in cntxEqualFacts.values():
|
|
2529
|
-
qnameFacts = dict((f.qname,f) for f in cntxFacts)
|
|
2530
|
-
context = cntxFacts[0].context
|
|
2531
|
-
contextDims = cntxFacts[0].context.qnameDims
|
|
2532
|
-
for qnF, fNilOk, qnG, gNilOk in ((rxp.A, True, rxp.R, False),
|
|
2533
|
-
(rxp.A, True, rxp.M, False),
|
|
2534
|
-
# wch - seems redundant, but actually, rxp.A can be absent in some cases
|
|
2535
|
-
(rxp.A, False, rxp.Gv, False),
|
|
2536
|
-
(rxp.A, False, rxp.Co, False),
|
|
2537
|
-
(rxp.Co, False, rxp.A, False),
|
|
2538
|
-
(rxp.Cm, False, rxp.A, False),
|
|
2539
|
-
(rxp.Gv, False, rxp.Co, False),
|
|
2540
|
-
(rxp.E, False, rxp.Co, False),
|
|
2541
|
-
(rxp.Gv, False, rxp.A, False),
|
|
2542
|
-
(rxp.Km, False, rxp.K, False),
|
|
2543
|
-
(rxp.K, False, rxp.Km, False),
|
|
2544
|
-
# (rxp.Cm, False, rxp.Cu, False), # wch there is no Cu
|
|
2545
|
-
(rxp.K, False, rxp.A, False),
|
|
2546
|
-
(rxp.M, False, rxp.A, False),
|
|
2547
|
-
(rxp.P, False, rxp.A, False),
|
|
2548
|
-
(rxp.R, False, rxp.A, False),
|
|
2549
|
-
(rxp.Pr, False, rxp.A, False),
|
|
2550
|
-
(rxp.Pr, False, rxp.Gv, False),
|
|
2551
|
-
(rxp.Sn, False, rxp.Co, False)):
|
|
2552
|
-
if (qnF in qnameFacts and (fNilOk or not qnameFacts[qnF].isNil) and
|
|
2553
|
-
(qnG not in qnameFacts or (not gNilOk and qnameFacts[qnG].isNil))):
|
|
2554
|
-
modelXbrl.error("EFM.6.05.58.03",
|
|
2555
|
-
_("The Context %(context)s has a %(fact1)s and is missing required %(fact2NotNil)sfact %(fact2)s"),
|
|
2556
|
-
modelObject=qnameFacts[qnF], context=context.id, fact1=qnF, fact2=qnG, fact2NotNil="" if gNilOk else "non-nil ",
|
|
2557
|
-
edgarCode="rxp-055803-Context-Required-Facts")
|
|
2558
|
-
if rxp.A in qnameFacts and not qnameFacts[rxp.A].isNil:
|
|
2559
|
-
if (rxp.Cm in qnameFacts and not qnameFacts[rxp.Cm].isNil and
|
|
2560
|
-
qnameFacts[rxp.A].unit is not None and qnameFacts[rxp.A].unit.measures == currencyMeasures):
|
|
2561
|
-
modelXbrl.error("EFM.6.05.58.04",
|
|
2562
|
-
_("A value cannot be given for rxp:Cm in context %(context)s because the payment is in the reporting currency %(currency)s."),
|
|
2563
|
-
edgarCode="rxp-055804-Conversion-Method-Value",
|
|
2564
|
-
modelObject=(qnameFacts[rxp.A],qnameFacts[rxp.Cm]), context=context.id, currency=qnCurrencyMeasure)
|
|
2565
|
-
if (hasRxpAwithCurAndYr == False and qnameFacts[rxp.A].unit.measures == currencyMeasures and
|
|
2566
|
-
qnameFacts[rxp.A].context.startDatetime == rxpAstartDatetime and qnameFacts[rxp.A].context.endDatetime == rxpAendDatetime):
|
|
2567
|
-
hasRxpAwithCurAndYr = True
|
|
2568
|
-
if rxp.P in qnameFacts and not any(f.xValid >= VALID and f.context is not None and not f.context.qnameDims
|
|
2569
|
-
for f in modelXbrl.factsByQname.get(qnameFacts[rxp.P].xValue,())):
|
|
2570
|
-
modelXbrl.error("EFM.6.05.58.07",
|
|
2571
|
-
_("Payment type %(paymentType)s was reported in context %(context)s but there is no fact with element %(paymentType)s in the Required Context."),
|
|
2572
|
-
edgarCode="rxp-055807-Category-Total-Existence",
|
|
2573
|
-
modelObject=context, context=context.id, paymentType=qnameFacts[rxp.P].xValue)
|
|
2574
|
-
if (context.hasDimension(rxp.GovernmentAxis) and
|
|
2575
|
-
not any(f.xValid >= VALID and f.xValue == m and f.context.hasDimension(rxp.PmtAxis)
|
|
2576
|
-
for m in (contextDims[rxp.GovernmentAxis].memberQname,)
|
|
2577
|
-
for f in modelXbrl.factsByQname[rxp.Gv])):
|
|
2578
|
-
modelXbrl.error("EFM.6.58.08", # TODO wch has not put this in EFM draft yet.
|
|
2579
|
-
_("A payment amount for each government is required. Provide a value for element rxp:Gv with value %(member)s."),
|
|
2580
|
-
edgarCode="rxp-055808-Government-Payment-Amount-Existence",
|
|
2581
|
-
modelObject=context, context=context.id, dimension=rxp.GovernmentAxis, member=context.dimMemberQname(rxp.GovernmentAxis))
|
|
2582
|
-
if (context.hasDimension(rxp.ProjectAxis) and
|
|
2583
|
-
not any(f.xValid >= VALID and f.xValue == m
|
|
2584
|
-
for m in (contextDims[rxp.ProjectAxis].memberQname,)
|
|
2585
|
-
for f in modelXbrl.factsByQname[rxp.Pr])):
|
|
2586
|
-
modelXbrl.error("EFM.6.05.58.09", # TODO wch this seems right but needs revisiting
|
|
2587
|
-
_("A payment for each project axis member is required. Provide a value for element rxp:Pr with value %(dimension)s in context %(context)s."),
|
|
2588
|
-
edgarCode="rxp-055809-Project-Payment-Amount-Existence",
|
|
2589
|
-
modelObject=context, context=context.id, dimension=rxp.GovernmentAxis)
|
|
2590
|
-
if hasRxpAwithCurAndYr == False:
|
|
2591
|
-
modelXbrl.error("EFM.6.05.58.05",
|
|
2592
|
-
_("Amount rxp:A missing for reporting currency and matching 12 months preceeding dei:DocumentPeriodEndDate."),
|
|
2593
|
-
edgarCode="rxp-055805-Amount-For-Required-12-Months-Period",
|
|
2594
|
-
modelObject=documentPeriodEndDateFact)
|
|
2595
|
-
|
|
2596
|
-
val.modelXbrl.profileActivity("... Form SD 6.05.58 fact checks", minTimeToShow=1.0)
|
|
2597
|
-
# deference object references no longer needed
|
|
2598
|
-
del cntxEqualFacts
|
|
2599
|
-
# dereference compatibly with 2.7 (as these may be used in nested contexts above
|
|
2600
|
-
hasHypRelSet = hypDimRelSet = dimDefRelSet = domMemRelSet = dimDomRelSet = None
|
|
2601
|
-
memDim.clear()
|
|
2602
|
-
elif disclosureSystem.GFM:
|
|
2603
|
-
for deiItem in (
|
|
2604
|
-
disclosureSystem.deiCurrentFiscalYearEndDateElement,
|
|
2605
|
-
disclosureSystem.deiDocumentFiscalYearFocusElement,
|
|
2606
|
-
disclosureSystem.deiFilerNameElement):
|
|
2607
|
-
if deiItem not in deiItems or deiItems[deiItem] == "":
|
|
2608
|
-
modelXbrl.error("GFM.3.02.01",
|
|
2609
|
-
_("dei:%(elementName)s was not found in the required context"),
|
|
2610
|
-
modelXbrl=modelXbrl, elementName=deiItem)
|
|
2611
|
-
if deiDocumentType not in ("SD", "SD/A"):
|
|
2612
|
-
val.modelXbrl.profileActivity("... filer required facts checks", minTimeToShow=1.0)
|
|
2613
|
-
|
|
2614
|
-
# log extracted facts
|
|
2615
|
-
if (isXbrlInstance or isFtJson) and (extractedCoverFacts or storeDbObjectFacts):
|
|
2616
|
-
if storeDbObjectFacts.get("eloValuesFromFacts"):
|
|
2617
|
-
storeDbObjectFacts["eloValuesFromFacts"][()]["missingReqInlineTag"] = ["no", "yes"][missingReqInlineTag]
|
|
2618
|
-
contextualFactNameSets = (("Security12bTitle", "TradingSymbol"), ("Security12gTitle", "TradingSymbol"))
|
|
2619
|
-
exchangeFactName = "SecurityExchangeName"
|
|
2620
|
-
exchangeAxisQN = qname(deiNamespaceURI, "EntityListingsExchangeAxis")
|
|
2621
|
-
hasADR = False
|
|
2622
|
-
cEqualCoverFacts = defaultdict(dict)
|
|
2623
|
-
# find c-equivalent Title, Registration and Symbols
|
|
2624
|
-
for name, facts in extractedCoverFacts.items():
|
|
2625
|
-
for f in facts:
|
|
2626
|
-
cEqualCoverFacts[f.context.contextDimAwareHash][name] = f
|
|
2627
|
-
if not hasADR and any(d.dimensionQname.localName in deiCAxes
|
|
2628
|
-
and d.memberQname == deiADRmember
|
|
2629
|
-
for d in f.context.qnameDims.values()):
|
|
2630
|
-
hasADR = True
|
|
2631
|
-
hasOTC = not hasADR and not extractedCoverFacts.get(exchangeFactName, ())
|
|
2632
|
-
# organize by "record" of in hierarchical order of extractionCoverTagNames
|
|
2633
|
-
coverFactRecords = set()
|
|
2634
|
-
def addCoverFactRecord(facts):
|
|
2635
|
-
nameValuePairs = []
|
|
2636
|
-
for f in facts:
|
|
2637
|
-
if isinstance(f, tuple):
|
|
2638
|
-
nameValuePairs.append(f)
|
|
2639
|
-
elif f is not None:
|
|
2640
|
-
nameValuePairs.append( (f.qname.localName, f.xValue) )
|
|
2641
|
-
coverFactRecords.add(tuple(sorted(nameValuePairs)))
|
|
2642
|
-
|
|
2643
|
-
if hasOTC:
|
|
2644
|
-
for contextualFactNames in contextualFactNameSets:
|
|
2645
|
-
for cEqualFacts in cEqualCoverFacts.values():
|
|
2646
|
-
if contextualFactNames == cEqualFacts.keys(): # context has all OTC facts
|
|
2647
|
-
addCoverFactRecord(list(cEqualFacts.values()) + [(exchangeFactName,None)])
|
|
2648
|
-
else:
|
|
2649
|
-
for contextualFactNames in contextualFactNameSets:
|
|
2650
|
-
for cEqualFacts in cEqualCoverFacts.values():
|
|
2651
|
-
if set(contextualFactNames) <= cEqualFacts.keys(): # context has all OTC facts
|
|
2652
|
-
cntx = cEqualFacts[contextualFactNames[0]].context # can be any fact's context as they're all same context
|
|
2653
|
-
classOfStockDim = None
|
|
2654
|
-
for d in cntx.qnameDims.values():
|
|
2655
|
-
if d.dimensionQname.localName in deiCAxes:
|
|
2656
|
-
classOfStockDim = d
|
|
2657
|
-
break
|
|
2658
|
-
if hasADR and (d is None or d.memberQname != deiADRmember):
|
|
2659
|
-
continue
|
|
2660
|
-
rec = [cEqualFacts[name] for name in contextualFactNames]
|
|
2661
|
-
if exchangeFactName in cEqualFacts:
|
|
2662
|
-
rec.append(cEqualFacts[exchangeFactName])
|
|
2663
|
-
addCoverFactRecord(rec)
|
|
2664
|
-
else:
|
|
2665
|
-
for f in extractedCoverFacts[exchangeFactName]:
|
|
2666
|
-
fdims = f.context.qnameDims
|
|
2667
|
-
addThisExchFact = False
|
|
2668
|
-
if exchangeAxisQN in cntx.qnameDims:
|
|
2669
|
-
if cntx.qnameDims[exchangeAxisQN].isEqualTo(fdims.get(exchangeAxisQN)):
|
|
2670
|
-
addThisExchFact = True
|
|
2671
|
-
elif classOfStockDim is not None and classOfStockDim.dimensionQname in fdims:
|
|
2672
|
-
if classOfStockDim.isEqualTo(fdims[classOfStockDim.dimensionQname]):
|
|
2673
|
-
addThisExchFact = True
|
|
2674
|
-
# may need better disaggregation control
|
|
2675
|
-
else:
|
|
2676
|
-
addThisExchFact = True
|
|
2677
|
-
if addThisExchFact:
|
|
2678
|
-
rec.append(f)
|
|
2679
|
-
# override any inherited facts with exch c-equal facts
|
|
2680
|
-
exchCEqualFacts = cEqualCoverFacts[f.context.contextDimAwareHash]
|
|
2681
|
-
for name in contextualFactNames:
|
|
2682
|
-
if name in exchCEqualFacts:
|
|
2683
|
-
rec.append(f)
|
|
2684
|
-
addCoverFactRecord(rec)
|
|
2685
|
-
jsonParam = OrderedDict()
|
|
2686
|
-
if coverFactRecords:
|
|
2687
|
-
jsonParam["coverFacts"] = [dict(keyval for keyval in rec) for rec in sorted(coverFactRecords)]
|
|
2688
|
-
for _objName, _objFacts in sorted(storeDbObjectFacts.items(), key=lambda i:i[0]):
|
|
2689
|
-
if isinstance(_objFacts,dict):
|
|
2690
|
-
if isinstance(next(iter(_objFacts.keys())),tuple): # turn dict into list of objects with axes
|
|
2691
|
-
_orderedObjFacts = []
|
|
2692
|
-
for axes,vals in sorted(_objFacts.items(), key=lambda i:i[0]):
|
|
2693
|
-
_storeDbActionVals = storeDbActions.get(_objName,EMPTY_DICT).get(axes,EMPTY_DICT)
|
|
2694
|
-
_entry = OrderedDict()
|
|
2695
|
-
for k in sorted(vals.keys() | _storeDbActionVals.keys()):
|
|
2696
|
-
if k in vals:
|
|
2697
|
-
_entry[k] = vals[k]
|
|
2698
|
-
if k in _storeDbActionVals:
|
|
2699
|
-
_entry[k] = _storeDbActionVals[k] # overrides for EDGAR
|
|
2700
|
-
_orderedObjFacts.append(_entry)
|
|
2701
|
-
if len(_orderedObjFacts) == 1 and len(axes) == 0: # not an array
|
|
2702
|
-
_orderedObjFacts = _orderedObjFacts[0]
|
|
2703
|
-
else:
|
|
2704
|
-
_orderedObjFacts = OrderedDict((k,v) for k,v in sorted(_objFacts.items()))
|
|
2705
|
-
jsonParam[_objName] = _orderedObjFacts
|
|
2706
|
-
if isFeeTagging:
|
|
2707
|
-
jsonObjType = "fee"
|
|
2708
|
-
testEnvJsonFile = val.params.get("saveFeeFacts")
|
|
2709
|
-
else:
|
|
2710
|
-
jsonObjType = "cover"
|
|
2711
|
-
testEnvJsonFile = val.params.get("saveCoverFacts")
|
|
2712
|
-
modelXbrl.log("INFO-RESULT",
|
|
2713
|
-
"EFM.{}Facts".format(jsonObjType),
|
|
2714
|
-
"Extracted {} facts returned as json parameter".format(jsonObjType),
|
|
2715
|
-
modelXbrl=modelXbrl,
|
|
2716
|
-
json=allowableJsonCharsForEdgar(json.dumps(jsonParam)))
|
|
2717
|
-
if testEnvJsonFile:
|
|
2718
|
-
with open(testEnvJsonFile, "w") as fh:
|
|
2719
|
-
fh.write(allowableJsonCharsForEdgar(json.dumps(jsonParam, indent=3)))
|
|
2720
|
-
|
|
2721
|
-
#6.5.27 footnote elements, etc
|
|
2722
|
-
footnoteLinkNbr = 0
|
|
2723
|
-
if isInlineXbrl and isEFM:
|
|
2724
|
-
_linkEltIter = (linkPrototype
|
|
2725
|
-
for linkKey, links in modelXbrl.baseSets.items()
|
|
2726
|
-
for linkPrototype in links
|
|
2727
|
-
if linkPrototype.modelDocument.type in (ModelDocument.Type.INLINEXBRL, ModelDocument.Type.INLINEXBRLDOCUMENTSET)
|
|
2728
|
-
and linkKey[1] and linkKey[2] and linkKey[3] # fully specified roles
|
|
2729
|
-
and linkKey[0] != "XBRL-footnotes")
|
|
2730
|
-
else:
|
|
2731
|
-
_linkEltIter = xbrlInstRoots[0].iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}footnoteLink")
|
|
2732
|
-
for footnoteLinkElt in _linkEltIter:
|
|
2733
|
-
if isinstance(footnoteLinkElt, (ModelObject,LinkPrototype)):
|
|
2734
|
-
footnoteLinkNbr += 1
|
|
2735
|
-
|
|
2736
|
-
linkrole = footnoteLinkElt.get("{http://www.w3.org/1999/xlink}role")
|
|
2737
|
-
if linkrole != XbrlConst.defaultLinkRole:
|
|
2738
|
-
modelXbrl.error(("EFM.6.05.28.linkrole", "GFM.1.02.20"),
|
|
2739
|
-
_("FootnoteLink %(footnoteLinkNumber)s has disallowed role %(linkrole)s"),
|
|
2740
|
-
modelObject=footnoteLinkElt, footnoteLinkNumber=footnoteLinkNbr, linkrole=linkrole)
|
|
2741
|
-
|
|
2742
|
-
# find modelLink of this footnoteLink
|
|
2743
|
-
# modelLink = modelXbrl.baseSetModelLink(footnoteLinkElt)
|
|
2744
|
-
relationshipSet = modelXbrl.relationshipSet("XBRL-footnotes", linkrole)
|
|
2745
|
-
#if (modelLink is None) or (not relationshipSet):
|
|
2746
|
-
# continue # had no child elements to parse
|
|
2747
|
-
locNbr = 0
|
|
2748
|
-
arcNbr = 0
|
|
2749
|
-
for child in footnoteLinkElt:
|
|
2750
|
-
if isinstance(child,(ModelObject,LocPrototype,ArcPrototype)):
|
|
2751
|
-
xlinkType = child.get("{http://www.w3.org/1999/xlink}type")
|
|
2752
|
-
if (not isinstance(child,ModelInlineFootnote) and
|
|
2753
|
-
(child.namespaceURI != XbrlConst.link or
|
|
2754
|
-
xlinkType not in ("locator", "resource", "arc") or
|
|
2755
|
-
child.localName not in ("loc", "footnote", "footnoteArc"))):
|
|
2756
|
-
modelXbrl.error(("EFM.6.05.27", "GFM.1.02.19"),
|
|
2757
|
-
_("Footnote link %(footnoteLinkNumber)s has a child element %(elementName)s that is not allowed. Please remove it."),
|
|
2758
|
-
edgarCode="du-0527-Footnote-Substitution-Group",
|
|
2759
|
-
modelObject=child, footnoteLinkNumber=footnoteLinkNbr, elementName=child.prefixedName)
|
|
2760
|
-
elif xlinkType == "locator":
|
|
2761
|
-
locNbr += 1
|
|
2762
|
-
locrole = child.get("{http://www.w3.org/1999/xlink}role")
|
|
2763
|
-
if locrole is not None and (disclosureSystem.GFM or \
|
|
2764
|
-
not disclosureSystem.uriAuthorityValid(locrole)):
|
|
2765
|
-
modelXbrl.error(("EFM.6.05.29", "GFM.1.02.21"),
|
|
2766
|
-
_("Footnote locator %(xlinkLabel)s has a custom role, %(role)s that is not allowed. Please replace it with the default footnote role."),
|
|
2767
|
-
edgarCode="du-0529-Footnote-Custom-Loc-Role",
|
|
2768
|
-
modelObject=child, footnoteLinkNumber=footnoteLinkNbr,
|
|
2769
|
-
xlinkLabel=child.xlinkLabel,
|
|
2770
|
-
locNumber=locNbr, role=locrole)
|
|
2771
|
-
href = child.get("{http://www.w3.org/1999/xlink}href")
|
|
2772
|
-
if not href.startswith("#"):
|
|
2773
|
-
modelXbrl.error(("EFM.6.05.32", "GFM.1.02.23"),
|
|
2774
|
-
_("Footnote %(locLabel)s refers to a location, %(locHref)s, that does not begin with '#' so that any change to the file name would render the XBRL invalid."),
|
|
2775
|
-
edgarCode="du-0532-Footnote-Locator-Portable",
|
|
2776
|
-
modelObject=child, footnoteLinkNumber=footnoteLinkNbr, locNumber=locNbr, locHref=href,
|
|
2777
|
-
locLabel=child.get("{http://www.w3.org/1999/xlink}label"))
|
|
2778
|
-
#else:
|
|
2779
|
-
# label = child.get("{http://www.w3.org/1999/xlink}label")
|
|
2780
|
-
elif xlinkType == "arc":
|
|
2781
|
-
arcNbr += 1
|
|
2782
|
-
arcrole = child.get("{http://www.w3.org/1999/xlink}arcrole")
|
|
2783
|
-
if (isEFM and not disclosureSystem.uriAuthorityValid(arcrole)) or \
|
|
2784
|
-
(disclosureSystem.GFM and arcrole != XbrlConst.factFootnote and arcrole != XbrlConst.factExplanatoryFact):
|
|
2785
|
-
modelXbrl.error(("EFM.6.05.30", "GFM.1.02.22"),
|
|
2786
|
-
_("Footnote relationship %(arcToLabel)s has a custom arc role %(arcrole)s that is not allowed. "
|
|
2787
|
-
"Please replace it with the default (fact-footnote) arcrole."),
|
|
2788
|
-
edgarCode="du-0530-Footnote-Custom-Arcrole",
|
|
2789
|
-
modelObject=child, footnoteLinkNumber=footnoteLinkNbr, arcNumber=arcNbr,
|
|
2790
|
-
arcToLabel=child.get("{http://www.w3.org/1999/xlink}to"),
|
|
2791
|
-
arcrole=arcrole)
|
|
2792
|
-
elif xlinkType == "resource" or isinstance(child,ModelInlineFootnote): # footnote
|
|
2793
|
-
footnoterole = child.role if isinstance(child,ModelInlineFootnote) else child.get("{http://www.w3.org/1999/xlink}role")
|
|
2794
|
-
if footnoterole == "":
|
|
2795
|
-
modelXbrl.error(("EFM.6.05.28.missingRole", "GFM.1.2.20"),
|
|
2796
|
-
_("Footnote %(xlinkLabel)s is missing a role. Please provide the default footnote role."),
|
|
2797
|
-
edgarCode="du-0528-Footnote-Role-Missing",
|
|
2798
|
-
modelObject=child, xlinkLabel=getattr(child, "xlinkLabel", None))
|
|
2799
|
-
elif (isEFM and not disclosureSystem.uriAuthorityValid(footnoterole)) or \
|
|
2800
|
-
(disclosureSystem.GFM and footnoterole != XbrlConst.footnote):
|
|
2801
|
-
modelXbrl.error(("EFM.6.05.28", "GFM.1.2.20"),
|
|
2802
|
-
_("Footnote %(xlinkLabel)s has a role %(role)s that is not allowed. "
|
|
2803
|
-
"Please replace it with the default footnote role."),
|
|
2804
|
-
edgarCode="du-0528-Footnote-Custom-Footnote-Role",
|
|
2805
|
-
modelObject=child, xlinkLabel=getattr(child, "xlinkLabel", None),
|
|
2806
|
-
role=footnoterole)
|
|
2807
|
-
if isEFM and not isInlineXbrl: # inline content was validated before and needs continuations assembly
|
|
2808
|
-
ValidateFilingText.validateFootnote(modelXbrl, child)
|
|
2809
|
-
# find modelResource for this element
|
|
2810
|
-
foundFact = False
|
|
2811
|
-
if XmlUtil.text(child) != "" and not isInlineXbrl:
|
|
2812
|
-
if relationshipSet:
|
|
2813
|
-
for relationship in relationshipSet.toModelObject(child):
|
|
2814
|
-
if isinstance(relationship.fromModelObject, ModelFact):
|
|
2815
|
-
foundFact = True
|
|
2816
|
-
break
|
|
2817
|
-
if not foundFact:
|
|
2818
|
-
modelXbrl.error(("EFM.6.05.33", "GFM.1.02.24"),
|
|
2819
|
-
_("The footnote with label %(footnoteLabel)s and text '%(text)s' is not connected to any fact. "
|
|
2820
|
-
"Please remove the footnote, or link it to a fact."),
|
|
2821
|
-
edgarCode="cp-0533-Dangling-Footnote",
|
|
2822
|
-
modelObject=child, footnoteLinkNumber=footnoteLinkNbr,
|
|
2823
|
-
footnoteLabel=getattr(child, "xlinkLabel", None),
|
|
2824
|
-
text=XmlUtil.text(child)[:100])
|
|
2825
|
-
val.modelXbrl.profileActivity("... filer rfootnotes checks", minTimeToShow=1.0)
|
|
2826
|
-
|
|
2827
|
-
# entry point schema checks
|
|
2828
|
-
elif modelXbrl.modelDocument.type == ModelDocument.Type.SCHEMA:
|
|
2829
|
-
pass
|
|
2830
|
-
|
|
2831
|
-
# inline-only checks
|
|
2832
|
-
if isInlineXbrl and isEFM:
|
|
2833
|
-
hiddenEltIds = {}
|
|
2834
|
-
presentedHiddenEltIds = defaultdict(list)
|
|
2835
|
-
eligibleForTransformHiddenFacts = []
|
|
2836
|
-
requiredToDisplayFacts = []
|
|
2837
|
-
requiredToDisplayFactIds = {}
|
|
2838
|
-
for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements: # ix root elements
|
|
2839
|
-
ixdsHtmlTree = ixdsHtmlRootElt.getroottree()
|
|
2840
|
-
if ixdsHtmlRootElt.tag in ("html", "xhtml") or (
|
|
2841
|
-
isinstance(ixdsHtmlRootElt, ModelObject) and not ixdsHtmlRootElt.namespaceURI):
|
|
2842
|
-
modelXbrl.error("EFM.5.02.05.xhtmlNamespaceMissing",
|
|
2843
|
-
_("InlineXBRL root element <%(element)s> MUST be html and have the xhtml namespace."),
|
|
2844
|
-
modelObject=ixdsHtmlRootElt, element=ixdsHtmlRootElt.tag)
|
|
2845
|
-
nsRequiredPrefixes = {"http://www.w3.org/1999/xhtml": "xhtml",
|
|
2846
|
-
"http://www.xbrl.org/2013/inlineXBRL": "ix",
|
|
2847
|
-
"http://www.xbrl.org/inlineXBRL/transformation/2015-02-26": "ixt",
|
|
2848
|
-
"http://www.sec.gov/inlineXBRL/transformation/2015-08-31": "ixt-sec"}
|
|
2849
|
-
for prefix, ns in ((None, "http://www.w3.org/1999/xhtml"),
|
|
2850
|
-
("ix", "http://www.xbrl.org/2013/inlineXBRL")):
|
|
2851
|
-
for _prefix, _ns in ixdsHtmlRootElt.nsmap.items():
|
|
2852
|
-
if _ns == ns and _prefix != prefix:
|
|
2853
|
-
modelXbrl.error("EFM.5.02.05.standardNamespacePrefix",
|
|
2854
|
-
_("The prefix %(submittedPrefix)s must be replaced by %(recommendedPrefix)s for standard namespace %(namespace)s."),
|
|
2855
|
-
edgarCode="ix-0502-Standard-Namespace-Prefix",
|
|
2856
|
-
modelObject=ixdsHtmlRootElt, submittedPrefix=_prefix, recommendedPrefix=prefix, namespace=ns)
|
|
2857
|
-
ixNStag = ixdsHtmlRootElt.modelDocument.ixNStag
|
|
2858
|
-
ixTags = set(ixNStag + ln for ln in ("nonNumeric", "nonFraction", "references", "relationship"))
|
|
2859
|
-
unsupportedTrFacts = []
|
|
2860
|
-
unsupportedTrNamespaces = set()
|
|
2861
|
-
unsupportedNamespacePrefixes = defaultdict(set)
|
|
2862
|
-
for tag in ixTags:
|
|
2863
|
-
for ixElt in ixdsHtmlRootElt.iterdescendants(tag=tag):
|
|
2864
|
-
if isinstance(ixElt,ModelObject):
|
|
2865
|
-
if ixElt.get("target"):
|
|
2866
|
-
modelXbrl.error("EFM.5.02.05.targetDisallowed",
|
|
2867
|
-
_("Inline element %(localName)s has disallowed target attribute '%(target)s'."),
|
|
2868
|
-
modelObject=ixElt, localName=ixElt.elementQname, target=ixElt.get("target"))
|
|
2869
|
-
if isinstance(ixElt, ModelInlineFact):
|
|
2870
|
-
format = ixElt.format
|
|
2871
|
-
if format:
|
|
2872
|
-
if format.namespaceURI not in ixTrRegistries:
|
|
2873
|
-
unsupportedTrNamespaces.add(format.namespaceURI)
|
|
2874
|
-
unsupportedTrFacts.append(ixElt)
|
|
2875
|
-
elif format.prefix != ixTrRegistries[format.namespaceURI]:
|
|
2876
|
-
unsupportedNamespacePrefixes[(format.prefix,format.namespaceURI)].add(ixElt)
|
|
2877
|
-
if unsupportedTrFacts:
|
|
2878
|
-
modelXbrl.error("EFM.5.02.05.12.unupportedTransformationRegistry",
|
|
2879
|
-
_("Inline elements have disallowed transformation registries %(unsupportedRegistries)s."),
|
|
2880
|
-
edgarCode="ix-0512-Unsupported-Transformation-Registry",
|
|
2881
|
-
modelObject=unsupportedTrFacts, unsupportedRegistries=", ".join(sorted(unsupportedTrNamespaces)))
|
|
2882
|
-
for (pfx,ns),facts in unsupportedNamespacePrefixes.items():
|
|
2883
|
-
modelXbrl.error("EFM.5.02.05.standardNamespacePrefix",
|
|
2884
|
-
_("The prefix %(submittedPrefix)s must be replaced by %(recommendedPrefix)s for standard namespace %(namespace)s."),
|
|
2885
|
-
edgarCode="ix-0502-Standard-Namespace-Prefix",
|
|
2886
|
-
modelObject=facts, submittedPrefix=pfx, recommendedPrefix=ixTrRegistries[ns], namespace=ns)
|
|
2887
|
-
|
|
2888
|
-
del unsupportedTrFacts, unsupportedTrNamespaces, unsupportedNamespacePrefixes
|
|
2889
|
-
for ixElt in ixdsHtmlRootElt.iterdescendants(tag=ixNStag+"tuple"):
|
|
2890
|
-
if isinstance(ixElt,ModelObject):
|
|
2891
|
-
modelXbrl.error("EFM.5.02.05.tupleDisallowed",
|
|
2892
|
-
_("Inline tuple %(qname)s is disallowed."),
|
|
2893
|
-
modelObject=ixElt, qname=ixElt.qname)
|
|
2894
|
-
for ixElt in ixdsHtmlRootElt.iterdescendants(tag=ixNStag+"fraction"):
|
|
2895
|
-
if isinstance(ixElt,ModelObject):
|
|
2896
|
-
modelXbrl.error("EFM.5.02.05.fractionDisallowed",
|
|
2897
|
-
_("Inline fraction %(qname)s is disallowed."),
|
|
2898
|
-
modelObject=ixElt, qname=ixElt.qname)
|
|
2899
|
-
if ixdsHtmlRootElt.getroottree().docinfo.doctype:
|
|
2900
|
-
modelXbrl.error("EFM.5.02.05.doctypeDisallowed",
|
|
2901
|
-
_("Inline HTML %(doctype)s is disallowed."),
|
|
2902
|
-
modelObject=ixdsHtmlRootElt, doctype=modelXbrl.modelDocument.xmlDocument.docinfo.doctype)
|
|
2903
|
-
|
|
2904
|
-
for ixHiddenElt in ixdsHtmlRootElt.iterdescendants(tag=ixNStag + "hidden"):
|
|
2905
|
-
for tag in (ixNStag + "nonNumeric", ixNStag+"nonFraction"):
|
|
2906
|
-
for ixElt in ixHiddenElt.iterdescendants(tag=tag):
|
|
2907
|
-
if (getattr(ixElt, "xValid", 0) >= VALID and # may not be validated
|
|
2908
|
-
(ixElt.qname in coverVisibleQNames
|
|
2909
|
-
or not hideableNamespacesPattern.match(ixElt.qname.namespaceURI)) and
|
|
2910
|
-
(not isRRorOEF or not rrUntransformableEltsPattern.match(ixElt.qname.localName)
|
|
2911
|
-
or abbreviatedNamespace(ixElt.qname.namespaceURI, NOYEAR) not in ("rr","oef"))):
|
|
2912
|
-
if (ixElt.concept.baseXsdType not in untransformableTypes and
|
|
2913
|
-
not ixElt.isNil):
|
|
2914
|
-
eligibleForTransformHiddenFacts.append(ixElt)
|
|
2915
|
-
elif ixElt.id is None:
|
|
2916
|
-
requiredToDisplayFacts.append(ixElt)
|
|
2917
|
-
if ixElt.id:
|
|
2918
|
-
hiddenEltIds[ixElt.id] = ixElt
|
|
2919
|
-
for ixElt in ixdsHtmlRootElt.iterdescendants(tag=ixNStag+"footnote"):
|
|
2920
|
-
if isinstance(ixElt,ModelInlineFootnote) and ixElt.stringValue:
|
|
2921
|
-
if not modelXbrl.relationshipSet("XBRL-footnotes").toModelObject(ixElt):
|
|
2922
|
-
modelXbrl.error(("EFM.6.05.33", "GFM.1.02.24"),
|
|
2923
|
-
_("The footnote with id %(footnoteId)s and text '%(text)s' is not connected to any fact. "
|
|
2924
|
-
"Please remove the footnote, or link it to a fact."),
|
|
2925
|
-
edgarCode="cp-0533-Dangling-Footnote",
|
|
2926
|
-
modelObject=ixElt, footnoteId=ixElt.id,
|
|
2927
|
-
text=ixElt.stringValue[:100])
|
|
2928
|
-
if eligibleForTransformHiddenFacts:
|
|
2929
|
-
modelXbrl.warning("EFM.5.02.05.14.hidden-fact-eligible-for-transform",
|
|
2930
|
-
_("%(countEligible)s fact(s) appearing in ix:hidden were eligible for transformation: %(elements)s"),
|
|
2931
|
-
edgarCode="ix-0514-Hidden-Fact-Eligible-For-Transform",
|
|
2932
|
-
modelObject=eligibleForTransformHiddenFacts,
|
|
2933
|
-
countEligible=len(eligibleForTransformHiddenFacts),
|
|
2934
|
-
elements=", ".join(sorted(set(str(f.qname) for f in eligibleForTransformHiddenFacts))))
|
|
2935
|
-
for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
|
|
2936
|
-
for ixElt in ixdsHtmlRootElt.getroottree().iterfind("//{http://www.w3.org/1999/xhtml}*[@style]"):
|
|
2937
|
-
hiddenFactRefMatch = styleIxHiddenPattern.match(ixElt.get("style",""))
|
|
2938
|
-
if hiddenFactRefMatch:
|
|
2939
|
-
hiddenFactRef = hiddenFactRefMatch.group(2)
|
|
2940
|
-
if hiddenFactRef not in hiddenEltIds:
|
|
2941
|
-
modelXbrl.error("EFM.5.02.05.14.hidden-fact-not-found",
|
|
2942
|
-
_("The value of the -sec-ix-hidden style property, %(id)s, does not correspond to the id of any hidden fact."),
|
|
2943
|
-
edgarCode="ix-0514-Hidden-Fact-Not-Found",
|
|
2944
|
-
modelObject=ixElt, id=hiddenFactRef)
|
|
2945
|
-
else:
|
|
2946
|
-
presentedHiddenEltIds[hiddenFactRef].append(ixElt)
|
|
2947
|
-
for hiddenFactRef, ixElts in presentedHiddenEltIds.items():
|
|
2948
|
-
if len(ixElts) > 1 and hiddenFactRef in hiddenEltIds:
|
|
2949
|
-
fact = hiddenEltIds[hiddenFactRef]
|
|
2950
|
-
modelXbrl.warning("EFM.5.02.05.14.hidden-fact-multiple-references",
|
|
2951
|
-
_("Fact %(element)s, id %(id)s, is referenced from %(countReferences)s elements."),
|
|
2952
|
-
edgarCode="ix-0514-Hidden-Fact-Multiple-References",
|
|
2953
|
-
modelObject=ixElts + [fact], id=hiddenFactRef, element=fact.qname, countReferences=len(ixElts))
|
|
2954
|
-
for hiddenEltId, ixElt in hiddenEltIds.items():
|
|
2955
|
-
if (hiddenEltId not in presentedHiddenEltIds and
|
|
2956
|
-
getattr(ixElt, "xValid", 0) >= VALID and # may not be validated
|
|
2957
|
-
(ixElt.qname in coverVisibleQNames
|
|
2958
|
-
or not ixElt.qname.namespaceURI.startswith("http://xbrl.sec.gov/dei/")) and
|
|
2959
|
-
(ixElt.concept.baseXsdType in untransformableTypes or ixElt.isNil)):
|
|
2960
|
-
requiredToDisplayFacts.append(ixElt)
|
|
2961
|
-
undisplayedCoverFacts = dict((f, coverVisibleQNames[f.qname])
|
|
2962
|
-
for f in requiredToDisplayFacts
|
|
2963
|
-
if f.qname in coverVisibleQNames)
|
|
2964
|
-
for f in undisplayedCoverFacts:
|
|
2965
|
-
requiredToDisplayFacts.remove(f)
|
|
2966
|
-
if requiredToDisplayFacts:
|
|
2967
|
-
modelXbrl.warning("EFM.5.02.05.14.hidden-fact-not-referenced",
|
|
2968
|
-
_("%(countUnreferenced)s fact(s) appearing in ix:hidden were not referenced by any -sec-ix-hidden style property: %(elements)s"),
|
|
2969
|
-
edgarCode="ix-0514-Hidden-Fact-Not-Referenced",
|
|
2970
|
-
modelObject=requiredToDisplayFacts,
|
|
2971
|
-
countUnreferenced=len(requiredToDisplayFacts),
|
|
2972
|
-
elements=", ".join(sorted(set(str(f.qname) for f in requiredToDisplayFacts))))
|
|
2973
|
-
if undisplayedCoverFacts:
|
|
2974
|
-
for level, err, verb in (("WARNING", False, "should"), ("ERROR", True, "MUST")):
|
|
2975
|
-
facts = [f for f, _err in undisplayedCoverFacts.items() if _err == err]
|
|
2976
|
-
if facts:
|
|
2977
|
-
modelXbrl.log(level, "EFM.6.05.45.coverPageFactNotVisible",
|
|
2978
|
-
_("Submission type %(subType)s has %(countUnreferenced)s cover page fact(s) in ix:hidden that %(verb)s be visible or referenced by an -sec-ix-hidden style property: %(elements)s"),
|
|
2979
|
-
edgarCode="dq-0545-Cover-Page-Fact-Not-Visible",
|
|
2980
|
-
modelObject=facts, subType=submissionType, countUnreferenced=len(facts), verb=verb,
|
|
2981
|
-
elements=", ".join(sorted(set(f.qname.localName for f in facts))))
|
|
2982
|
-
del facts
|
|
2983
|
-
del eligibleForTransformHiddenFacts, hiddenEltIds, presentedHiddenEltIds, requiredToDisplayFacts, undisplayedCoverFacts
|
|
2984
|
-
# all-labels and references checks
|
|
2985
|
-
defaultLangStandardLabels = {}
|
|
2986
|
-
for concept in modelXbrl.qnameConcepts.values():
|
|
2987
|
-
# conceptHasDefaultLangStandardLabel = False
|
|
2988
|
-
for modelLabelRel in labelsRelationshipSet.fromModelObject(concept):
|
|
2989
|
-
if modelLabelRel.modelDocument.inDTS: # ignore documentation labels added by EdgarRenderer not in DTS
|
|
2990
|
-
modelLabel = modelLabelRel.toModelObject
|
|
2991
|
-
role = modelLabel.role
|
|
2992
|
-
text = modelLabel.text
|
|
2993
|
-
lang = modelLabel.xmlLang
|
|
2994
|
-
if role == XbrlConst.documentationLabel:
|
|
2995
|
-
if concept.modelDocument.targetNamespace in disclosureSystem.standardTaxonomiesDict:
|
|
2996
|
-
modelXbrl.error(("EFM.6.10.05", "GFM.1.05.05"),
|
|
2997
|
-
_("Your filing attempted to add a new definition, '%(text)s', to an existing concept in the standard taxonomy, %(concept)s. Please remove this definition."),
|
|
2998
|
-
edgarCode="cp-1005-Custom-Documentation-Standard-Element",
|
|
2999
|
-
modelObject=modelLabel, concept=concept.qname, text=text)
|
|
3000
|
-
elif text and lang and disclosureSystem.defaultXmlLang and lang.startswith(disclosureSystem.defaultXmlLang):
|
|
3001
|
-
if role == XbrlConst.standardLabel:
|
|
3002
|
-
if text in defaultLangStandardLabels:
|
|
3003
|
-
concept2, modelLabel2 = defaultLangStandardLabels[text]
|
|
3004
|
-
modelXbrl.error(("EFM.6.10.04", "GFM.1.05.04"),
|
|
3005
|
-
_("More than one element has %(text)s as its English standard label (%(concept)s and %(concept2)s). "
|
|
3006
|
-
"Please change or remove all but one label."),
|
|
3007
|
-
edgarCode="du-1004-English-Standard-Labels-Duplicated",
|
|
3008
|
-
modelObject=(concept, modelLabel, concept2, modelLabel2),
|
|
3009
|
-
concept=concept.qname,
|
|
3010
|
-
concept2=concept2.qname,
|
|
3011
|
-
lang=disclosureSystem.defaultLanguage, text=text[:80])
|
|
3012
|
-
else:
|
|
3013
|
-
defaultLangStandardLabels[text] = (concept, modelLabel)
|
|
3014
|
-
# conceptHasDefaultLangStandardLabel = True
|
|
3015
|
-
if len(text) > 511:
|
|
3016
|
-
modelXbrl.error(("EFM.6.10.06", "GFM.1.05.06"),
|
|
3017
|
-
_("Element %(concept)s, label length %(length)s, has more than 511 characters or contains a left-angle-bracket character in the label for role %(role)s. "
|
|
3018
|
-
"Please correct the label."),
|
|
3019
|
-
edgarCode="rq-1006-Label-Disallowed",
|
|
3020
|
-
modelObject=modelLabel, concept=concept.qname, role=role, length=len(text), text=text[:80])
|
|
3021
|
-
match = modelXbrl.modelManager.disclosureSystem.labelCheckPattern.search(text)
|
|
3022
|
-
if match:
|
|
3023
|
-
modelXbrl.error(("EFM.6.10.06", "GFM.1.05.07"),
|
|
3024
|
-
'Label for concept %(concept)s role %(role)s has disallowed characters: "%(text)s"',
|
|
3025
|
-
modelObject=modelLabel, concept=concept.qname, role=role, text=match.group())
|
|
3026
|
-
if (text is not None and len(text) > 0 and
|
|
3027
|
-
modelXbrl.modelManager.disclosureSystem.labelTrimPattern and
|
|
3028
|
-
(modelXbrl.modelManager.disclosureSystem.labelTrimPattern.match(text[0]) or \
|
|
3029
|
-
modelXbrl.modelManager.disclosureSystem.labelTrimPattern.match(text[-1]))):
|
|
3030
|
-
modelXbrl.error(("EFM.6.10.08", "GFM.1.05.08"),
|
|
3031
|
-
_("The label %(text)s of element %(concept)s has leading or trailing white space in role %(role)s for lang %(lang)s. Please remove it."),
|
|
3032
|
-
edgarCode="du-1008-Label-Not-Trimmed",
|
|
3033
|
-
modelObject=modelLabel, concept=concept.qname, role=role, lang=lang, text=text)
|
|
3034
|
-
for modelRefRel in referencesRelationshipSetWithProhibits.fromModelObject(concept):
|
|
3035
|
-
if modelRefRel.modelDocument.inDTS: # ignore references added by EdgarRenderer that are not in DTS
|
|
3036
|
-
modelReference = modelRefRel.toModelObject
|
|
3037
|
-
text = XmlUtil.innerText(modelReference)
|
|
3038
|
-
#6.18.1 no reference to company extension concepts
|
|
3039
|
-
if (concept.modelDocument.targetNamespace not in disclosureSystem.standardTaxonomiesDict and
|
|
3040
|
-
concept.modelDocument.targetNamespace not in val.otherStandardTaxonomies):
|
|
3041
|
-
modelXbrl.error(("EFM.6.18.01", "GFM.1.9.1"),
|
|
3042
|
-
_("Your filing provides a reference, '%(xml)s', for an custom concept in extension taxonomy, %(concept)s. "
|
|
3043
|
-
"Please remove this reference."),
|
|
3044
|
-
edgarCode="cp-1801-Custom-Element-Has-Reference",
|
|
3045
|
-
modelObject=modelReference, concept=concept.qname, text=text, xml=XmlUtil.xmlstring(modelReference, stripXmlns=True, contentsOnly=True))
|
|
3046
|
-
elif isEFM and not isStandardUri(val, modelRefRel.modelDocument.uri) and concept.modelDocument.targetNamespace not in val.otherStandardTaxonomies:
|
|
3047
|
-
#6.18.2 no extension to add or remove references to standard concepts
|
|
3048
|
-
modelXbrl.error(("EFM.6.18.02"),
|
|
3049
|
-
_("Your filing attempted to add a new reference, '%(xml)s', to an existing concept in the standard taxonomy, %(concept)s. "
|
|
3050
|
-
"Please remove this reference."),
|
|
3051
|
-
edgarCode="cp-1802-Standard-Element-Has-Reference",
|
|
3052
|
-
modelObject=modelReference, concept=concept.qname, text=text, xml=XmlUtil.xmlstring(modelReference, stripXmlns=True, contentsOnly=True))
|
|
3053
|
-
|
|
3054
|
-
# role types checks
|
|
3055
|
-
# 6.7.10 only one role type declaration in DTS
|
|
3056
|
-
for roleURI, modelRoleTypes in modelXbrl.roleTypes.items():
|
|
3057
|
-
countInDTS = sum(1 for m in modelRoleTypes if m.modelDocument.inDTS)
|
|
3058
|
-
if countInDTS > 1:
|
|
3059
|
-
modelXbrl.error(("EFM.6.07.10", "GFM.1.03.10"),
|
|
3060
|
-
_("Role %(roleType)s was declared more than once (%(numberOfDeclarations)s times.). "
|
|
3061
|
-
"Please remove all but one declaration."),
|
|
3062
|
-
edgarCode="du-0710-Role-Type-Duplicates",
|
|
3063
|
-
modelObject=modelRoleTypes, roleType=roleURI, numberOfDeclarations=countInDTS)
|
|
3064
|
-
# 6.7.14 only one arcrole type declaration in DTS
|
|
3065
|
-
for arcroleURI, modelRoleTypes in modelXbrl.arcroleTypes.items():
|
|
3066
|
-
countInDTS = sum(1 for m in modelRoleTypes if m.modelDocument.inDTS)
|
|
3067
|
-
if countInDTS > 1:
|
|
3068
|
-
modelXbrl.error(("EFM.6.07.14", "GFM.1.03.16"),
|
|
3069
|
-
_("Relationship arc role %(arcroleType)s is declared more than once (%(numberOfDeclarations)s duplicates). "
|
|
3070
|
-
"Please remove all but one of them."),
|
|
3071
|
-
edgarCode="du-0714-Arcrole-Type-Duplicates",
|
|
3072
|
-
modelObject=modelRoleTypes, arcroleType=arcroleURI, numberOfDeclarations=countInDTS )
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
val.modelXbrl.profileActivity("... filer concepts checks", minTimeToShow=1.0)
|
|
3076
|
-
|
|
3077
|
-
del defaultLangStandardLabels #dereference
|
|
3078
|
-
|
|
3079
|
-
# checks on all documents: instance, schema, instance
|
|
3080
|
-
val.hasExtensionSchema = False
|
|
3081
|
-
if not isFtJson:
|
|
3082
|
-
checkFilingDTS(val, modelXbrl.modelDocument, isEFM, isGFM, [])
|
|
3083
|
-
val.modelXbrl.profileActivity("... filer DTS checks", minTimeToShow=1.0)
|
|
3084
|
-
|
|
3085
|
-
# checks for namespace clashes
|
|
3086
|
-
def elementsReferencingTxClass(txClass):
|
|
3087
|
-
return set(rd.referringModelObject
|
|
3088
|
-
for t in flattenSequence(txClass)
|
|
3089
|
-
for doc in modelXbrl.urlDocs.values()
|
|
3090
|
-
for d, rd in doc.referencesDocument.items()
|
|
3091
|
-
if t in abbreviatedNamespace(d.targetNamespace,WITHYEARandWILD))
|
|
3092
|
-
if isEFM:
|
|
3093
|
-
t = set(conflictClassFromNamespace(d.targetNamespace) for d in modelXbrl.urlDocs.values())
|
|
3094
|
-
t &= compatibleTaxonomies["checked-taxonomies"] # only consider checked taxonomy classes
|
|
3095
|
-
conflictClass = None
|
|
3096
|
-
for ti, ts in compatibleTaxonomies["compatible-classes"].items(): # OrderedDict
|
|
3097
|
-
if ti in t:
|
|
3098
|
-
conflictClasses = t - {ti} - ts
|
|
3099
|
-
if conflictClasses:
|
|
3100
|
-
conflictClass = "-".join([ti] + sorted(conflictClasses))
|
|
3101
|
-
break # match found
|
|
3102
|
-
if not conflictClass: # look for same taxonomy class in multiple years
|
|
3103
|
-
for ti in t:
|
|
3104
|
-
tiClass = ti.partition('/')[0]
|
|
3105
|
-
if any(ts.startswith(tiClass) for ts in (t - {ti})):
|
|
3106
|
-
conflictClasses = sorted(ts for ts in t if ts.startswith(tiClass))
|
|
3107
|
-
conflictClass = "-".join(conflictClasses)
|
|
3108
|
-
if conflictClass:
|
|
3109
|
-
modelXbrl.error("EFM.6.22.03.incompatibleSchemas",
|
|
3110
|
-
_("References for conflicting standard taxonomies %(conflictClass)s are not allowed in same DTS %(namespaceConflicts)s"),
|
|
3111
|
-
edgarCode="cp-2203-Incompatible-Taxonomy-Versions", conflictClass=conflictClass,
|
|
3112
|
-
modelObject=elementsReferencingTxClass(conflictClasses), namespaceConflicts=", ".join(sorted(t)))
|
|
3113
|
-
if any(ti.startswith("rr/") for ti in t) and deiDocumentType not in docTypesRequiringRrSchema:
|
|
3114
|
-
modelXbrl.error("EFM.6.22.03.incompatibleTaxonomyDocumentType",
|
|
3115
|
-
_("Taxonomy class %(conflictClass)s may not be used with document type %(documentType)s"),
|
|
3116
|
-
modelObject=elementsReferencingTxClass("rr/*"), conflictClass="rr/*", documentType=deiDocumentType)
|
|
3117
|
-
if any(ti.startswith("ifrs/") for ti in t) and deiDocumentType in docTypesNotAllowingIfrs:
|
|
3118
|
-
modelXbrl.error("EFM.6.22.03.incompatibleTaxonomyDocumentType",
|
|
3119
|
-
_("Taxonomy class %(conflictClass)s may not be used with document type %(documentType)s"),
|
|
3120
|
-
modelObject=elementsReferencingTxClass("ifrs/*"), conflictClass="ifrs/*", documentType=deiDocumentType)
|
|
3121
|
-
if isInlineXbrl and deiDocumentType in docTypesNotAllowingInlineXBRL:
|
|
3122
|
-
modelXbrl.error("EFM.6.22.03.incompatibleInlineDocumentType",
|
|
3123
|
-
_("Inline XBRL may not be used with document type %(documentType)s"),
|
|
3124
|
-
modelObject=modelXbrl, conflictClass="inline XBRL", documentType=deiDocumentType)
|
|
3125
|
-
''' removed by EER-434
|
|
3126
|
-
if deiDocumentType is not None and not val.hasExtensionSchema and deiDocumentType != "L SDR": # and disclosureSystemVersion[0] <= 58:
|
|
3127
|
-
modelXbrl.error("EFM.6.03.10",
|
|
3128
|
-
_("%(documentType)s report is missing a extension schema file."),
|
|
3129
|
-
edgarCode="cp-0310-Missing-Schema",
|
|
3130
|
-
modelObject=modelXbrl, documentType=deiDocumentType)
|
|
3131
|
-
'''
|
|
3132
|
-
|
|
3133
|
-
# 6.7.12: check link role orders
|
|
3134
|
-
if submissionType not in submissionTypesExemptFromRoleOrder and deiDocumentType not in docTypesExemptFromRoleOrder:
|
|
3135
|
-
seqDefRoleTypes = []
|
|
3136
|
-
for roleURI in modelXbrl.relationshipSet(XbrlConst.parentChild).linkRoleUris:
|
|
3137
|
-
for roleType in modelXbrl.roleTypes.get(roleURI,()):
|
|
3138
|
-
match = efmRoleDefinitionPattern.match(roleType.definitionNotStripped)
|
|
3139
|
-
if match and modelXbrl.relationshipSet(XbrlConst.parentChild, roleURI).modelRelationships:
|
|
3140
|
-
seqDefRoleTypes.append((match.group(1), roleType))
|
|
3141
|
-
priorLevel = level = (0, None, None, None) # (sort order, level, description)
|
|
3142
|
-
for seq, roleType in sorted(seqDefRoleTypes, key=lambda s: s[0]): # sort on sequence only
|
|
3143
|
-
definition = roleType.definitionNotStripped
|
|
3144
|
-
if '- Document - ' in definition: level = (0, "0, Cover", definition, roleType)
|
|
3145
|
-
elif ' - Statement - ' in definition: level = (1, "1, Statement", definition, roleType)
|
|
3146
|
-
elif ' (Detail' in definition: level = (5, "4, Detail", definition, roleType)
|
|
3147
|
-
elif ' (Table' in definition: level = (4, "3, Table", definition, roleType)
|
|
3148
|
-
elif ' (Polic' in definition: level = (3, "2, Policy", definition, roleType)
|
|
3149
|
-
else: level = (2, "1, Note", definition, roleType)
|
|
3150
|
-
if priorLevel[1] is not None and level[0] < priorLevel[0]:
|
|
3151
|
-
modelXbrl.warning("EFM.6.07.12.presentationBaseSetOrder",
|
|
3152
|
-
_("Role '%(descriptionX)s', a level %(levelX)s role, appears before '%(descriptionY)s', a level %(levelY)s role."),
|
|
3153
|
-
edgarCode="dq-0712-Presentation-Base-Set-Order",
|
|
3154
|
-
modelObject=(priorLevel[3], level[3]), descriptionX=priorLevel[2], levelX=priorLevel[1],
|
|
3155
|
-
descriptionY=level[2], levelY=level[1])
|
|
3156
|
-
priorLevel = level
|
|
3157
|
-
del seqDefRoleTypes, priorLevel, level # dereference
|
|
3158
|
-
|
|
3159
|
-
conceptRelsUsedWithPreferredLabels = defaultdict(list)
|
|
3160
|
-
usedCalcsPresented = defaultdict(set) # pairs of concepts objectIds used in calc
|
|
3161
|
-
usedCalcFromTosELR = {}
|
|
3162
|
-
localPreferredLabels = defaultdict(set)
|
|
3163
|
-
drsELRs = set()
|
|
3164
|
-
|
|
3165
|
-
# do calculation, then presentation, then other arcroles
|
|
3166
|
-
val.summationItemRelsSetAllELRs = modelXbrl.relationshipSet(XbrlConst.summationItems)
|
|
3167
|
-
for arcroleFilter in (XbrlConst.summationItem, XbrlConst.summationItem11, XbrlConst.parentChild, "*"):
|
|
3168
|
-
for baseSetKey, baseSetModelLinks in modelXbrl.baseSets.items():
|
|
3169
|
-
arcrole, ELR, linkqname, arcqname = baseSetKey
|
|
3170
|
-
if ELR and linkqname and arcqname and not arcrole.startswith("XBRL-"):
|
|
3171
|
-
# assure summationItem, then parentChild, then others
|
|
3172
|
-
if not (arcroleFilter == arcrole or
|
|
3173
|
-
arcroleFilter == "*" and arcrole not in (XbrlConst.summationItem, XbrlConst.summationItem11, XbrlConst.parentChild)):
|
|
3174
|
-
continue
|
|
3175
|
-
ineffectiveArcs = ModelRelationshipSet.ineffectiveArcs(baseSetModelLinks, arcrole)
|
|
3176
|
-
#validate ineffective arcs
|
|
3177
|
-
for modelRel in ineffectiveArcs:
|
|
3178
|
-
if isinstance(modelRel.fromModelObject, ModelObject) and isinstance(modelRel.toModelObject, ModelObject):
|
|
3179
|
-
modelXbrl.error(("EFM.6.09.03", "GFM.1.04.03"),
|
|
3180
|
-
_("The %(arcrole)s relationship from %(conceptFrom)s to %(conceptTo)s, link role %(linkroleDefinition)s, in the submission is ineffectual. Please remove or correct the relationship."),
|
|
3181
|
-
edgarCode="du-0903-Relationship-Ineffectual",
|
|
3182
|
-
modelObject=modelRel, arc=modelRel.qname, arcrole=modelRel.arcrole,
|
|
3183
|
-
linkrole=modelRel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(modelRel.linkrole),
|
|
3184
|
-
conceptFrom=modelRel.fromModelObject.qname, conceptTo=modelRel.toModelObject.qname,
|
|
3185
|
-
ineffectivity=modelRel.ineffectivity)
|
|
3186
|
-
if arcrole == XbrlConst.parentChild:
|
|
3187
|
-
isStatementSheet = any(linkroleDefinitionStatementSheet.match(roleType.definition or '')
|
|
3188
|
-
for roleType in val.modelXbrl.roleTypes.get(ELR,()))
|
|
3189
|
-
conceptsPresented = set()
|
|
3190
|
-
# 6.12.2 check for distinct order attributes
|
|
3191
|
-
parentChildRels = modelXbrl.relationshipSet(arcrole, ELR)
|
|
3192
|
-
for relFrom, siblingRels in parentChildRels.fromModelObjects().items():
|
|
3193
|
-
targetConceptPreferredLabels = defaultdict(dict)
|
|
3194
|
-
orderRels = {}
|
|
3195
|
-
firstRel = True
|
|
3196
|
-
relFromUsed = True
|
|
3197
|
-
for rel in siblingRels:
|
|
3198
|
-
if firstRel:
|
|
3199
|
-
firstRel = False
|
|
3200
|
-
if relFrom in conceptsUsed:
|
|
3201
|
-
conceptsUsed[relFrom] = True # 6.12.3, has a pres relationship
|
|
3202
|
-
relFromUsed = True
|
|
3203
|
-
relTo = rel.toModelObject
|
|
3204
|
-
preferredLabel = rel.preferredLabel
|
|
3205
|
-
if relTo in conceptsUsed:
|
|
3206
|
-
conceptsUsed[relTo] = True # 6.12.3, has a pres relationship
|
|
3207
|
-
if preferredLabel and preferredLabel != "":
|
|
3208
|
-
conceptRelsUsedWithPreferredLabels[relTo].append(rel)
|
|
3209
|
-
# 6.12.5 distinct preferred labels in base set
|
|
3210
|
-
preferredLabels = targetConceptPreferredLabels[relTo]
|
|
3211
|
-
if preferredLabel in preferredLabels:
|
|
3212
|
-
if preferredLabel in preferredLabels:
|
|
3213
|
-
rel2, relTo2 = preferredLabels[preferredLabel]
|
|
3214
|
-
else:
|
|
3215
|
-
rel2 = relTo2 = None
|
|
3216
|
-
modelXbrl.error(("EFM.6.12.05", "GFM.1.06.05"),
|
|
3217
|
-
_("Relationships from %(fromConcept)s to %(concept)s in role %(linkroleDefinition)s do not have distinct values for "
|
|
3218
|
-
"the preferredLabel attribute, %(preferredLabel)s. Change all but one value of preferredLabel."),
|
|
3219
|
-
edgarCode="rq-1205-Preferred-Label-Duplicates",
|
|
3220
|
-
modelObject=(rel, relTo, rel2, relTo2),
|
|
3221
|
-
concept=relTo.qname, fromConcept=rel.fromModelObject.qname,
|
|
3222
|
-
preferredLabel=preferredLabel, linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole))
|
|
3223
|
-
else:
|
|
3224
|
-
preferredLabels[preferredLabel] = (rel, relTo)
|
|
3225
|
-
if relFromUsed:
|
|
3226
|
-
# 6.14.5
|
|
3227
|
-
conceptsPresented.add(relFrom.objectIndex)
|
|
3228
|
-
conceptsPresented.add(relTo.objectIndex)
|
|
3229
|
-
order = rel.order
|
|
3230
|
-
if order in orderRels and relTo is not None:
|
|
3231
|
-
modelXbrl.error(("EFM.6.12.02", "GFM.1.06.02"),
|
|
3232
|
-
_("More than one presentation relationship in role %(linkroleDefinition)s has order value %(order)s, from concept %(conceptFrom)s. "
|
|
3233
|
-
"Change all but one so they are distinct."),
|
|
3234
|
-
edgarCode="rq-1202-Presentation-Order-Duplicates",
|
|
3235
|
-
modelObject=(rel, orderRels[order]), conceptFrom=relFrom.qname, order=rel.arcElement.get("order"), linkrole=rel.linkrole,
|
|
3236
|
-
linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole), linkroleName=modelXbrl.roleTypeName(rel.linkrole),
|
|
3237
|
-
conceptTo=relTo.qname, conceptTo2=orderRels[order].toModelObject.qname)
|
|
3238
|
-
else:
|
|
3239
|
-
orderRels[order] = rel
|
|
3240
|
-
if isinstance(relTo, ModelConcept):
|
|
3241
|
-
if relTo.periodType == "duration" and instantPreferredLabelRolePattern.match(preferredLabel or ""):
|
|
3242
|
-
modelXbrl.warning("EFM.6.12.07",
|
|
3243
|
-
_("In \"%(linkrole)s\", element %(conceptTo)s has period type 'duration' but is given a preferred label %(preferredLabel)s "
|
|
3244
|
-
"when shown under parent %(conceptFrom)s. The preferred label will be ignored."),
|
|
3245
|
-
modelObject=(rel, relTo), conceptTo=relTo.qname, conceptFrom=relFrom.qname, order=rel.arcElement.get("order"), linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole),
|
|
3246
|
-
linkroleName=modelXbrl.roleTypeName(rel.linkrole),
|
|
3247
|
-
conceptTo2=orderRels[order].toModelObject.qname,
|
|
3248
|
-
preferredLabel=preferredLabel, preferredLabelValue=preferredLabel.rpartition("/")[2])
|
|
3249
|
-
if (relTo.isExplicitDimension and not any(
|
|
3250
|
-
isinstance(_rel.toModelObject, ModelConcept) and _rel.toModelObject.type is not None and _rel.toModelObject.type.isDomainItemType
|
|
3251
|
-
for _rel in parentChildRels.fromModelObject(relTo))):
|
|
3252
|
-
modelXbrl.warning("EFM.6.12.08",
|
|
3253
|
-
_("In \"%(linkrole)s\" axis %(axis)s has no domain element children, which effectively filters out every fact."),
|
|
3254
|
-
modelObject=(relFrom,relTo), axis=relFrom.qname,
|
|
3255
|
-
linkrole=ELR, linkroleDefinition=modelXbrl.roleTypeDefinition(ELR), linkroleName=modelXbrl.roleTypeName(ELR))
|
|
3256
|
-
if (relFrom.isExplicitDimension and not any(
|
|
3257
|
-
isinstance(_rel.toModelObject, ModelConcept) and _rel.toModelObject.type is not None and _rel.toModelObject.type.isDomainItemType
|
|
3258
|
-
for _rel in siblingRels)):
|
|
3259
|
-
modelXbrl.warning("EFM.6.12.08",
|
|
3260
|
-
_("In \"%(linkrole)s\" axis %(axis)s has no domain element children, which effectively filters out every fact."),
|
|
3261
|
-
modelObject=relFrom, axis=relFrom.qname,
|
|
3262
|
-
linkrole=ELR, linkroleDefinition=modelXbrl.roleTypeDefinition(ELR), linkroleName=modelXbrl.roleTypeName(ELR))
|
|
3263
|
-
targetConceptPreferredLabels.clear()
|
|
3264
|
-
orderRels.clear()
|
|
3265
|
-
localPreferredLabels.clear() # clear for next relationship
|
|
3266
|
-
for conceptPresented in conceptsPresented:
|
|
3267
|
-
if conceptPresented in usedCalcsPresented:
|
|
3268
|
-
usedCalcPairingsOfConcept = usedCalcsPresented[conceptPresented]
|
|
3269
|
-
if len(usedCalcPairingsOfConcept & conceptsPresented) > 0:
|
|
3270
|
-
usedCalcPairingsOfConcept -= conceptsPresented
|
|
3271
|
-
_validateEFMCalcTree = (
|
|
3272
|
-
# If `efmFiling` is undefined (GUI and potentially the Python library) calc tree walk should be performed.
|
|
3273
|
-
not hasattr(modelXbrl.modelManager, 'efmFiling')
|
|
3274
|
-
# `validateEFMCalcTree` can be set to False from the CLI (`--efm-skip-calc-tree`).
|
|
3275
|
-
or getattr(modelXbrl.modelManager.efmFiling.options, 'validateEFMCalcTree', True)
|
|
3276
|
-
)
|
|
3277
|
-
# 6.15.02, 6.15.03 semantics checks for totals and calc arcs (by tree walk)
|
|
3278
|
-
if validateLoggingSemantic and _validateEFMCalcTree:
|
|
3279
|
-
for rootConcept in parentChildRels.rootConcepts:
|
|
3280
|
-
checkCalcsTreeWalk(val, parentChildRels, rootConcept, isStatementSheet, False, conceptsUsed, set())
|
|
3281
|
-
# 6.12.6
|
|
3282
|
-
if len(parentChildRels.rootConcepts) > 1:
|
|
3283
|
-
val.modelXbrl.warning("EFM.6.12.06",
|
|
3284
|
-
_("Presentation relationship set role %(linkrole)s has multiple (%(numberRootConcepts)s) root nodes. "
|
|
3285
|
-
"XBRL allows unordered root nodes, but rendering requires ordering. They will instead be ordered by their labels. "
|
|
3286
|
-
"To avoid undesirable ordering of axes and primary items across multiple root nodes, rearrange the presentation relationships to have only a single root node."),
|
|
3287
|
-
modelObject=(rel,parentChildRels.rootConcepts), linkrole=ELR, linkroleDefinition=val.modelXbrl.roleTypeDefinition(ELR),
|
|
3288
|
-
linkroleName=val.modelXbrl.roleTypeName(ELR),
|
|
3289
|
-
numberRootConcepts=len(parentChildRels.rootConcepts))
|
|
3290
|
-
elif arcrole in XbrlConst.summationItems:
|
|
3291
|
-
# 6.14.3 check for relation concept periods
|
|
3292
|
-
fromRelationships = modelXbrl.relationshipSet(arcrole,ELR).fromModelObjects()
|
|
3293
|
-
# allElrRelSet = modelXbrl.relationshipSet(arcrole)
|
|
3294
|
-
for relFrom, rels in fromRelationships.items():
|
|
3295
|
-
orderRels = {}
|
|
3296
|
-
for rel in rels:
|
|
3297
|
-
relTo = rel.toModelObject
|
|
3298
|
-
# 6.14.03 must have matched period types across relationshp
|
|
3299
|
-
if isinstance(relTo, ModelConcept) and relFrom.periodType != relTo.periodType:
|
|
3300
|
-
val.modelXbrl.error(("EFM.6.14.03", "GFM.1.07.03"),
|
|
3301
|
-
"Element %(conceptFrom)s and element %(conceptTo)s have different period types, but there is a calculation relationship between them in role %(linkroleDefinition)s. "
|
|
3302
|
-
"Please recheck submission calculation links.",
|
|
3303
|
-
edgarCode="fs-1403-Calculation-Relationship-Has-Different-Period-Types",
|
|
3304
|
-
modelObject=rel, linkrole=rel.linkrole, conceptFrom=relFrom.qname, conceptTo=relTo.qname, linkroleDefinition=val.modelXbrl.roleTypeDefinition(ELR))
|
|
3305
|
-
# 6.14.5 concepts used must have pres in same ext link
|
|
3306
|
-
if relFrom in conceptsUsed and relTo in conceptsUsed:
|
|
3307
|
-
fromObjId = relFrom.objectIndex
|
|
3308
|
-
toObjId = relTo.objectIndex
|
|
3309
|
-
if fromObjId < toObjId:
|
|
3310
|
-
usedCalcsPresented[fromObjId].add(toObjId)
|
|
3311
|
-
else:
|
|
3312
|
-
usedCalcsPresented[toObjId].add(fromObjId)
|
|
3313
|
-
|
|
3314
|
-
order = rel.order
|
|
3315
|
-
if order in orderRels and disclosureSystem.GFM:
|
|
3316
|
-
val.modelXbrl.error(("EFM.N/A", "GFM.1.07.06"),
|
|
3317
|
-
_("Duplicate calculations relations from concept %(conceptFrom)s for order %(order)s in base set role %(linkrole)s to concept %(conceptTo)s and to concept %(conceptTo2)s"),
|
|
3318
|
-
modelObject=(rel, orderRels[order]), linkrole=rel.linkrole, conceptFrom=relFrom.qname, order=order,
|
|
3319
|
-
conceptTo=rel.toModelObject.qname, conceptTo2=orderRels[order].toModelObject.qname)
|
|
3320
|
-
else:
|
|
3321
|
-
orderRels[order] = rel
|
|
3322
|
-
directedCycleRels = directedCycle(val, relFrom,relFrom,fromRelationships,{relFrom})
|
|
3323
|
-
if directedCycleRels is not None:
|
|
3324
|
-
val.modelXbrl.error(("EFM.6.14.04", "GFM.1.07.04"),
|
|
3325
|
-
_("Element %(concept)s is summed into itself in calculation group %(linkroleDefinition)s. Please recheck submission."),
|
|
3326
|
-
edgarCode="fs-1404-Circular-Calculation",
|
|
3327
|
-
modelObject=[relFrom] + directedCycleRels, linkrole=ELR, concept=relFrom.qname, linkroleDefinition=val.modelXbrl.roleTypeDefinition(ELR))
|
|
3328
|
-
orderRels.clear()
|
|
3329
|
-
# if relFrom used by fact and multiple calc networks from relFrom, test 6.15.04
|
|
3330
|
-
if rels and relFrom in conceptsUsed:
|
|
3331
|
-
relFromAndTos = (relFrom.objectIndex,) + tuple(sorted((rel.toModelObject.objectIndex
|
|
3332
|
-
for rel in rels if isinstance(rel.toModelObject, ModelConcept))))
|
|
3333
|
-
if relFromAndTos in usedCalcFromTosELR:
|
|
3334
|
-
otherRels = usedCalcFromTosELR[relFromAndTos]
|
|
3335
|
-
otherELR = otherRels[0].linkrole
|
|
3336
|
-
val.modelXbrl.log("WARNING-SEMANTIC", ("EFM.6.15.04", "GFM.2.06.04"),
|
|
3337
|
-
_("Calculation relationships should have a same set of targets in %(linkrole)s and %(linkrole2)s starting from %(concept)s"),
|
|
3338
|
-
modelObject=[relFrom] + rels + otherRels, linkrole=ELR, linkrole2=otherELR, concept=relFrom.qname)
|
|
3339
|
-
else:
|
|
3340
|
-
usedCalcFromTosELR[relFromAndTos] = rels
|
|
3341
|
-
|
|
3342
|
-
elif arcrole == XbrlConst.all or arcrole == XbrlConst.notAll:
|
|
3343
|
-
drsELRs.add(ELR)
|
|
3344
|
-
|
|
3345
|
-
elif arcrole == XbrlConst.dimensionDomain or arcrole == XbrlConst.dimensionDefault:
|
|
3346
|
-
# 6.16.3 check domain targets in extension linkbases are domain items
|
|
3347
|
-
fromRelationships = modelXbrl.relationshipSet(arcrole,ELR).fromModelObjects()
|
|
3348
|
-
for relFrom, rels in fromRelationships.items():
|
|
3349
|
-
for rel in rels:
|
|
3350
|
-
relTo = rel.toModelObject
|
|
3351
|
-
|
|
3352
|
-
if not (isinstance(relTo, ModelConcept) and relTo.type is not None and relTo.type.isDomainItemType) and not isStandardUri(val, rel.modelDocument.uri):
|
|
3353
|
-
val.modelXbrl.error(("EFM.6.16.03", "GFM.1.08.03"),
|
|
3354
|
-
_("There is a dimension-domain relationship from %(conceptFrom)s but its target element %(conceptTo)s is not a domain. "
|
|
3355
|
-
"Please change the relationship or change the type of the target element."),
|
|
3356
|
-
edgarCode="du-1603-Dimension-Domain-Target-Mismatch",
|
|
3357
|
-
modelObject=(rel, relFrom, relTo), conceptFrom=relFrom.qname, conceptTo=(relTo.qname if relTo is not None else None), linkrole=rel.linkrole)
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
# definition tests (GFM only, for now)
|
|
3361
|
-
if XbrlConst.isDefinitionOrXdtArcrole(arcrole) and disclosureSystem.GFM:
|
|
3362
|
-
fromRelationships = modelXbrl.relationshipSet(arcrole,ELR).fromModelObjects()
|
|
3363
|
-
for relFrom, rels in fromRelationships.items():
|
|
3364
|
-
orderRels = {}
|
|
3365
|
-
for rel in rels:
|
|
3366
|
-
relTo = rel.toModelObject
|
|
3367
|
-
order = rel.order
|
|
3368
|
-
if order in orderRels and disclosureSystem.GFM:
|
|
3369
|
-
val.modelXbrl.error("GFM.1.08.10",
|
|
3370
|
-
_("Duplicate definitions relations from concept %(conceptFrom)s for order %(order)s in base set role %(linkrole)s "
|
|
3371
|
-
"to concept %(conceptTo)s and to concept %(conceptTo2)s"),
|
|
3372
|
-
modelObject=(rel, relFrom, relTo), conceptFrom=relFrom.qname, order=order, linkrole=rel.linkrole,
|
|
3373
|
-
conceptTo=rel.toModelObject.qname, conceptTo2=orderRels[order].toModelObject.qname)
|
|
3374
|
-
else:
|
|
3375
|
-
orderRels[order] = rel
|
|
3376
|
-
if (arcrole not in (XbrlConst.dimensionDomain, XbrlConst.domainMember) and
|
|
3377
|
-
rel.get("{http://xbrl.org/2005/xbrldt}usable") == "false"):
|
|
3378
|
-
val.modelXrl.error("GFM.1.08.11",
|
|
3379
|
-
_("Disallowed xbrldt:usable='false' attribute on %(arc)s relationship from concept %(conceptFrom)s in "
|
|
3380
|
-
"base set role %(linkrole)s to concept %(conceptTo)s"),
|
|
3381
|
-
modelObject=(rel, relFrom, relTo), arc=rel.qname, conceptFrom=relFrom.qname, linkrole=rel.linkrole, conceptTo=rel.toModelObject.qname)
|
|
3382
|
-
|
|
3383
|
-
# 6.9.10 checks on custom arcs
|
|
3384
|
-
if isEFM:
|
|
3385
|
-
# find OEF, CEF, VIP or ECD
|
|
3386
|
-
tgtMemRoles = defaultdict(set)
|
|
3387
|
-
tgtMemRels = defaultdict(list)
|
|
3388
|
-
for d in modelXbrl.urlDocs.values():
|
|
3389
|
-
ns = d.targetNamespace
|
|
3390
|
-
abbrNs = abbreviatedNamespace(d.targetNamespace, NOYEAR)
|
|
3391
|
-
lbVal = linkbaseValidations.get(abbrNs)
|
|
3392
|
-
if d.type == ModelDocument.Type.SCHEMA and lbVal:
|
|
3393
|
-
preSrcConcepts = set(concept
|
|
3394
|
-
for name in lbVal.preSources
|
|
3395
|
-
for concept in modelXbrl.nameConcepts.get(name, ())
|
|
3396
|
-
if isStandardUri(val, concept.modelDocument.uri)) # want concept from std namespace not extension
|
|
3397
|
-
if lbVal.efmPre and ('elrPreDocTypes' not in lbVal or deiDocumentType in lbVal.elrPreDocTypes):
|
|
3398
|
-
for rel in modelXbrl.relationshipSet(XbrlConst.parentChild).modelRelationships:
|
|
3399
|
-
if not isStandardUri(val, rel.modelDocument.uri) and rel.modelDocument.targetNamespace not in val.otherStandardTaxonomies:
|
|
3400
|
-
relFrom = rel.fromModelObject
|
|
3401
|
-
relTo = rel.toModelObject
|
|
3402
|
-
if relFrom is not None and relTo is not None:
|
|
3403
|
-
relset = modelXbrl.relationshipSet(XbrlConst.parentChild, rel.linkrole)
|
|
3404
|
-
roleMatch = lbVal.elrPre.match(rel.linkrole)
|
|
3405
|
-
if ((roleMatch and relTo.qname.namespaceURI != ns and (
|
|
3406
|
-
not relTo.type.isDomainItemType or (lbVal.preSources and not
|
|
3407
|
-
any(relset.isRelated(c, "descendant-or-self", relFrom) for c in preSrcConcepts))))
|
|
3408
|
-
or
|
|
3409
|
-
(not roleMatch and not lbVal.preCustELRs and (relFrom.qname.namespaceURI == ns or relTo.qname.namespaceURI == ns))):
|
|
3410
|
-
modelXbrl.error(f"EFM.{lbVal.efmPre}.relationshipNotPermitted",
|
|
3411
|
-
_("The %(arcrole)s relationship from %(conceptFrom)s to %(conceptTo)s, link role %(linkroleDefinition)s, is not permitted."),
|
|
3412
|
-
edgarCode=f"du-{lbVal.efmPre[2:4]}{lbVal.efmPre[5:]}-Relationship-Not-Permitted",
|
|
3413
|
-
modelObject=(rel,relFrom,relTo), arc=rel.qname, arcrole=rel.arcrole,
|
|
3414
|
-
linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole),
|
|
3415
|
-
conceptFrom=relFrom.qname, conceptTo=relTo.qname)
|
|
3416
|
-
if lbVal.efmCal and ('elrCalDocTypes' not in lbVal or deiDocumentType in lbVal.elrCalDocTypes):
|
|
3417
|
-
for rel in modelXbrl.relationshipSet(XbrlConst.summationItems).modelRelationships:
|
|
3418
|
-
if not isStandardUri(val, rel.modelDocument.uri) and rel.modelDocument.targetNamespace not in val.otherStandardTaxonomies:
|
|
3419
|
-
relFrom = rel.fromModelObject
|
|
3420
|
-
relTo = rel.toModelObject
|
|
3421
|
-
if relFrom is not None and relTo is not None:
|
|
3422
|
-
if relFrom.qname.namespaceURI == ns or relTo.qname.namespaceURI == ns:
|
|
3423
|
-
modelXbrl.error(f"EFM.{lbVal.efmCal}.relationshipNotPermitted",
|
|
3424
|
-
_("The %(arcrole)s relationship from %(conceptFrom)s to %(conceptTo)s, link role %(linkroleDefinition)s, is not permitted."),
|
|
3425
|
-
edgarCode=f"du-{lbVal.efmCal[2:4]}{lbVal.efmCal[5:]}-Relationship-Not-Permitted",
|
|
3426
|
-
modelObject=(rel,relFrom,relTo), arc=rel.qname, arcrole=rel.arcrole,
|
|
3427
|
-
linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole),
|
|
3428
|
-
conceptFrom=relFrom.qname, conceptTo=relTo.qname)
|
|
3429
|
-
if lbVal.efmDef and ('elrDefDocTypes' not in lbVal or deiDocumentType in lbVal.elrDefDocTypes):
|
|
3430
|
-
tgtMemRoles.clear()
|
|
3431
|
-
tgtMemRels.clear()
|
|
3432
|
-
for rel in modelXbrl.relationshipSet("XBRL-dimensions").modelRelationships:
|
|
3433
|
-
if not isStandardUri(val, rel.modelDocument.uri) and rel.modelDocument.targetNamespace not in val.otherStandardTaxonomies:
|
|
3434
|
-
relFrom = rel.fromModelObject
|
|
3435
|
-
relFromQNstr = str(relFrom.qname)
|
|
3436
|
-
relTo = rel.toModelObject
|
|
3437
|
-
if relFrom is not None and relTo is not None:
|
|
3438
|
-
if ((relFrom.qname.namespaceURI == ns or relTo.qname.namespaceURI == ns)
|
|
3439
|
-
and not (
|
|
3440
|
-
rel.arcrole == XbrlConst.domainMember and (
|
|
3441
|
-
(relFrom.qname.namespaceURI == ns and relTo.qname.namespaceURI == ns and lbVal.elrDefInNs.match(rel.linkrole))
|
|
3442
|
-
or
|
|
3443
|
-
(relFrom.qname.namespaceURI == ns and lbVal.elrDefExNs.match(rel.linkrole))
|
|
3444
|
-
)
|
|
3445
|
-
)
|
|
3446
|
-
):
|
|
3447
|
-
modelXbrl.error(f"EFM.{lbVal.efmDef}.relationshipNotPermitted",
|
|
3448
|
-
_("The %(arcrole)s relationship from %(conceptFrom)s to %(conceptTo)s, link role %(linkroleDefinition)s, is not permitted."),
|
|
3449
|
-
edgarCode=f"du-{lbVal.efmDef[2:4]}{lbVal.efmDef[5:]}-Relationship-Not-Permitted",
|
|
3450
|
-
modelObject=(rel,relFrom,relTo), arc=rel.qname, arcrole=rel.arcrole,
|
|
3451
|
-
linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole),
|
|
3452
|
-
conceptFrom=relFrom.qname, conceptTo=relTo.qname)
|
|
3453
|
-
elif lbVal.elrDefRoleSrc and rel.linkrole in lbVal.elrDefRoleSrc and lbVal.elrDefRoleSrc[rel.linkrole] != relFromQNstr:
|
|
3454
|
-
modelXbrl.error(f"EFM.{lbVal.efmDef}.roleSourceNotPermitted",
|
|
3455
|
-
_("The %(arcrole)s relationship source, %(conceptFrom)s, to %(conceptTo)s, link role %(linkroleDefinition)s, is not permitted."),
|
|
3456
|
-
edgarCode=f"du-{lbVal.efmDef[2:4]}{lbVal.efmDef[5:]}-Role-Source-Not-Permitted",
|
|
3457
|
-
modelObject=(rel,relFrom,relTo), arc=rel.qname, arcrole=rel.arcrole,
|
|
3458
|
-
linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole),
|
|
3459
|
-
conceptFrom=relFrom.qname, conceptTo=relTo.qname)
|
|
3460
|
-
if lbVal.elrDefNoTgtRole and rel.targetRole:
|
|
3461
|
-
modelXbrl.error(f"EFM.{lbVal.efmDef}.targetRoleNotPermitted",
|
|
3462
|
-
_("The %(arcrole)s relationship targetRole from %(conceptFrom)s to %(conceptTo)s, link role %(linkroleDefinition)s, is not permitted."),
|
|
3463
|
-
edgarCode=f"du-{lbVal.efmDef[2:4]}{lbVal.efmDef[5:]}-TargetRole-Not-Permitted",
|
|
3464
|
-
modelObject=(rel,relFrom,relTo), arc=rel.qname, arcrole=rel.arcrole,
|
|
3465
|
-
linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole),
|
|
3466
|
-
conceptFrom=relFrom.qname, conceptTo=relTo.qname)
|
|
3467
|
-
if 'efmDefTgtMemsUnique' in lbVal and rel.arcrole == XbrlConst.domainMember and lbVal.elrDefRgtMemsRole.match(rel.linkrole):
|
|
3468
|
-
tgtMemRoles[relTo].add(rel.linkrole)
|
|
3469
|
-
tgtMemRels[relTo].append(rel)
|
|
3470
|
-
for tgtMem, roles in tgtMemRoles.items():
|
|
3471
|
-
if len(roles) > 1:
|
|
3472
|
-
modelXbrl.error(f"EFM.{lbVal.efmDefTgtMemsUnique}",
|
|
3473
|
-
_("Member concept %(member)s appears in more than one %(taxonomy)s role: %(roles)s."),
|
|
3474
|
-
edgarCode=f"{abbrNs}-{lbVal.efmDefTgtMemsUnique[2:].replace('.','')}-Member-Multiple-{abbrNs.upper()}-Roles",
|
|
3475
|
-
modelObject=tgtMemRels[tgtMem], member=tgtMem.qname, roles=", ".join(sorted(roles)), taxonomy=abbrNs.upper())
|
|
3476
|
-
del tgtMemRoles, tgtMemRels # dereference
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
del localPreferredLabels # dereference
|
|
3480
|
-
del usedCalcFromTosELR
|
|
3481
|
-
|
|
3482
|
-
val.modelXbrl.profileActivity("... filer relationships checks", minTimeToShow=1.0)
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
# checks on dimensions
|
|
3486
|
-
checkFilingDimensions(val, drsELRs)
|
|
3487
|
-
val.modelXbrl.profileActivity("... filer dimensions checks", minTimeToShow=1.0)
|
|
3488
|
-
|
|
3489
|
-
for concept, hasPresentationRelationship in conceptsUsed.items():
|
|
3490
|
-
if not hasPresentationRelationship:
|
|
3491
|
-
val.modelXbrl.error(("EFM.6.12.03", "GFM.1.6.3"),
|
|
3492
|
-
_("Element %(concept)s is used in a fact or context in the instance, but is not in any presentation relationships. "
|
|
3493
|
-
"Add the element to at least one presentation group."),
|
|
3494
|
-
edgarCode="cp-1203-Element-Used-Not-Presented",
|
|
3495
|
-
modelObject=[concept] + list(modelXbrl.factsByQname[concept.qname]), concept=concept.qname)
|
|
3496
|
-
|
|
3497
|
-
for fromIndx, toIndxs in usedCalcsPresented.items():
|
|
3498
|
-
for toIndx in toIndxs:
|
|
3499
|
-
fromModelObject = val.modelXbrl.modelObject(fromIndx)
|
|
3500
|
-
toModelObject = val.modelXbrl.modelObject(toIndx)
|
|
3501
|
-
calcRels = modelXbrl.relationshipSet(XbrlConst.summationItems) \
|
|
3502
|
-
.fromToModelObjects(fromModelObject, toModelObject, checkBothDirections=True)
|
|
3503
|
-
fromFacts = val.modelXbrl.factsByQname[fromModelObject.qname]
|
|
3504
|
-
toFacts = val.modelXbrl.factsByQname[toModelObject.qname]
|
|
3505
|
-
fromFactContexts = set(f.context.contextNonDimAwareHash for f in fromFacts if f.context is not None)
|
|
3506
|
-
contextId = backupId = None # for EFM message
|
|
3507
|
-
for f in toFacts:
|
|
3508
|
-
if f.context is not None:
|
|
3509
|
-
if f.context.contextNonDimAwareHash in fromFactContexts:
|
|
3510
|
-
contextId = f.context.id
|
|
3511
|
-
break
|
|
3512
|
-
backupId = f.context.id
|
|
3513
|
-
if contextId is None:
|
|
3514
|
-
contextId = backupId
|
|
3515
|
-
val.modelXbrl.error(("EFM.6.14.05", "GFM.1.7.5"),
|
|
3516
|
-
_("Context %(contextId)s has facts of elements %(conceptFrom)s and %(conceptTo)s, with a calculation relationship in %(linkroleDefinition)s, "
|
|
3517
|
-
"but these elements are not in any common corresponding presentation relationship group."),
|
|
3518
|
-
edgarCode="du-1405-Facts-In-Calculations-Presentation-Missing",
|
|
3519
|
-
modelObject=calcRels + [fromModelObject, toModelObject],
|
|
3520
|
-
linkroleDefinition=val.modelXbrl.roleTypeDefinition(calcRels[0].linkrole if calcRels else None),
|
|
3521
|
-
conceptFrom=val.modelXbrl.modelObject(fromIndx).qname, conceptTo=val.modelXbrl.modelObject(toIndx).qname, contextId=contextId)
|
|
3522
|
-
|
|
3523
|
-
if disclosureSystem.defaultXmlLang:
|
|
3524
|
-
for concept, preferredLabelRels in conceptRelsUsedWithPreferredLabels.items():
|
|
3525
|
-
for preferredLabelRel in preferredLabelRels:
|
|
3526
|
-
preferredLabel = preferredLabelRel.preferredLabel
|
|
3527
|
-
hasDefaultLangPreferredLabel = False
|
|
3528
|
-
for modelLabelRel in labelsRelationshipSet.fromModelObject(concept):
|
|
3529
|
-
modelLabel = modelLabelRel.toModelObject
|
|
3530
|
-
if modelLabel.xmlLang.startswith(disclosureSystem.defaultXmlLang) and \
|
|
3531
|
-
modelLabel.role == preferredLabel:
|
|
3532
|
-
hasDefaultLangPreferredLabel = True
|
|
3533
|
-
break
|
|
3534
|
-
if not hasDefaultLangPreferredLabel:
|
|
3535
|
-
val.modelXbrl.error("GFM.1.06.04", # 6.12.04 now reserved: ("EFM.6.12.04", "GFM.1.06.04"),
|
|
3536
|
-
_("Concept %(concept)s missing %(lang)s preferred labels for role %(preferredLabel)s"),
|
|
3537
|
-
modelObject=(preferredLabelRel, concept), concept=concept.qname, fromConcept=preferredLabelRel.fromModelObject.qname,
|
|
3538
|
-
lang=disclosureSystem.defaultLanguage, preferredLabel=preferredLabel)
|
|
3539
|
-
del conceptRelsUsedWithPreferredLabels
|
|
3540
|
-
|
|
3541
|
-
# 6 16 4, 1.16.5 Base sets of Domain Relationship Sets testing
|
|
3542
|
-
val.modelXbrl.profileActivity("... filer preferred label checks", minTimeToShow=1.0)
|
|
3543
|
-
|
|
3544
|
-
# DQC.US rules
|
|
3545
|
-
for dqcRuleName, dqcRule in dqcRules.items(): # note this is an OrderedDict to preserve rule execution order
|
|
3546
|
-
if dqcRuleName == "copyright": # first in JSON OrderedDict, initialize common variables for rule
|
|
3547
|
-
if ugtRels:
|
|
3548
|
-
ugtAxisDefaults = ugtRels["axis-defaults"]
|
|
3549
|
-
hasDocPerEndDateFact = documentPeriodEndDateFact is not None and documentPeriodEndDateFact.xValid >= VALID and documentPeriodEndDateFact.xValue and documentPeriodEndDateFact.context.endDatetime
|
|
3550
|
-
if hasDocPerEndDateFact and documentPeriodEndDate:
|
|
3551
|
-
maxEndDate = max(documentPeriodEndDate, documentPeriodEndDateFact.context.endDatetime)
|
|
3552
|
-
else:
|
|
3553
|
-
maxEndDate = documentPeriodEndDate # note that this may be None if there is no documentPeriodEndDate
|
|
3554
|
-
continue
|
|
3555
|
-
elif not dqcRuleFilter.match(dqcRuleName):
|
|
3556
|
-
continue
|
|
3557
|
-
elif not dqcRuleName.startswith("DQC.US."):
|
|
3558
|
-
continue # skip description and any other non-rule entries
|
|
3559
|
-
msg = dqcRule.get("message")
|
|
3560
|
-
edgarCode = "dqc-{}-{}".format(dqcRuleName[-4:], "-".join(dqcRule["name"].title().split()))
|
|
3561
|
-
if dqcRuleName == "DQC.US.0001" and ugtRels:
|
|
3562
|
-
ugtAxisMembers = ugtRels["axes"]
|
|
3563
|
-
warnedFactsByQn = defaultdict(list)
|
|
3564
|
-
for id, rule in dqcRule["rules"].items():
|
|
3565
|
-
for axisConcept in modelXbrl.nameConcepts.get(rule["axis"],()):
|
|
3566
|
-
membersOfExtensionAxis = axisMemQnames(modelXbrl, axisConcept.qname, rule["extensions-allowed"] == "Yes") # set of QNames
|
|
3567
|
-
allowableMembers = ugtAxisMembers[axisConcept.name] if rule["axis-descendants"] == "Yes" else set()
|
|
3568
|
-
for otherAxis in rule.get("additional-axes",EMPTY_LIST):
|
|
3569
|
-
allowableMembers |= ugtAxisMembers[otherAxis]
|
|
3570
|
-
for otherMemName in rule.get("additional-members",EMPTY_LIST) + rule.get("extension-members",EMPTY_LIST):
|
|
3571
|
-
for otherMemConcept in modelXbrl.nameConcepts.get(otherMemName,()):
|
|
3572
|
-
allowableMembers.add(otherMemConcept.qname)
|
|
3573
|
-
for childExtensionMember in rule.get("child-extension-members",EMPTY_LIST):
|
|
3574
|
-
allowableMembers |= memChildQnames(modelXbrl, childExtensionMember)
|
|
3575
|
-
unallowedMembers = membersOfExtensionAxis - allowableMembers
|
|
3576
|
-
if "unallowed-axes" in rule:
|
|
3577
|
-
unallowedMembers &= set.union(*(ugtAxisMembers[a] for a in rule.get("unallowed-axes")))
|
|
3578
|
-
unallowedMembersUsedByFacts = set()
|
|
3579
|
-
if unallowedMembers:
|
|
3580
|
-
for f in modelXbrl.factsByDimMemQname(axisConcept.qname, None): # None also includes default members
|
|
3581
|
-
if f.context is not None:
|
|
3582
|
-
dimValueQname = f.context.dimMemberQname(axisConcept.qname) # include default members
|
|
3583
|
-
if dimValueQname in unallowedMembers:
|
|
3584
|
-
unallowedMembersUsedByFacts.add(dimValueQname)
|
|
3585
|
-
if dimValueQname.namespaceURI not in disclosureSystem.standardTaxonomiesDict: # is extension member concept
|
|
3586
|
-
issue = {"No": "Extension members should not be used with this axis. ",
|
|
3587
|
-
"Limited": "This extension member should not be used with this axis. ",
|
|
3588
|
-
"Yes": "Extension member is not allowed by rule. "
|
|
3589
|
-
}[rule["extensions-allowed"]]
|
|
3590
|
-
elif rule["axis-descendants"] == "None":
|
|
3591
|
-
issue = "Only extension members can be used with this axis. "
|
|
3592
|
-
else:
|
|
3593
|
-
issue = "Base taxonomy member is not allowed by rule. "
|
|
3594
|
-
if not any(f.isDuplicateOf(warnedFact) for warnedFact in warnedFactsByQn[f.qname]):
|
|
3595
|
-
warnedFactsByQn[f.qname].append(f)
|
|
3596
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3597
|
-
modelObject=f, name=f.qname, value=strTruncate(f.value,128), axis=axisConcept.qname, member=dimValueQname, issue=issue,
|
|
3598
|
-
contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
3599
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3600
|
-
unusedUnallowed = unallowedMembers - unallowedMembersUsedByFacts
|
|
3601
|
-
for unusedMember in unusedUnallowed: # report one member per message for result comparability to XBRL-US implementation
|
|
3602
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(dqcRule["message-unreported"])),
|
|
3603
|
-
modelObject=modelXbrl, axis=axisConcept.qname, member=unusedMember,
|
|
3604
|
-
edgarCode=edgarCode+"-Unreported", ruleElementId=id)
|
|
3605
|
-
if rule.get("axis-default-must-match-UGT") == "Yes" and rule["axis"] in ugtAxisDefaults:
|
|
3606
|
-
ugtDefaultMem = ugtAxisDefaults[rule["axis"]]
|
|
3607
|
-
for dimDefRel in modelXbrl.relationshipSet(XbrlConst.dimensionDefault).fromModelObject(axisConcept):
|
|
3608
|
-
if dimDefRel.toModelObject is not None:
|
|
3609
|
-
extDefaultQname = dimDefRel.toModelObject.qname
|
|
3610
|
-
if extDefaultQname.localName != ugtDefaultMem:
|
|
3611
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(dqcRule["message-disallowed-default"])),
|
|
3612
|
-
modelObject=modelXbrl, axis=axisConcept.qname, default=extDefaultQname, allowedDefault=ugtDefaultMem,
|
|
3613
|
-
edgarCode=edgarCode+"-Disallowed-Default", ruleElementId=id)
|
|
3614
|
-
del warnedFactsByQn # dereference objects
|
|
3615
|
-
|
|
3616
|
-
elif dqcRuleName == "DQC.US.0004":
|
|
3617
|
-
for id, rule in dqcRule["rules"].items():
|
|
3618
|
-
# first check if there's a calc-sum and calc-items
|
|
3619
|
-
sumLn = rule.get("calc-sum")
|
|
3620
|
-
blkAxis = rule.get("blocking-axes",())
|
|
3621
|
-
alts = rule.get("alternatives",EMPTY_DICT)
|
|
3622
|
-
tolerance = rule["tolerance"]
|
|
3623
|
-
linkroleURIs = (None,) # for IDs without calc network evaluation
|
|
3624
|
-
if sumLn in modelXbrl.nameConcepts and "calc-items" in rule: # (dqc_us_rules/pull/544)
|
|
3625
|
-
sumConcept = modelXbrl.nameConcepts[sumLn][0]
|
|
3626
|
-
linkroleURIs = OrderedSet(modelLink.role
|
|
3627
|
-
for arcRole in XbrlConst.summationItems
|
|
3628
|
-
for modelLink in val.modelXbrl.baseSets[(arcRole,None,None,None)]
|
|
3629
|
-
if modelXbrl.relationshipSet(XbrlConst.summationItems, modelLink.role , None, None).fromModelObject(sumConcept))
|
|
3630
|
-
|
|
3631
|
-
for linkroleUri in linkroleURIs: # evaluate by network where applicable to ID
|
|
3632
|
-
itemWeights = {}
|
|
3633
|
-
summingNetworkChildren = False
|
|
3634
|
-
if linkroleUri: # has calc network evaluation
|
|
3635
|
-
itemWeights = dict((rel.toModelObject.name, rel.weightDecimal)
|
|
3636
|
-
for rel in modelXbrl.relationshipSet(XbrlConst.summationItems, linkroleUri, None, None).fromModelObject(sumConcept)
|
|
3637
|
-
if rel.toModelObject is not None)
|
|
3638
|
-
if set(rule.get("calc-items")) <= itemWeights.keys():
|
|
3639
|
-
itemLns = list(itemWeights.keys())
|
|
3640
|
-
sumLn = rule.get("calc-sum") # may be reset on previous linkroleUri in loop
|
|
3641
|
-
summingNetworkChildren = True
|
|
3642
|
-
else:
|
|
3643
|
-
sumLn = None
|
|
3644
|
-
if not sumLn:
|
|
3645
|
-
sumLn = rule["sum"]
|
|
3646
|
-
itemLns = rule["items"]
|
|
3647
|
-
bindings = factBindings(val.modelXbrl, flattenToSet( (sumLn, itemLns, alts.values() )), nils=False)
|
|
3648
|
-
for b in bindings.values():
|
|
3649
|
-
_itemLns = itemLns.copy() # need fresh array to use for substituting
|
|
3650
|
-
_sumLn = sumLn
|
|
3651
|
-
for iLn in itemLns: # check if substitution is necessary
|
|
3652
|
-
if iLn not in b and iLn in alts:
|
|
3653
|
-
for aLns in alts[iLn]:
|
|
3654
|
-
if all(aLn in b for aLn in aLns):
|
|
3655
|
-
p = _itemLns.index(iLn) # replace iLn with alts that all are in binding
|
|
3656
|
-
_itemLns[p:p+1] = aLns
|
|
3657
|
-
break
|
|
3658
|
-
if _sumLn not in b and _sumLn in alts:
|
|
3659
|
-
for aLns in alts[sumLn]:
|
|
3660
|
-
if aLns and aLns[0] in b:
|
|
3661
|
-
_sumLn = aLns[0]
|
|
3662
|
-
break
|
|
3663
|
-
if summingNetworkChildren: # use actually-present contributing items in binding
|
|
3664
|
-
_itemLns = b.keys() - {_sumLn}
|
|
3665
|
-
if _sumLn in b and _itemLns and all(ln in b for ln in _itemLns) and not (
|
|
3666
|
-
any(ax in f.context.qnameDims for ax in blkAxis for f in b.values())):
|
|
3667
|
-
dec = leastDecimals(b, flattenToSet( (_sumLn, _itemLns) ))
|
|
3668
|
-
sumFact = b[_sumLn]
|
|
3669
|
-
itemFacts = [b[ln] for ln in _itemLns]
|
|
3670
|
-
sfNil = sumFact.isNil
|
|
3671
|
-
allIfNil = itemFacts and all(f.isNil for f in itemFacts)
|
|
3672
|
-
if sfNil:
|
|
3673
|
-
sumValue = "(nil)"
|
|
3674
|
-
else:
|
|
3675
|
-
sumValue = roundValue(sumFact.xValue, decimals=dec)
|
|
3676
|
-
if not allIfNil:
|
|
3677
|
-
itemValues = tuple(roundValue(f.xValue * itemWeights.get(f.qname.localName, ONE), decimals=dec)
|
|
3678
|
-
for f in itemFacts if not f.isNil)
|
|
3679
|
-
try:
|
|
3680
|
-
if ((not (sfNil & allIfNil)) and (
|
|
3681
|
-
(sfNil ^ allIfNil) or
|
|
3682
|
-
abs(sumValue - sum(itemValues)) > pow(10, -dec) * tolerance)):
|
|
3683
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3684
|
-
modelObject=b.values(), sumName=_sumLn, sumValue=str(sumValue),
|
|
3685
|
-
itemNames=", ".join(_itemLns), itemValues=" + ".join(str(v) for v in itemValues),
|
|
3686
|
-
contextID=sumFact.contextID, unitID=sumFact.unitID or "(none)",
|
|
3687
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3688
|
-
except:
|
|
3689
|
-
print("exception")
|
|
3690
|
-
elif dqcRuleName == "DQC.US.0005" and deiDocumentType not in dqcRule["excluded-document-types"] and maxEndDate:
|
|
3691
|
-
for id, rule in dqcRule["rules"].items():
|
|
3692
|
-
msg = rule.get("message") # each rule has a message
|
|
3693
|
-
if "name" in rule:
|
|
3694
|
-
facts = modelXbrl.factsByLocalName.get(rule["name"],())
|
|
3695
|
-
maxEndDateComparedTo = maxEndDate.__gt__ # f.endDate < maxEndDate
|
|
3696
|
-
elif "axis" in rule and rule["axis"] in modelXbrl.nameConcepts:
|
|
3697
|
-
axisQn = modelXbrl.nameConcepts[rule["axis"]][0].qname
|
|
3698
|
-
if rule.get("member") in modelXbrl.nameConcepts:
|
|
3699
|
-
memQn = modelXbrl.nameConcepts[rule.get("member")][0].qname
|
|
3700
|
-
else:
|
|
3701
|
-
memQn = NONDEFAULT
|
|
3702
|
-
facts = modelXbrl.factsByDimMemQname(axisQn, memQn)
|
|
3703
|
-
maxEndDateComparedTo = maxEndDate.__ge__ # f.endDate <= maxEndDate
|
|
3704
|
-
else:
|
|
3705
|
-
continue
|
|
3706
|
-
for f in facts:
|
|
3707
|
-
if f.context is not None and maxEndDateComparedTo(f.context.endDatetime):
|
|
3708
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3709
|
-
modelObject=f, name=f.qname.localName, value=f.xValue,
|
|
3710
|
-
date=XmlUtil.dateunionValue(f.context.endDatetime, subtractOneDay=True),
|
|
3711
|
-
endDate=XmlUtil.dateunionValue(maxEndDate, subtractOneDay=True),
|
|
3712
|
-
axis=rule.get("axis"), member=rule.get("member"),
|
|
3713
|
-
contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
3714
|
-
edgarCode=edgarCode + '-' + id, ruleElementId=id)
|
|
3715
|
-
elif (dqcRuleName == "DQC.US.0006"
|
|
3716
|
-
and deiDocumentType not in dqcRule["excluded-document-types"]
|
|
3717
|
-
and deiDocumentType and "T" not in deiDocumentType):
|
|
3718
|
-
for id, rule in dqcRule["rules"].items():
|
|
3719
|
-
focusRange = rule["focus-range"].get(deiItems.get("DocumentFiscalPeriodFocus"))
|
|
3720
|
-
if focusRange and not any(modelXbrl.factsByLocalName.get(n,()) for n in rule["blocking-names"]):
|
|
3721
|
-
def r6facts():
|
|
3722
|
-
for n in rule["names"]:
|
|
3723
|
-
for f in modelXbrl.factsByLocalName.get(n,()):
|
|
3724
|
-
if f.context is not None:
|
|
3725
|
-
yield f
|
|
3726
|
-
for n in ("{http://www.xbrl.org/dtr/type/non-numeric}textBlockItemType",
|
|
3727
|
-
"{http://www.xbrl.org/dtr/type/2020-01-21}textBlockItemType"):
|
|
3728
|
-
for f in modelXbrl.factsByDatatype(True, qname(n)):
|
|
3729
|
-
if f.context is not None:
|
|
3730
|
-
yield f
|
|
3731
|
-
for f in r6facts():
|
|
3732
|
-
durationDays = (f.context.endDatetime - f.context.startDatetime).days
|
|
3733
|
-
if not (focusRange[0] <= durationDays <= focusRange[1]):
|
|
3734
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3735
|
-
modelObject=f, name=f.qname.localName, durationDays=durationDays, documentFiscalPeriodFocus=deiItems.get("DocumentFiscalPeriodFocus"),
|
|
3736
|
-
startDate=XmlUtil.dateunionValue(f.context.startDatetime), endDate=XmlUtil.dateunionValue(f.context.endDatetime, subtractOneDay=True),
|
|
3737
|
-
contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
3738
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3739
|
-
elif dqcRuleName == "DQC.US.0008" and ugtRels:
|
|
3740
|
-
for id, rule in dqcRule["rules"].items():
|
|
3741
|
-
ugtCalcs = ugtRels["calcs"]
|
|
3742
|
-
for rel in val.summationItemRelsSetAllELRs.modelRelationships:
|
|
3743
|
-
relFrom = rel.fromModelObject
|
|
3744
|
-
relTo = rel.toModelObject
|
|
3745
|
-
if (relFrom is not None and relTo is not None and
|
|
3746
|
-
relFrom.qname in ugtCalcs.get(rel.weight,EMPTY_DICT).get(relTo.qname,EMPTY_DICT)):
|
|
3747
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3748
|
-
modelObject=rel, linkrole=rel.linkrole, linkroleDefinition=modelXbrl.roleTypeDefinition(rel.linkrole),
|
|
3749
|
-
conceptFrom=relFrom.qname, conceptTo=relTo.qname,
|
|
3750
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3751
|
-
|
|
3752
|
-
elif dqcRuleName == "DQC.US.0009":
|
|
3753
|
-
for id, rule in dqcRule["rules"].items():
|
|
3754
|
-
lesserLn = rule["lesser"]
|
|
3755
|
-
greaterLn = rule["greater"]
|
|
3756
|
-
msg = rule.get("use-message","message") # general message defaults to "message"
|
|
3757
|
-
ruleMsg = dqcRule[msg]
|
|
3758
|
-
ruleEdgarCode = edgarCode + msg.title()[7:]
|
|
3759
|
-
bindings = factBindings(val.modelXbrl, (lesserLn, greaterLn) )
|
|
3760
|
-
for b in bindings.values():
|
|
3761
|
-
if lesserLn in b and greaterLn in b:
|
|
3762
|
-
dec = leastDecimals(b, (lesserLn, greaterLn) )
|
|
3763
|
-
lesserFact = b[lesserLn]
|
|
3764
|
-
lesserValue = roundValue(lesserFact.xValue, decimals=dec)
|
|
3765
|
-
greaterValue = roundValue(b[greaterLn].xValue, decimals=dec)
|
|
3766
|
-
if lesserValue > greaterValue:
|
|
3767
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(ruleMsg)),
|
|
3768
|
-
modelObject=b.values(), lesserName=lesserLn, lesserValue=str(lesserValue), greaterName=greaterLn, greaterValue=str(greaterValue),
|
|
3769
|
-
contextID=lesserFact.context.id, unitID=lesserFact.unit.id if lesserFact.unit is not None else "(none)",
|
|
3770
|
-
edgarCode=ruleEdgarCode, ruleElementId=id)
|
|
3771
|
-
elif dqcRuleName in ("DQC.US.0013","DQC.US.0015") and "DQC.US.0015" in ugtRels:
|
|
3772
|
-
dqc0015 = ugtRels["DQC.US.0015"]
|
|
3773
|
-
isDQC0013 = dqcRuleName == "DQC.US.0013"
|
|
3774
|
-
posIncomeBeforeTax = {} # reported for 0013 by context hash
|
|
3775
|
-
if isDQC0013: # 0013 has a precondition
|
|
3776
|
-
incomeBeforeTax = None # precondition, must be positive
|
|
3777
|
-
for pre in dqcRule["precondition"]: # array of facts to bind and condition on if first is present
|
|
3778
|
-
for (ctxHash,_unitHash), b in factBindings(val.modelXbrl, pre).items():
|
|
3779
|
-
if pre[0] in b:
|
|
3780
|
-
incomeBeforeTax = sum(f.xValue for f in b.values())
|
|
3781
|
-
if incomeBeforeTax > 0 and ctxHash not in posIncomeBeforeTax:
|
|
3782
|
-
posIncomeBeforeTax[ctxHash] = incomeBeforeTax
|
|
3783
|
-
if not posIncomeBeforeTax: # no positive values for any context
|
|
3784
|
-
continue # precondition fails, skip rule
|
|
3785
|
-
concepts = set()
|
|
3786
|
-
conceptRuleIDs = {}
|
|
3787
|
-
for id, name in dqcRule["concepts"].items():
|
|
3788
|
-
for concept in modelXbrl.nameConcepts.get(name, ()):
|
|
3789
|
-
qn = concept.qname
|
|
3790
|
-
concepts.add(qn)
|
|
3791
|
-
conceptRuleIDs[qn] = id
|
|
3792
|
-
break
|
|
3793
|
-
else:
|
|
3794
|
-
concepts = dqc0015.concepts
|
|
3795
|
-
conceptRuleIDs = dqc0015.conceptRuleIDs
|
|
3796
|
-
additionalExcludedNames = set(dqcRule["additional-excluded-names"])
|
|
3797
|
-
excludedConceptTypedDimensions = dqcRule.get("excluded-concept-typed-dimensions", EMPTY_DICT)
|
|
3798
|
-
warnedFactsByQn = defaultdict(list)
|
|
3799
|
-
for f in modelXbrl.facts:
|
|
3800
|
-
if (f.qname in concepts and f.isNumeric and not f.isNil and f.xValid >= VALID and f.xValue < 0 and f.context is not None and (
|
|
3801
|
-
not isDQC0013 or f.context.contextDimAwareHash in posIncomeBeforeTax) and (
|
|
3802
|
-
all((d.isTyped and # typed member exclusion
|
|
3803
|
-
d.dimensionQname.localName not in excludedConceptTypedDimensions.get(f.qname.localName, EMPTY_SET)
|
|
3804
|
-
) or (d.isExplicit and # explicit dimension exclusion
|
|
3805
|
-
(d.dimensionQname not in dqc0015.excludedAxesMembers or
|
|
3806
|
-
("*" not in dqc0015.excludedAxesMembers[d.dimensionQname] and
|
|
3807
|
-
d.memberQname not in dqc0015.excludedAxesMembers[d.dimensionQname])) and
|
|
3808
|
-
d.memberQname not in dqc0015.excludedMembers and
|
|
3809
|
-
(dqc0015.excludedMemberNamesPattern is None or
|
|
3810
|
-
not dqc0015.excludedMemberNamesPattern.search(d.memberQname.localName)))
|
|
3811
|
-
for d in f.context.qnameDims.values())) and (
|
|
3812
|
-
f.qname.localName not in additionalExcludedNames)):
|
|
3813
|
-
if not any(f.isDuplicateOf(warnedFact) for warnedFact in warnedFactsByQn[f.qname]):
|
|
3814
|
-
id = conceptRuleIDs.get(f.qname, 9999)
|
|
3815
|
-
warnedFactsByQn[f.qname].append(f)
|
|
3816
|
-
modelXbrl.warning("{}.{}".format(dqcRuleName, id), _(logMsg(msg)),
|
|
3817
|
-
modelObject=f, name=f.qname, value=f.value, contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
3818
|
-
incomeBeforeTax=posIncomeBeforeTax.get(f.context.contextDimAwareHash), # used by 0013 message
|
|
3819
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3820
|
-
del warnedFactsByQn # dereference objects
|
|
3821
|
-
elif (dqcRuleName == "DQC.US.0033" and hasDocPerEndDateFact
|
|
3822
|
-
and not (deiDocumentType == "8K" and any(f.get("xValue") for f in modelXbrl.factsByLocalName.get("AmendmentFlag",())))
|
|
3823
|
-
and abs((documentPeriodEndDate + ONE_DAY - documentPeriodEndDateFact.context.endDatetime).days) == 0): # was 3
|
|
3824
|
-
for id, rule in dqcRule["rules"].items():
|
|
3825
|
-
for n in rule["names"]:
|
|
3826
|
-
for f in modelXbrl.factsByLocalName.get(n,()):
|
|
3827
|
-
if f.context is not None and not dateUnionEqual(documentPeriodEndDate, f.context.endDatetime, instantEndDate=True):
|
|
3828
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3829
|
-
modelObject=f, name=f.qname.localName, endDate=XmlUtil.dateunionValue(f.context.endDatetime, subtractOneDay=True),
|
|
3830
|
-
documentPeriodEndDate=documentPeriodEndDate,
|
|
3831
|
-
contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
3832
|
-
incomeBeforeTax=incomeBeforeTax,
|
|
3833
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3834
|
-
elif dqcRuleName == "DQC.US.0036" and hasDocPerEndDateFact:
|
|
3835
|
-
for id, rule in dqcRule["rules"].items():
|
|
3836
|
-
if f.context is not None and abs((documentPeriodEndDate + ONE_DAY - documentPeriodEndDateFact.context.endDatetime).days) > 1: # was 3
|
|
3837
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3838
|
-
modelObject=f, name=documentPeriodEndDateFact.qname.localName,
|
|
3839
|
-
endDate=XmlUtil.dateunionValue(documentPeriodEndDateFact.context.endDatetime, subtractOneDay=True),
|
|
3840
|
-
documentPeriodEndDate=documentPeriodEndDate,
|
|
3841
|
-
contextID=documentPeriodEndDateFact.context.id,
|
|
3842
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3843
|
-
elif dqcRuleName == "DQC.US.0041":
|
|
3844
|
-
ugtAxisDefaults = ugtRels["axis-defaults"]
|
|
3845
|
-
for id, rule in dqcRule["rules"].items():
|
|
3846
|
-
for rel in modelXbrl.relationshipSet(XbrlConst.dimensionDefault).modelRelationships:
|
|
3847
|
-
if (rel.fromModelObject is not None and rel.toModelObject is not None
|
|
3848
|
-
and rel.fromModelObject.qname in ugtAxisDefaults
|
|
3849
|
-
and ugtAxisDefaults[rel.fromModelObject.qname] != rel.toModelObject.qname):
|
|
3850
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3851
|
-
modelObject=(rel, rel.fromModelObject), axisName=rel.fromModelObject.qname,
|
|
3852
|
-
axisDefaultName=ugtAxisDefaults[rel.fromModelObject.qname],
|
|
3853
|
-
extensionDefaultName=rel.toModelObject.qname,
|
|
3854
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3855
|
-
elif dqcRuleName == "DQC.US.0043":
|
|
3856
|
-
incomeNames = dqcRule["income-names"]
|
|
3857
|
-
def descendantWeights(fromConcept, ELR=None, effectiveWeight=1, bottomWeights=None, visited=None):
|
|
3858
|
-
if visited is None:
|
|
3859
|
-
visited = set()
|
|
3860
|
-
bottomWeights = set()
|
|
3861
|
-
visited.add(fromConcept)
|
|
3862
|
-
for rel in modelXbrl.relationshipSet(XbrlConst.summationItems, ELR).fromModelObject(fromConcept):
|
|
3863
|
-
if rel.toModelObject is not None and rel.toModelObject.name not in incomeNames:
|
|
3864
|
-
w = effectiveWeight * rel.weight
|
|
3865
|
-
bottomWeights.add((rel.toModelObject, w))
|
|
3866
|
-
descendantWeights(rel.toModelObject, rel.linkrole, w, bottomWeights, visited)
|
|
3867
|
-
visited.discard(fromConcept)
|
|
3868
|
-
return bottomWeights
|
|
3869
|
-
|
|
3870
|
-
for id, rule in dqcRule["rules"].items():
|
|
3871
|
-
topName = rule["parent-name"]
|
|
3872
|
-
if (modelXbrl.factsByLocalName.get(topName,())
|
|
3873
|
-
and ("excluded-name" not in rule or not modelXbrl.factsByLocalName.get(rule["excluded-name"],()))):
|
|
3874
|
-
top = modelXbrl.nameConcepts[topName][0]
|
|
3875
|
-
for bottom, effectiveWeight in descendantWeights(top): # don't include stopping income concept
|
|
3876
|
-
if ((bottom.balance == "credit" and effectiveWeight > 0)
|
|
3877
|
-
or (bottom.balance == "debit" and effectiveWeight < 0)):
|
|
3878
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg[bottom.balance or ""])),
|
|
3879
|
-
modelObject=(top, bottom), topName=top.name, bottomName=bottom.name,
|
|
3880
|
-
edgarCode=f"{edgarCode}-{bottom.balance}", ruleElementId=id)
|
|
3881
|
-
|
|
3882
|
-
elif dqcRuleName == "DQC.US.0044":
|
|
3883
|
-
ugtAccrualItems = ugtRels["accrual-items"]
|
|
3884
|
-
for id, rule in dqcRule["rules"].items():
|
|
3885
|
-
def checkAccrualDescendants(rel, visited):
|
|
3886
|
-
if rel.toModelObject is not None:
|
|
3887
|
-
name = rel.toModelObject.name
|
|
3888
|
-
if name in ugtAccrualItems:
|
|
3889
|
-
for f in modelXbrl.factsByLocalName[name]:
|
|
3890
|
-
if f.xValue != 0:
|
|
3891
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3892
|
-
modelObject=f, name=name, contextID=f.contextID, unitID=f.unitID or "(none)", value=f.xValue,
|
|
3893
|
-
activity=rule["activity"],
|
|
3894
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3895
|
-
if name not in visited:
|
|
3896
|
-
visited.add(name)
|
|
3897
|
-
for childRel in modelXbrl.relationshipSet(rel.arcrole, rel.consecutiveLinkrole).fromModelObject(rel.toModelObject):
|
|
3898
|
-
checkAccrualDescendants(childRel, visited)
|
|
3899
|
-
visited.discard(name)
|
|
3900
|
-
for parentLn in rule["summation-items"]:
|
|
3901
|
-
for parentConcept in modelXbrl.nameConcepts[parentLn]:
|
|
3902
|
-
for rel in val.summationItemRelsSetAllELRs.fromModelObject(parentConcept):
|
|
3903
|
-
checkAccrualDescendants(rel, set())
|
|
3904
|
-
elif dqcRuleName == "DQC.US.0047":
|
|
3905
|
-
# 0047 has only one id, rule
|
|
3906
|
-
id, rule = next(iter(dqcRule["rules"].items()))
|
|
3907
|
-
calcRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.summationItems, linkroleUri)
|
|
3908
|
-
excludedChildren = set(rule["excluded-children"])
|
|
3909
|
-
for parentName in rule["parents"]:
|
|
3910
|
-
for parentConcept in modelXbrl.nameConcepts.get(parentName,()):
|
|
3911
|
-
for rel in calcRelationshipSet.fromModelObject(parentConcept):
|
|
3912
|
-
if rel.toModelObject is not None:
|
|
3913
|
-
childConcept = rel.toModelObject
|
|
3914
|
-
childName = childConcept.qname.localName
|
|
3915
|
-
if not childConcept.balance and childName not in excludedChildren and isStandardUri(val, rel.toModelObject.modelDocument.uri):
|
|
3916
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3917
|
-
modelObject=(rel, parentConcept, childConcept), # may be no base sets, in which case just show the instance
|
|
3918
|
-
parentName=parentName, childName=childName,
|
|
3919
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3920
|
-
elif dqcRuleName == "DQC.US.0048" and deiDocumentType not in dqcRule["excluded-document-types"]:
|
|
3921
|
-
# 0048 has only one id, rule
|
|
3922
|
-
id, rule = next(iter(dqcRule["rules"].items()))
|
|
3923
|
-
# check if calc root check is blocked
|
|
3924
|
-
blockRootCheck = any(f.xValue == v for ln,v in dqcRule["blocking-facts"].items() for f in modelXbrl.factsByLocalName.get(ln,()))
|
|
3925
|
-
# find presentation ELR of interestStatementOfCashFlowsAbstract
|
|
3926
|
-
preCashFlowLinkRoles = set()
|
|
3927
|
-
calcCashFlowLinkRoles = set()
|
|
3928
|
-
calcCashFlowLinkRolesMissingRoots = set()
|
|
3929
|
-
linkroleUris = OrderedSet(modelLink.role for modelLink in val.modelXbrl.baseSets[(XbrlConst.parentChild,None,None,None)])
|
|
3930
|
-
for linkroleUri in linkroleUris: # role ELRs may be repeated in pre LB
|
|
3931
|
-
roleTypes = val.modelXbrl.roleTypes.get(linkroleUri)
|
|
3932
|
-
definition = (roleTypes[0].definition or linkroleUri) if roleTypes else linkroleUri
|
|
3933
|
-
preRoots = modelXbrl.relationshipSet(XbrlConst.parentChild, linkroleUri, None, None).rootConcepts
|
|
3934
|
-
if ((any(c.name == "StatementOfCashFlowsAbstract" for c in preRoots) or
|
|
3935
|
-
'cashflow' in linkroleUri.lower())
|
|
3936
|
-
and ' - Statement - ' in definition and 'parenthetical' not in linkroleUri.lower()):
|
|
3937
|
-
preCashFlowLinkRoles.add(linkroleUri)
|
|
3938
|
-
calcRelationshipSet = modelXbrl.relationshipSet(XbrlConst.summationItems, linkroleUri)
|
|
3939
|
-
calcRoots = calcRelationshipSet.rootConcepts
|
|
3940
|
-
if calcRoots:
|
|
3941
|
-
calcCashFlowLinkRoles.add(linkroleUri)
|
|
3942
|
-
roots = rule["roots"]
|
|
3943
|
-
if not (blockRootCheck or
|
|
3944
|
-
any(all(any(c.name == rName for c in calcRoots) for rName in rNames) for rNames in roots)):
|
|
3945
|
-
calcCashFlowLinkRolesMissingRoots.add(linkroleUri)
|
|
3946
|
-
if preCashFlowLinkRoles:
|
|
3947
|
-
if not calcCashFlowLinkRoles:
|
|
3948
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(dqcRule["message-no-roles"])),
|
|
3949
|
-
modelObject=modelXbrl,
|
|
3950
|
-
linkRoles=(", ".join(sorted(preCashFlowLinkRoles))),
|
|
3951
|
-
edgarCode=edgarCode+"-No-Roles", ruleElementId=id)
|
|
3952
|
-
elif calcCashFlowLinkRolesMissingRoots == calcCashFlowLinkRoles: # every calc is missing the roots
|
|
3953
|
-
for linkRole in calcCashFlowLinkRolesMissingRoots:
|
|
3954
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3955
|
-
modelObject=val.modelXbrl.baseSets[(XbrlConst.summationItem,linkroleUri,None,None)]
|
|
3956
|
-
or val.modelXbrl.baseSets[(XbrlConst.summationItem11,linkroleUri,None,None)]
|
|
3957
|
-
or modelXbrl, # may be no base sets, in which case just show the instance
|
|
3958
|
-
linkRole=linkroleUri, linkroleDefinition=definition,
|
|
3959
|
-
rootNames=(", ".join(r.name for r in calcRoots) or "(none)"),
|
|
3960
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3961
|
-
elif dqcRuleName == "DQC.US.0053":
|
|
3962
|
-
# 0047 has only one id, rule
|
|
3963
|
-
for id, rule in dqcRule["rules"].items():
|
|
3964
|
-
def checkMember(axis, rel, excludedMemNames, visited):
|
|
3965
|
-
if rel.toModelObject is not None:
|
|
3966
|
-
name = rel.toModelObject.name
|
|
3967
|
-
if name in excludedMemNames:
|
|
3968
|
-
return rel
|
|
3969
|
-
if name not in visited:
|
|
3970
|
-
visited.add(name)
|
|
3971
|
-
for childRel in modelXbrl.relationshipSet(XbrlConst.domainMember, rel.consecutiveLinkrole).fromModelObject(rel.toModelObject):
|
|
3972
|
-
mRel = checkMember(axis, childRel, excludedMemNames, visited)
|
|
3973
|
-
if mRel is not None:
|
|
3974
|
-
return mRel
|
|
3975
|
-
visited.discard(name)
|
|
3976
|
-
return None
|
|
3977
|
-
for dimName, excludedMemNames in rule["excluded-dimension-members"].items():
|
|
3978
|
-
for dimConcept in modelXbrl.nameConcepts.get(dimName, ()):
|
|
3979
|
-
for rel in modelXbrl.relationshipSet(XbrlConst.dimensionDomain).fromModelObject(dimConcept):
|
|
3980
|
-
mRel = checkMember(rel.fromModelObject, rel, excludedMemNames, set())
|
|
3981
|
-
if mRel is not None: # look for any facts
|
|
3982
|
-
factsFound = False
|
|
3983
|
-
for memName in excludedMemNames:
|
|
3984
|
-
for memConcept in modelXbrl.nameConcepts.get(memName, ()):
|
|
3985
|
-
for f in modelXbrl.factsByDimMemQname(dimConcept.qname, memConcept.qname):
|
|
3986
|
-
factsFound = True
|
|
3987
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
3988
|
-
modelObject=(f, mRel), member=memName, axis=dimName, linkRole=rel.linkrole,
|
|
3989
|
-
factName=f.qname, value=f.xValue,
|
|
3990
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
3991
|
-
if not factsFound:
|
|
3992
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(dqcRule["message-no-facts"])),
|
|
3993
|
-
modelObject=mRel, member=memName, axis=dimName, linkRole=rel.linkrole,
|
|
3994
|
-
edgarCode=f"{edgarCode}-No-Facts", ruleElementId=id)
|
|
3995
|
-
elif dqcRuleName == "DQC.US.0057":
|
|
3996
|
-
linkroleUris = OrderedSet(modelLink.role for modelLink in val.modelXbrl.baseSets[(XbrlConst.parentChild,None,None,None)])
|
|
3997
|
-
for linkroleUri in linkroleUris: # role ELRs may be repeated in pre LB
|
|
3998
|
-
roleTypes = val.modelXbrl.roleTypes.get(linkroleUri)
|
|
3999
|
-
definition = (roleTypes[0].definition or linkroleUri) if roleTypes else linkroleUri
|
|
4000
|
-
relSet = modelXbrl.relationshipSet(XbrlConst.parentChild, linkroleUri)
|
|
4001
|
-
preRoots = relSet.rootConcepts
|
|
4002
|
-
if ((any(c.name == "StatementOfCashFlowsAbstract" for c in preRoots) or
|
|
4003
|
-
'cashflow' in linkroleUri.lower())
|
|
4004
|
-
and ' - Statement - ' in definition and 'parenthetical' not in linkroleUri.lower()):
|
|
4005
|
-
balanceEltNames = set()
|
|
4006
|
-
balanceElts = set()
|
|
4007
|
-
def checkConcept(relSet, fromConcept, visited):
|
|
4008
|
-
for rel in relSet.fromModelObject(fromConcept):
|
|
4009
|
-
toConcept = rel.toModelObject
|
|
4010
|
-
if toConcept is not None and toConcept not in visited:
|
|
4011
|
-
if toConcept.periodType == "instant":
|
|
4012
|
-
balanceEltNames.add(toConcept.name)
|
|
4013
|
-
balanceElts.add(toConcept)
|
|
4014
|
-
visited.add(toConcept)
|
|
4015
|
-
checkConcept(relSet, toConcept, visited)
|
|
4016
|
-
visited.discard(toConcept)
|
|
4017
|
-
for c in preRoots:
|
|
4018
|
-
checkConcept(relSet, c, set())
|
|
4019
|
-
for id, rule in dqcRule["rules"].items():
|
|
4020
|
-
mustBePresentElements = rule["must-be-present-elements"]
|
|
4021
|
-
if not balanceEltNames & set(mustBePresentElements):
|
|
4022
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(rule["message"])),
|
|
4023
|
-
modelObject=balanceElts, role=linkroleUri, elementNames=", ".join(mustBePresentElements),
|
|
4024
|
-
balanceElements=", ".join(sorted(balanceEltNames)),
|
|
4025
|
-
edgarCode=f"{edgarCode}-{id}", ruleElementId=id)
|
|
4026
|
-
balanceElts.clear() # deref
|
|
4027
|
-
elif dqcRuleName == "DQC.US.0060":
|
|
4028
|
-
for id, rule in dqcRule["rules"].items():
|
|
4029
|
-
for eltLn, depLns in rule["element-dependencies"].items():
|
|
4030
|
-
bindings = factBindings(val.modelXbrl, flattenToSet( (eltLn, depLns )), nils=False, noAdditionalDims=True)
|
|
4031
|
-
for b in bindings.values():
|
|
4032
|
-
if eltLn in b and not any(depLn in b for depLn in depLns):
|
|
4033
|
-
f = b[eltLn]
|
|
4034
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
4035
|
-
modelObject=b.values(), name=eltLn, value=f.xValue,
|
|
4036
|
-
dependentElements=", ".join(depLns),
|
|
4037
|
-
contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
4038
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
4039
|
-
elif dqcRuleName == "DQC.US.0071":
|
|
4040
|
-
# 0071 has only one id, rule
|
|
4041
|
-
id, rule = next(iter(dqcRule["rules"].items()))
|
|
4042
|
-
dimConcept = modelXbrl.qnameConcepts.get(qname(rule["axis"], deiDefaultPrefixedNamespaces))
|
|
4043
|
-
domConcept = modelXbrl.qnameConcepts.get(qname(rule["domain"], deiDefaultPrefixedNamespaces))
|
|
4044
|
-
dimToSkipIfPresent = modelXbrl.nameConcepts.get(rule["skip-if-axis-present"])
|
|
4045
|
-
priItemNames = rule["primary-items"]
|
|
4046
|
-
priItemConcepts = set(concept
|
|
4047
|
-
for name in priItemNames
|
|
4048
|
-
if name in modelXbrl.nameConcepts
|
|
4049
|
-
for concept in modelXbrl.nameConcepts[name])
|
|
4050
|
-
priItemQnames = sorted(concept.qname for concept in priItemConcepts)
|
|
4051
|
-
if dimToSkipIfPresent:
|
|
4052
|
-
dimToSkipIfPresent = dimToSkipIfPresent[0] # first nameConcept
|
|
4053
|
-
|
|
4054
|
-
def getDescendants(arcRoles, fromConcept, elr, startAtObj=None, descendants=None, visited=None, cubeOnly=False):
|
|
4055
|
-
if descendants is None:
|
|
4056
|
-
descendants = set()
|
|
4057
|
-
visited = set()
|
|
4058
|
-
for rel in modelXbrl.relationshipSet(arcRoles, elr).fromModelObject(fromConcept):
|
|
4059
|
-
toConcept = rel.toModelObject
|
|
4060
|
-
if toConcept == startAtObj:
|
|
4061
|
-
startAtObj = None
|
|
4062
|
-
elif startAtObj == None: # below startAtObj
|
|
4063
|
-
if not cubeOnly or toConcept.isHypercubeItem:
|
|
4064
|
-
descendants.add(toConcept)
|
|
4065
|
-
if toConcept is not None and toConcept not in visited:
|
|
4066
|
-
visited.add(toConcept)
|
|
4067
|
-
getDescendants(arcRoles, toConcept, rel.consecutiveLinkrole, startAtObj, descendants, visited)
|
|
4068
|
-
visited.discard(toConcept)
|
|
4069
|
-
return descendants
|
|
4070
|
-
|
|
4071
|
-
for linkroleUri in OrderedSet(modelLink.role for modelLink in val.modelXbrl.baseSets[(XbrlConst.all,None,None,None)]): # role ELRs may be repeated in dim LB
|
|
4072
|
-
roleTypes = modelXbrl.roleTypes.get(linkroleUri)
|
|
4073
|
-
if not roleTypes or " - Statement - " not in roleTypes[0].definition:
|
|
4074
|
-
continue
|
|
4075
|
-
tableRelSet = modelXbrl.relationshipSet("XBRL-dimensions", linkroleUri)
|
|
4076
|
-
priItemRelSet = modelXbrl.relationshipSet(XbrlConst.domainMember, linkroleUri)
|
|
4077
|
-
cubeRoots = tableRelSet.rootConcepts
|
|
4078
|
-
for cubeRoot in cubeRoots:
|
|
4079
|
-
for cube in getDescendants("XBRL-dimensions",cubeRoot, linkroleUri, cubeOnly=True):
|
|
4080
|
-
if (tableRelSet.isRelated(cube, "descendant", dimToSkipIfPresent, isDRS=True) or
|
|
4081
|
-
not tableRelSet.isRelated(cube, "descendant", dimConcept, isDRS=True) or not any(
|
|
4082
|
-
priItemRelSet.isRelated(cubeRoot, "descendant", priItemConcept, isDRS=True)
|
|
4083
|
-
for priItemConcept in priItemConcepts)):
|
|
4084
|
-
continue
|
|
4085
|
-
domDescendants = getDescendants((XbrlConst.dimensionDomain, XbrlConst.domainMember),domConcept, linkroleUri)
|
|
4086
|
-
if len(domDescendants) == 1:
|
|
4087
|
-
for boundFacts in factBindings(modelXbrl, priItemNames, coverDimQnames=(dimConcept.qname,)).values():
|
|
4088
|
-
factsWithDim = set()
|
|
4089
|
-
factsWithoutDim = set()
|
|
4090
|
-
for f in boundFacts.values():
|
|
4091
|
-
if f.context is not None:
|
|
4092
|
-
if dimConcept.qname in f.context.qnameDims and f.context.qnameDims[dimConcept.qname].member in domDescendants:
|
|
4093
|
-
factsWithDim.add(f)
|
|
4094
|
-
else:
|
|
4095
|
-
factsWithoutDim.add(f)
|
|
4096
|
-
if len(factsWithDim) == 1 and len(factsWithoutDim) == 0:
|
|
4097
|
-
f = factsWithDim.pop()
|
|
4098
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
4099
|
-
modelObject=f, name=f.qname,value=f.xValue, role=linkroleUri, table=cubeRoot.qname,
|
|
4100
|
-
member=f.context.qnameDims[dimConcept.qname].memberQname,
|
|
4101
|
-
contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
4102
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
4103
|
-
elif dqcRuleName == "DQC.US.0073":
|
|
4104
|
-
# 0073 has only one id, rule
|
|
4105
|
-
id, rule = next(iter(dqcRule["rules"].items()))
|
|
4106
|
-
dimConcept = modelXbrl.nameConcepts.get(rule["axis"], ())
|
|
4107
|
-
if not dimConcept:
|
|
4108
|
-
continue # axis not in taxonomy
|
|
4109
|
-
dimConcept = dimConcept[0]
|
|
4110
|
-
allowablePrimaryItems = rule["allowable-primary-items"]
|
|
4111
|
-
allowablePrimaryItemSet = set(allowablePrimaryItems)
|
|
4112
|
-
for f in modelXbrl.factsByDimMemQname(dimConcept.qname, NONDEFAULT):
|
|
4113
|
-
if isStandardUri(val, f.concept.modelDocument.uri) and f.concept.name not in allowablePrimaryItemSet:
|
|
4114
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
4115
|
-
modelObject=f, name=f.qname,value=f.xValue,
|
|
4116
|
-
allowableNames=", ".join(allowablePrimaryItems),
|
|
4117
|
-
contextID=f.contextID, unitID=f.unitID or "(none)",
|
|
4118
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
4119
|
-
elif dqcRuleName == "DQC.US.0079":
|
|
4120
|
-
for id, rule in dqcRule["rules"].items():
|
|
4121
|
-
ignoreDims = rule["acceptable-dimensions"]
|
|
4122
|
-
replacementMembers = rule["replacement-members"]
|
|
4123
|
-
def checkMember(axis, rel, visited):
|
|
4124
|
-
if rel.toModelObject is not None:
|
|
4125
|
-
name = rel.toModelObject.name
|
|
4126
|
-
if name.lower() in replacementMembers and rel.toModelObject.qname.namespaceURI not in val.disclosureSystem.standardTaxonomiesDict:
|
|
4127
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
4128
|
-
modelObject=(rel, rel.toModelObject), member=rel.toModelObject.qname, axis=axis.qname,
|
|
4129
|
-
replacement=replacementMembers[name.lower()],
|
|
4130
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
4131
|
-
if name not in visited:
|
|
4132
|
-
visited.add(name)
|
|
4133
|
-
for childRel in modelXbrl.relationshipSet(XbrlConst.domainMember, rel.consecutiveLinkrole).fromModelObject(rel.toModelObject):
|
|
4134
|
-
checkMember(axis, childRel, visited)
|
|
4135
|
-
visited.discard(name)
|
|
4136
|
-
for rel in modelXbrl.relationshipSet(XbrlConst.dimensionDomain).modelRelationships:
|
|
4137
|
-
if rel.fromModelObject is not None and rel.fromModelObject.name not in ignoreDims:
|
|
4138
|
-
checkMember(rel.fromModelObject, rel, set())
|
|
4139
|
-
elif dqcRuleName == "DQC.US.0084":
|
|
4140
|
-
# 0084 has only one id, rule
|
|
4141
|
-
id, rule = next(iter(dqcRule["rules"].items()))
|
|
4142
|
-
tolerance = rule["tolerance"]
|
|
4143
|
-
immaterialDifferenceFlag = "ImmaterialDifferenceFlag" in modelXbrl.factsByLocalName
|
|
4144
|
-
durationFactNames = set(f.concept.name
|
|
4145
|
-
for f in modelXbrl.factsByPeriodType("duration")
|
|
4146
|
-
if f.xValid >= VALID and f.concept.isMonetary and isStandardUri(val, f.concept.modelDocument.uri) and "average" not in f.concept.name.lower())
|
|
4147
|
-
# aggreate bound facts by local name & dims for period sleuthing
|
|
4148
|
-
def checkPerFacts(*facts):
|
|
4149
|
-
minDec = leastDecimals(facts)
|
|
4150
|
-
itemValues = [f.xValue for f in facts[1:]]
|
|
4151
|
-
difference = abs(facts[0].xValue - sum(itemValues))
|
|
4152
|
-
if isinf(minDec):
|
|
4153
|
-
maxDiff = 0
|
|
4154
|
-
elif minDec == 0 and immaterialDifferenceFlag:
|
|
4155
|
-
maxDiff == Decimal(abs(facts[0].xValue)) * Decimal("0.01")
|
|
4156
|
-
else:
|
|
4157
|
-
maxDiff = pow(10, -minDec) * tolerance * (len(facts) - 2)
|
|
4158
|
-
if difference > maxDiff:
|
|
4159
|
-
sumFact = facts[0]
|
|
4160
|
-
modelXbrl.warning(f"{dqcRuleName}.{id}", _(logMsg(msg)),
|
|
4161
|
-
modelObject=facts, name=sumFact.concept.name, value=sumFact.xValue,
|
|
4162
|
-
sumPeriods=sum(itemValues),
|
|
4163
|
-
difference=difference, minDecimals=minDec, tolerance=tolerance,
|
|
4164
|
-
periods=", \n".join(f"{XmlUtil.dateunionValue(f.context.startDatetime)}/{XmlUtil.dateunionValue(f.context.endDatetime, subtractOneDay=True)} {f.xValue}" for f in facts[1:]),
|
|
4165
|
-
contextID=sumFact.context.id, unitID=sumFact.unit.id if sumFact.unit is not None else "(none)",
|
|
4166
|
-
edgarCode=edgarCode, ruleElementId=id)
|
|
4167
|
-
|
|
4168
|
-
# dict by dimHash by localName of set of facts
|
|
4169
|
-
dimBoundFactsByPeriod = defaultdict(dict) # emulate defaultdict(defaultdict(set))
|
|
4170
|
-
for dimHash, binding in factBindings(modelXbrl, durationFactNames, coverPeriod=True).items():
|
|
4171
|
-
periodCoveredFacts = dimBoundFactsByPeriod[dimHash]
|
|
4172
|
-
for ln, perFacts in binding.items():
|
|
4173
|
-
if ln not in periodCoveredFacts:
|
|
4174
|
-
periodCoveredFacts[ln] = set()
|
|
4175
|
-
for f in perFacts.values():
|
|
4176
|
-
periodCoveredFacts[ln].add(f)
|
|
4177
|
-
for periodCoveredFacts in dimBoundFactsByPeriod.values():
|
|
4178
|
-
for ln, facts in periodCoveredFacts.items():
|
|
4179
|
-
startPerFacts = defaultdict(set)
|
|
4180
|
-
for f in facts:
|
|
4181
|
-
startPerFacts[f.context.startDatetime].add(f)
|
|
4182
|
-
for s1, facts in startPerFacts.items(): # s1 is period holding subperiod facts
|
|
4183
|
-
if len(facts) > 1: # needs longer and shorter duration of same start
|
|
4184
|
-
for f1 in facts:
|
|
4185
|
-
# find any fact other facts with f1's duration
|
|
4186
|
-
e1 = f1.context.endDatetime
|
|
4187
|
-
for f2 in facts:
|
|
4188
|
-
e2 = f2.context.endDatetime
|
|
4189
|
-
if e2 < e1 and f2 != f1: # f2 is with f1 duration
|
|
4190
|
-
for f3 in startPerFacts.get(e2,()):
|
|
4191
|
-
e3 = f3.context.endDatetime
|
|
4192
|
-
if e3 == e1:
|
|
4193
|
-
checkPerFacts(f1, f2, f3)
|
|
4194
|
-
elif e3 < e1:
|
|
4195
|
-
for f4 in startPerFacts.get(e3,()):
|
|
4196
|
-
e4 = f4.context.endDatetime
|
|
4197
|
-
if e4 == e1:
|
|
4198
|
-
checkPerFacts(f1, f2, f3, f4)
|
|
4199
|
-
elif e4 < e1:
|
|
4200
|
-
for f5 in startPerFacts.get(e4,()):
|
|
4201
|
-
e5 = f5.context.endDatetime
|
|
4202
|
-
if e5 == e1:
|
|
4203
|
-
checkPerFacts(f1, f2, f3, f4, f5)
|
|
4204
|
-
|
|
4205
|
-
val.modelXbrl.profileActivity("... DQCRT checks", minTimeToShow=0.1)
|
|
4206
|
-
del val.summationItemRelsSetAllELRs
|
|
4207
|
-
|
|
4208
|
-
if "EFM/Filing.py#validateFiling_end" in val.modelXbrl.arelleUnitTests:
|
|
4209
|
-
raise pyNamedObject(val.modelXbrl.arelleUnitTests["EFM/Filing.py#validateFiling_end"], "EFM/Filing.py#validateFiling_end")
|
|
4210
|
-
|
|
4211
|
-
if isEFM:
|
|
4212
|
-
for pluginXbrlMethod in pluginClassMethods("Validate.EFM.Finally"):
|
|
4213
|
-
pluginXbrlMethod(val, conceptsUsed)
|
|
4214
|
-
val.modelXbrl.profileActivity("... plug in '.Finally' checks", minTimeToShow=1.0)
|
|
4215
|
-
val.modelXbrl.profileStat(_("validate{0}").format(modelXbrl.modelManager.disclosureSystem.validationType))
|
|
4216
|
-
|
|
4217
|
-
modelXbrl.modelManager.showStatus(_("ready"), 2000)
|
|
4218
|
-
|
|
4219
|
-
def isStandardUri(val, uri):
|
|
4220
|
-
try:
|
|
4221
|
-
return val._isStandardUri[uri]
|
|
4222
|
-
except KeyError:
|
|
4223
|
-
isStd = (uri in val.disclosureSystem.standardTaxonomiesDict or
|
|
4224
|
-
(not isHttpUrl(uri) and
|
|
4225
|
-
# try 2011-12-23 RH: if works, remove the localHrefs
|
|
4226
|
-
# any(u.endswith(e) for u in (uri.replace("\\","/"),) for e in disclosureSystem.standardLocalHrefs)
|
|
4227
|
-
"/basis/sbr/" in uri.replace("\\","/")
|
|
4228
|
-
))
|
|
4229
|
-
val._isStandardUri[uri] = isStd
|
|
4230
|
-
return isStd
|
|
4231
|
-
|
|
4232
|
-
def directedCycle(val, relFrom, origin, fromRelationships, path):
|
|
4233
|
-
if relFrom in fromRelationships:
|
|
4234
|
-
for rel in fromRelationships[relFrom]:
|
|
4235
|
-
relTo = rel.toModelObject
|
|
4236
|
-
if relTo == origin:
|
|
4237
|
-
return [rel]
|
|
4238
|
-
if relTo not in path: # report cycle only where origin causes the cycle
|
|
4239
|
-
path.add(relTo)
|
|
4240
|
-
foundCycle = directedCycle(val, relTo, origin, fromRelationships, path)
|
|
4241
|
-
if foundCycle is not None:
|
|
4242
|
-
foundCycle.insert(0, rel)
|
|
4243
|
-
return foundCycle
|
|
4244
|
-
path.discard(relTo)
|
|
4245
|
-
return None
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
def checkConceptLabels(val, modelXbrl, labelsRelationshipSet, disclosureSystem, concept):
|
|
4249
|
-
hasDefaultLangStandardLabel = False
|
|
4250
|
-
dupLabels = {}
|
|
4251
|
-
for modelLabelRel in labelsRelationshipSet.fromModelObject(concept):
|
|
4252
|
-
modelLabel = modelLabelRel.toModelObject
|
|
4253
|
-
if isinstance(modelLabel, ModelResource) and modelLabel.xmlLang and modelLabel.modelDocument.inDTS:
|
|
4254
|
-
if modelLabel.xmlLang.startswith(disclosureSystem.defaultXmlLang) and \
|
|
4255
|
-
modelLabel.role == XbrlConst.standardLabel:
|
|
4256
|
-
hasDefaultLangStandardLabel = True
|
|
4257
|
-
dupDetectKey = ( (modelLabel.role or ''), modelLabel.xmlLang)
|
|
4258
|
-
if dupDetectKey in dupLabels:
|
|
4259
|
-
modelXbrl.error(("EFM.6.10.02", "GFM.1.5.2"),
|
|
4260
|
-
_("Concept %(concept)s has duplicated labels for role %(role)s lang %(lang)s."),
|
|
4261
|
-
edgarCode="cp-1002-Element-Used-Has-Duplicate-Label",
|
|
4262
|
-
modelObject=(modelLabel, dupLabels[dupDetectKey]), # removed concept from modelObjects
|
|
4263
|
-
concept=concept.qname, role=dupDetectKey[0], lang=dupDetectKey[1])
|
|
4264
|
-
# these are the element hrefs to the two labels, may be useful to make prohibiting arc's loc
|
|
4265
|
-
# f"{modelLabelRel.toModelObject.modelDocument.uri}#{XmlUtil.elementFragmentIdentifier(modelLabel)}"
|
|
4266
|
-
# f"{dupLabels[dupDetectKey].modelDocument.uri}#{XmlUtil.elementFragmentIdentifier(dupLabels[dupDetectKey])}"
|
|
4267
|
-
else:
|
|
4268
|
-
dupLabels[dupDetectKey] = modelLabel
|
|
4269
|
-
|
|
4270
|
-
#6 10.1 en-US standard label
|
|
4271
|
-
if not hasDefaultLangStandardLabel:
|
|
4272
|
-
modelXbrl.error(("EFM.6.10.01", "GFM.1.05.01"),
|
|
4273
|
-
_("You have submitted an instance using an element without an %(lang)s standard label %(concept)s. Please check your submission and correct the labels."),
|
|
4274
|
-
# concept must be the first referenced modelObject
|
|
4275
|
-
edgarCode="cp-1001-Element-Used-Standard-Label",
|
|
4276
|
-
modelObject=[concept] + list(modelXbrl.factsByQname[concept.qname]), concept=concept.qname,
|
|
4277
|
-
lang=disclosureSystem.defaultLanguage)
|
|
4278
|
-
|
|
4279
|
-
#6 10.3 default lang label for every role
|
|
4280
|
-
try:
|
|
4281
|
-
dupLabels[("zzzz",disclosureSystem.defaultXmlLang)] = None #to allow following loop
|
|
4282
|
-
priorRole = None
|
|
4283
|
-
priorLang = None
|
|
4284
|
-
hasDefaultLang = True
|
|
4285
|
-
for role, lang in sorted(dupLabels.keys()):
|
|
4286
|
-
if role != priorRole:
|
|
4287
|
-
if not hasDefaultLang:
|
|
4288
|
-
modelXbrl.error(("EFM.6.10.03", "GFM.1.5.3"),
|
|
4289
|
-
_("You have submitted an instance using an element %(concept)s with %(lang)s for role %(role)s. Please check your submission and correct the labels."),
|
|
4290
|
-
edgarCode="cp-1003-Element-Used-Standard-English-Label",
|
|
4291
|
-
modelObject=list(modelXbrl.factsByQname[concept.qname]) + [dupLabels[(priorRole,priorLang)]],
|
|
4292
|
-
concept=concept.qname,
|
|
4293
|
-
lang=disclosureSystem.defaultLanguage, role=priorRole)
|
|
4294
|
-
hasDefaultLang = False
|
|
4295
|
-
priorLang = lang
|
|
4296
|
-
priorRole = role
|
|
4297
|
-
if lang is not None and lang.startswith(disclosureSystem.defaultXmlLang):
|
|
4298
|
-
hasDefaultLang = True
|
|
4299
|
-
except Exception:
|
|
4300
|
-
pass
|
|
4301
|
-
|
|
4302
|
-
def deiParamEqual(deiName, xbrlVal, secVal):
|
|
4303
|
-
if xbrlVal is None: # nil fact
|
|
4304
|
-
return False
|
|
4305
|
-
if deiName == "DocumentPeriodEndDate":
|
|
4306
|
-
x = str(xbrlVal).split('-')
|
|
4307
|
-
s = secVal.split('-')
|
|
4308
|
-
return (x[0]==s[2] and x[1]==s[0] and x[2]==s[1])
|
|
4309
|
-
elif deiName == "CurrentFiscalYearEndDate":
|
|
4310
|
-
x = str(xbrlVal).lstrip('-').split('-')
|
|
4311
|
-
s = secVal.split('/')
|
|
4312
|
-
return (len(secVal) == 5 and secVal[2] == '/' and x[0] == s[0] and x[1] == s[1])
|
|
4313
|
-
elif deiName in {"EntityEmergingGrowthCompany", "EntityExTransitionPeriod", "EntityShellCompany",
|
|
4314
|
-
"EntitySmallBusiness", "EntityVoluntaryFilers", "EntityWellKnownSeasonedIssuer",
|
|
4315
|
-
"IcfrAuditorAttestationFlag",
|
|
4316
|
-
"cef:IntervalFundFlag", "cef:NewCefOrBdcRegistrantFlag", "cef:PrimaryShelfQualifiedFlag"}:
|
|
4317
|
-
return {"y": True, "yes": True, "true": True, "n": False, "no": False, "false": False
|
|
4318
|
-
}.get(str(xbrlVal).lower()) == {
|
|
4319
|
-
"yes":True, "Yes":True, "y":True, "Y":True, "no":False, "No":False, "N":False, "n":False
|
|
4320
|
-
}.get(secVal,secVal)
|
|
4321
|
-
elif deiName == "EntityFileNumber":
|
|
4322
|
-
return secVal == xbrlVal
|
|
4323
|
-
elif deiName == "EntityInvCompanyType":
|
|
4324
|
-
return xbrlVal in {"N-1A":("N-1A",), "N-1":("N-1",), "N-2":("N-2",), "N-3":("N-3",), "N-4":("N-4",), "N-5":("N-5",),
|
|
4325
|
-
"N-6":("N-6",), "S-1":("S-1","S-3"), "S-3":("S-1","S-3"),"S-6":("S-6",)}.get(secVal,())
|
|
4326
|
-
elif deiName == "EntityFilerCategory":
|
|
4327
|
-
return xbrlVal in {"Non-Accelerated Filer":("Non-accelerated Filer", "Smaller Reporting Company"),
|
|
4328
|
-
"Accelerated Filer":("Accelerated Filer", "Smaller Reporting Accelerated Filer"),
|
|
4329
|
-
"Large Accelerated Filer":("Large Accelerated Filer",),
|
|
4330
|
-
"Not Applicable":("Non-accelerated Filer", "Smaller Reporting Company")}.get(secVal,())
|
|
4331
|
-
elif deiName == "2014EntityFilerCategory":
|
|
4332
|
-
return xbrlVal in {True:("Smaller Reporting Company", "Smaller Reporting Accelerated Filer"),
|
|
4333
|
-
False:("Non-accelerated Filer", "Accelerated Filer", "Large Accelerated Filer")}.get(secVal,())
|
|
4334
|
-
elif deiName == "FeeRate":
|
|
4335
|
-
return xbrlVal == decimal.Decimal(secVal)
|
|
4336
|
-
return False # unhandled deiName
|
|
4337
|
-
|
|
4338
|
-
def eloValueOfFact(deiName, xbrlVal):
|
|
4339
|
-
if xbrlVal is None: # nil fact
|
|
4340
|
-
return None
|
|
4341
|
-
if deiName == "DocumentPeriodEndDate":
|
|
4342
|
-
return ("{1}-{2}-{0}".format(*str(xbrlVal).split('-')))
|
|
4343
|
-
elif deiName == "CurrentFiscalYearEndDate":
|
|
4344
|
-
return ("{0}/{1}".format(*str(xbrlVal).lstrip('-').split('-')))
|
|
4345
|
-
elif deiName in {"EntityEmergingGrowthCompany", "EntityExTransitionPeriod", "EntityShellCompany",
|
|
4346
|
-
"EntitySmallBusiness", "EntityVoluntaryFilers", "EntityWellKnownSeasonedIssuer",
|
|
4347
|
-
"IcfrAuditorAttestationFlag",
|
|
4348
|
-
"cef:NewCefOrBdcRegistrantFlag", "cef:NewCefOrBdcRegistrantFlag", "cef:NewCefOrBdcRegistrantFlag"}:
|
|
4349
|
-
return {"y": "yes", "yes": "yes", "true": "yes", "n": "no", "no": "no", "false": "no"
|
|
4350
|
-
}.get(str(xbrlVal).lower())
|
|
4351
|
-
elif deiName == "EntityFileNumber":
|
|
4352
|
-
return xbrlVal
|
|
4353
|
-
elif deiName == "EntityInvCompanyType":
|
|
4354
|
-
return xbrlVal
|
|
4355
|
-
elif deiName == "EntityFilerCategory":
|
|
4356
|
-
return xbrlVal
|
|
4357
|
-
elif isinstance(xbrlVal, bool):
|
|
4358
|
-
return xbrlVal
|
|
4359
|
-
elif isinstance(xbrlVal, list):
|
|
4360
|
-
return [v.localName if isinstance(v,QName) else str(v) for v in xbrlVal]
|
|
4361
|
-
return str(xbrlVal)
|
|
4362
|
-
|
|
4363
|
-
def cleanedCompanyName(name):
|
|
4364
|
-
for pattern, replacement in (
|
|
4365
|
-
(r"\s&(?=\s)", " and "), # Replace & with and
|
|
4366
|
-
(r"/.+/|\\.+\\", " "), # Remove any "/../" , "\...\" or "/../../" expression.
|
|
4367
|
-
(r"\s*[(].+[)]$", " "), # Remove any parenthetical expression if it occurs at the END of the string.
|
|
4368
|
-
(r"[\u058A\u05BE\u2010\u2011\u2012\u2013\u2014\u2015\uFE58\uFE63\uFF0D]", "-"), # Normalize fancy dashes.
|
|
4369
|
-
(r"-", ""), #dash to space
|
|
4370
|
-
(r"[\u2019']", ""), #Apostrophe to space
|
|
4371
|
-
(r"^\s*the(?=\s)", ""), # Remove the word "THE" (i.e., followed by space) from the beginning.
|
|
4372
|
-
(r"[^\w-]", " "), # Remove any punctuation.
|
|
4373
|
-
(r"^\w(?=\s)|\s\w(?=\s)|\s\w$", " "), # Remove single letter words
|
|
4374
|
-
(r"^INCORPORATED(?=\s|$)|(?<=\s)INCORPORATED(?=\s|$)", "INC"), # Truncate the word INCORPORATED (case insensitive) to INC
|
|
4375
|
-
(r"^CORPORATION(?=\s|$)|(?<=\s)CORPORATION(?=\s|$)", "CORP"), # Truncate the word CORPORATION (case insensitive) to CORP
|
|
4376
|
-
(r"^COMPANY(?=\s|$)|(?<=\s)COMPANY(?=\s|$)", "CO"), # Truncate the word CORPORATION (case insensitive) to CORP
|
|
4377
|
-
(r"^LIMITED(?=\s|$)|(?<=\s)LIMITED(?=\s|$)", "LTD"), # Truncate the word LIMITED (case insensitive) to LTD
|
|
4378
|
-
(r"^AND(?=\s|$)|(?<=\s)AND(?=\s|$)", "&"), # Replace the word AND with an ampersand (&)
|
|
4379
|
-
(r"\s+", " "), # Normalize all spaces (i.e., trim, collapse, map   to 
 and so forth)
|
|
4380
|
-
(r"\s", "") # remove space to nothing for comparison
|
|
4381
|
-
):
|
|
4382
|
-
name = re.sub(pattern, replacement, name, flags=re.IGNORECASE)
|
|
4383
|
-
return unicodedata.normalize('NFKD', name.strip().lower()).encode('ASCII', 'ignore').decode() # remove diacritics
|