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
arelle/oim/Load.py
ADDED
|
@@ -0,0 +1,2869 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import csv
|
|
7
|
+
import datetime
|
|
8
|
+
import io
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import time
|
|
13
|
+
import traceback
|
|
14
|
+
from collections import defaultdict
|
|
15
|
+
from math import log10
|
|
16
|
+
|
|
17
|
+
import isodate
|
|
18
|
+
import regex as re
|
|
19
|
+
from lxml import etree
|
|
20
|
+
|
|
21
|
+
from arelle import (ModelDocument, ModelXbrl, PackageManager, UrlUtil,
|
|
22
|
+
ValidateXbrlDimensions, XbrlConst, XmlUtil, XmlValidate)
|
|
23
|
+
from arelle.ModelValue import (DATETIME, dateTime, dayTimeDuration, qname,
|
|
24
|
+
yearMonthDuration)
|
|
25
|
+
from arelle.PluginManager import pluginClassMethods
|
|
26
|
+
from arelle.PrototypeInstanceObject import DimValuePrototype
|
|
27
|
+
from arelle.PythonUtil import attrdict, isLegacyAbs, strTruncate
|
|
28
|
+
from arelle.typing import TypeGetText
|
|
29
|
+
from arelle.ValidateDuplicateFacts import (DuplicateTypeArg,
|
|
30
|
+
getDuplicateFactSetsWithType)
|
|
31
|
+
|
|
32
|
+
_: TypeGetText
|
|
33
|
+
|
|
34
|
+
nsOims = ("https://xbrl.org/2021",
|
|
35
|
+
"http://www.xbrl.org/WGWD/YYYY-MM-DD",
|
|
36
|
+
"https://www.xbrl.org/WGWD/YYYY-MM-DD",
|
|
37
|
+
"http://www.xbrl.org/((~status_date_uri~))",
|
|
38
|
+
"https://xbrl.org/((~status_date_uri~))"
|
|
39
|
+
)
|
|
40
|
+
nsOimCes = ("https://xbrl.org/2021/oim-common/error",
|
|
41
|
+
"http://www.xbrl.org/WGWD/YYYY-MM-DD/oim-common/error",
|
|
42
|
+
"http://www.xbrl.org/CR/2020-05-06/oim-common/error",
|
|
43
|
+
"http://www.xbrl.org/((~status_date_uri~))/oim-common/error",
|
|
44
|
+
"https://xbrl.org/((~status_date_uri~))/oim-common/error"
|
|
45
|
+
)
|
|
46
|
+
jsonDocumentTypes = (
|
|
47
|
+
"https://xbrl.org/2021/xbrl-json",
|
|
48
|
+
"http://www.xbrl.org/WGWD/YYYY-MM-DD/xbrl-json",
|
|
49
|
+
"http://www.xbrl.org/YYYY-MM-DD/xbrl-json",
|
|
50
|
+
"https://xbrl.org/((~status_date_uri~))/xbrl-json" # allows loading of XII "template" test cases without CI production
|
|
51
|
+
)
|
|
52
|
+
csvDocumentTypes = (
|
|
53
|
+
"https://xbrl.org/2021/xbrl-csv",
|
|
54
|
+
"http://www.xbrl.org/WGWD/YYYY-MM-DD/xbrl-csv",
|
|
55
|
+
"http://xbrl.org/YYYY/xbrl-csv",
|
|
56
|
+
"https://xbrl.org/((~status_date_uri~))/xbrl-csv" # allows loading of XII "template" test cases without CI production
|
|
57
|
+
)
|
|
58
|
+
csvDocinfoObjects = {"documentType", "namespaces", "taxonomy", "extends", "final", "linkTypes", "linkGroups"}
|
|
59
|
+
csvExtensibleObjects = {"namespaces", "linkTypes", "linkGroups", "features", "final", "tableTemplates", "tables", "dimensions", "parameters"}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
reservedLinkTypesAndGroups = {
|
|
63
|
+
"footnote": "http://www.xbrl.org/2003/arcrole/fact-footnote",
|
|
64
|
+
"explanatoryFact": "http://www.xbrl.org/2009/arcrole/fact-explanatoryFact",
|
|
65
|
+
"_": "http://www.xbrl.org/2003/role/link"
|
|
66
|
+
}
|
|
67
|
+
reservedLinkTypeAndGroupAliases = {
|
|
68
|
+
"http://www.xbrl.org/2003/arcrole/fact-footnote": "footnote",
|
|
69
|
+
"http://www.xbrl.org/2009/arcrole/fact-explanatoryFact": "explanatoryFact",
|
|
70
|
+
"http://www.xbrl.org/2003/role/link": "_"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
XLINKTYPE = "{http://www.w3.org/1999/xlink}type"
|
|
75
|
+
XLINKLABEL = "{http://www.w3.org/1999/xlink}label"
|
|
76
|
+
XLINKARCROLE = "{http://www.w3.org/1999/xlink}arcrole"
|
|
77
|
+
XLINKROLE = "{http://www.w3.org/1999/xlink}role"
|
|
78
|
+
XLINKFROM = "{http://www.w3.org/1999/xlink}from"
|
|
79
|
+
XLINKTO = "{http://www.w3.org/1999/xlink}to"
|
|
80
|
+
XLINKHREF = "{http://www.w3.org/1999/xlink}href"
|
|
81
|
+
XMLLANG = "{http://www.w3.org/XML/1998/namespace}lang"
|
|
82
|
+
|
|
83
|
+
NSReservedAliasURIs = {
|
|
84
|
+
"xbrl": nsOims,
|
|
85
|
+
"xs": (XbrlConst.xsd,),
|
|
86
|
+
"enum2": XbrlConst.enum2s,
|
|
87
|
+
# "oimce": nsOimCes,
|
|
88
|
+
"xbrli": (XbrlConst.xbrli,),
|
|
89
|
+
"xs": (XbrlConst.xsd,),
|
|
90
|
+
"utr": (XbrlConst.utr,),
|
|
91
|
+
"iso4217": (XbrlConst.iso4217,),
|
|
92
|
+
#"xbrle": [ns + "/error" for ns in nsOims],
|
|
93
|
+
#"xbrlxe": [ns + "/xbrl-xml/error" for ns in nsOims]
|
|
94
|
+
}
|
|
95
|
+
JSONNSReservedAliasURIs = {
|
|
96
|
+
# xbrlje no longer reserved, issue #381
|
|
97
|
+
# "xbrlje": [ns + "/xbrl-json/error" for ns in nsOims],
|
|
98
|
+
}
|
|
99
|
+
CSVNSReservedAliasURIs = {
|
|
100
|
+
"xbrlce": [ns + "/xbrl-csv/error" for ns in nsOims],
|
|
101
|
+
}
|
|
102
|
+
JSONNSReservedURIAliases = {} # #381 no longer reserved - dict((ns + "/xbrl-json/error", "xbrlje") for ns in nsOims)
|
|
103
|
+
CSVNSReservedURIAliases = dict((ns + "/xbrl-csv/error", "xbrlce") for ns in nsOims)
|
|
104
|
+
NSReservedAliasURIPrefixes = { # for starts-with checking
|
|
105
|
+
# "dtr-type": "http://www.xbrl.org/dtr/type/",
|
|
106
|
+
}
|
|
107
|
+
NSReservedURIAlias = {}
|
|
108
|
+
|
|
109
|
+
OIMDefaultContextElement = "scenario"
|
|
110
|
+
OIMReservedAliasURIs = {
|
|
111
|
+
# "namespaces": NSReservedAliasURIs, -- generated at load time
|
|
112
|
+
"linkTypes": reservedLinkTypesAndGroups,
|
|
113
|
+
"linkGroups": reservedLinkTypesAndGroups
|
|
114
|
+
}
|
|
115
|
+
OIMReservedURIAlias = {
|
|
116
|
+
#"namespaces": NSReservedURIAlias, -- generated at load time
|
|
117
|
+
"linkTypes": reservedLinkTypeAndGroupAliases,
|
|
118
|
+
"linkGroups": reservedLinkTypeAndGroupAliases
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
EMPTY_DICT = {}
|
|
122
|
+
EMPTY_LIST = []
|
|
123
|
+
|
|
124
|
+
DUPJSONKEY = "!@%duplicateKeys%@!"
|
|
125
|
+
DUPJSONVALUE = "!@%duplicateValues%@!"
|
|
126
|
+
|
|
127
|
+
UTF_7_16_Pattern = re.compile(r"(?P<utf16>(^([\x00][^\x00])+$)|(^([^\x00][\x00])+$))|(?P<utf7>^\s*\+AHs-)")
|
|
128
|
+
UTF_7_16_Bytes_Pattern = re.compile(br"(?P<utf16>(^([\x00][^\x00])+$)|(^([^\x00][\x00])+$))|(?P<utf7>^\s*\+AHs-)")
|
|
129
|
+
EBCDIC_Bytes_Pattern = re.compile(b"^[\x40\x4a-\x4f\x50\x5a-\x5f\x60-\x61\x6a-\x6f\x79-\x7f\x81-\x89\x8f\x91-\x99\xa1-\xa9\xb0\xba-\xbb\xc1-\xc9\xd1-\xd9\xe0\xe2-\xe9\xf0-\xf9\xff\x0a\x0d]+$")
|
|
130
|
+
NEVER_EBCDIC_Bytes_Pattern = re.compile(b"[\x30-\x31\x3e\x41-\x49\x51-\x59\x62-\x69\x70-\x78\x80\x8a-\x8e\x90\x9a-\x9f\xa0\xaa-\xaf\xb1-\xb9\xbc-\xbf\xca-\xcf\xda-\xdf\xe1\xea-\xef\xfa-\xfe]")
|
|
131
|
+
JSONmetadataPattern = re.compile(r"\s*\{.*\"documentInfo\"\s*:.*\}", re.DOTALL)
|
|
132
|
+
NoCanonicalPattern = attrdict(match=lambda s: True)
|
|
133
|
+
CanonicalFloatPattern = re.compile(r"^-?[0-9]\.[0-9]([0-9]*[1-9])?E-?([1-9][0-9]*|0)$|^-?INF$|^NaN$")
|
|
134
|
+
CanonicalIntegerPattern = re.compile(r"^-?([1-9][0-9]*)?[0-9]$")
|
|
135
|
+
CanonicalXmlTypePattern = {
|
|
136
|
+
"boolean": re.compile("^true$|^false$"),
|
|
137
|
+
"date": re.compile(r"-?[0-9]{4}-[0-9]{2}-[0-9]{2}Z?$"),
|
|
138
|
+
"dateTime": re.compile(r"-?[0-9]{4}-[0-9]{2}-[0-9]{2}T([01][0-9]|20|21|22|23):[0-9]{2}:[0-9]{2}(\.[0-9]([0-9]*[1-9])?)?Z?$"),
|
|
139
|
+
"XBRLI_DATEUNION": re.compile(r"-?[0-9]{4}-[0-9]{2}-[0-9]{2}Z?$|-?[0-9]{4}-[0-9]{2}-[0-9]{2}T([01][0-9]|20|21|22|23):[0-9]{2}:[0-9]{2}(\.[0-9]([0-9]*[1-9])?)?Z?$"),
|
|
140
|
+
"time": re.compile(r"-?([01][0-9]|20|21|22|23):[0-9]{2}:[0-9]{2}(\.[0-9]([0-9]*[1-9])?)?Z?$"),
|
|
141
|
+
"decimal": re.compile(r"^[-]?([1-9][0-9]*)?[0-9]\.[0-9]([0-9]*[1-9])?$"),
|
|
142
|
+
"float": CanonicalFloatPattern,
|
|
143
|
+
"double": CanonicalFloatPattern,
|
|
144
|
+
"hexBinary": re.compile(r"^([0-9A-F][0-9A-F])*$"),
|
|
145
|
+
"integer": CanonicalIntegerPattern,
|
|
146
|
+
"language": re.compile(r"[a-z]{1,8}(-[a-z0-9]{1,8})*$"),
|
|
147
|
+
"nonPositiveInteger": CanonicalIntegerPattern,
|
|
148
|
+
"negativeInteger": CanonicalIntegerPattern,
|
|
149
|
+
"long": CanonicalIntegerPattern,
|
|
150
|
+
"int": CanonicalIntegerPattern,
|
|
151
|
+
"short": CanonicalIntegerPattern,
|
|
152
|
+
"byte": CanonicalIntegerPattern,
|
|
153
|
+
"nonNegativeInteger": CanonicalIntegerPattern,
|
|
154
|
+
"unsignedLong": CanonicalIntegerPattern,
|
|
155
|
+
"unsignedInt": CanonicalIntegerPattern,
|
|
156
|
+
"unsignedShort": CanonicalIntegerPattern,
|
|
157
|
+
"unsignedByte": CanonicalIntegerPattern,
|
|
158
|
+
"positiveInteger": CanonicalIntegerPattern,
|
|
159
|
+
}
|
|
160
|
+
IdentifierPattern = re.compile(
|
|
161
|
+
"^[_A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]"
|
|
162
|
+
r"[_\-"
|
|
163
|
+
"\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*$")
|
|
164
|
+
RowIdentifierPattern = re.compile(
|
|
165
|
+
r"[_\-"
|
|
166
|
+
"\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*$")
|
|
167
|
+
PeriodPattern = re.compile(
|
|
168
|
+
r"^-?[0-9]{4}-[0-9]{2}-[0-9]{2}T([01][0-9]|20|21|22|23):[0-9]{2}:[0-9]{2}(\.[0-9]([0-9]*[1-9])?)?Z?"
|
|
169
|
+
r"(/-?[0-9]{4}-[0-9]{2}-[0-9]{2}T([01][0-9]|20|21|22|23):[0-9]{2}:[0-9]{2}(\.[0-9]([0-9]*[1-9])?)?Z?)?$"
|
|
170
|
+
)
|
|
171
|
+
PrefixedQName = re.compile(
|
|
172
|
+
"[_A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]"
|
|
173
|
+
r"[_\-\."
|
|
174
|
+
"\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*:"
|
|
175
|
+
"[_A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]"
|
|
176
|
+
r"[_\-\."
|
|
177
|
+
"\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*")
|
|
178
|
+
SpecialValuePattern = re.compile("##|#empty$|#nil$|#none$")
|
|
179
|
+
SQNamePattern = re.compile(
|
|
180
|
+
"[_A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]"
|
|
181
|
+
r"[_\-\."
|
|
182
|
+
"\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*:"
|
|
183
|
+
r"\S+")
|
|
184
|
+
UnitPrefixedQNameSubstitutionChar = "\x07" # replaces PrefixedQName in unit pattern
|
|
185
|
+
UnitPattern = re.compile(
|
|
186
|
+
# QNames are replaced by \x07 in these expressions
|
|
187
|
+
# numerator only (no parentheses)
|
|
188
|
+
"(^\x07$)|(^\x07([*]\x07)+$)|"
|
|
189
|
+
# numerator and optional denominator, with parentheses if more than one term in either
|
|
190
|
+
"(^((\x07)|([(]\x07([*]\x07)+[)]))([/]((\x07)|([(]\x07([*]\x07)+[)])))?$)"
|
|
191
|
+
)
|
|
192
|
+
UrlInvalidPattern = re.compile(
|
|
193
|
+
r"^[ \t\n\r]+[^ \t\n\r]*|.*[^ \t\n\r][ \t\n\r]+$|" # leading or trailing whitespace
|
|
194
|
+
r".*[^ \t\n\r]([\t\n\r]+|[ \t\n\r]{2,})[^ \t\n\r]|" # embedded uncollapsed whitespace
|
|
195
|
+
r".*%[^0-9a-fA-F]|.*%[0-9a-fA-F][^0-9a-fA-F]|.*#.*#" # invalid %nn or two ##s
|
|
196
|
+
)
|
|
197
|
+
WhitespacePattern = re.compile(r"[ \t\n\r]")
|
|
198
|
+
WhitespaceUntrimmedPattern = re.compile(r"^[ \t\n\r]|.*[ \t\n\r]$")
|
|
199
|
+
|
|
200
|
+
xlUnicodePattern = re.compile("_x([0-9A-F]{4})_")
|
|
201
|
+
|
|
202
|
+
decimalsSuffixPattern = re.compile(r".*[0-9.][\r\n\t ]*d[\r\n\t ]*(0|-?[1-9][0-9]*|INF)[\r\n\t ]*$") # test starting 1 position before the d
|
|
203
|
+
|
|
204
|
+
htmlBodyTemplate = "<body xmlns='http://www.w3.org/1999/xhtml'>\n{0}\n</body>\n"
|
|
205
|
+
xhtmlTagPrefix = "{http://www.w3.org/1999/xhtml}"
|
|
206
|
+
builtInDimensionKeys = frozenset({"concept", "entity", "period", "unit", "language"})
|
|
207
|
+
|
|
208
|
+
UNSUPPORTED_DATA_TYPES = XbrlConst.dtrPrefixedContentItemTypes + (
|
|
209
|
+
qname(XbrlConst.xbrli,"fractionItemType"), )
|
|
210
|
+
|
|
211
|
+
# CSV Files
|
|
212
|
+
CSV_PARAMETER_FILE = 1
|
|
213
|
+
CSV_FACTS_FILE = 2
|
|
214
|
+
CSV_HAS_HEADER_ROW = True
|
|
215
|
+
|
|
216
|
+
# allowed duplicates settings
|
|
217
|
+
NONE = 1
|
|
218
|
+
COMPLETE = 2
|
|
219
|
+
CONSISTENT = 3
|
|
220
|
+
ALL = 4
|
|
221
|
+
AllowedDuplicatesFeatureValues = {"none": NONE, "complete": COMPLETE, "consistent": CONSISTENT, "all": ALL}
|
|
222
|
+
DisallowedDescription = {NONE: "Disallowed", COMPLETE: "Non-complete", CONSISTENT: "Inconsistent", ALL: "Allowed"}
|
|
223
|
+
DuplicateTypeArgMap = {NONE: DuplicateTypeArg.ALL, COMPLETE: DuplicateTypeArg.INCOMPLETE, CONSISTENT: DuplicateTypeArg.INCONSISTENT, ALL: DuplicateTypeArg.NONE}
|
|
224
|
+
|
|
225
|
+
class SQNameType:
|
|
226
|
+
pass # fake class for detecting SQName type in JSON structure check
|
|
227
|
+
|
|
228
|
+
class QNameType:
|
|
229
|
+
pass # fake class for detecting QName type in JSON structure check
|
|
230
|
+
|
|
231
|
+
class LangType:
|
|
232
|
+
pass
|
|
233
|
+
|
|
234
|
+
class URIType:
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
class IdentifierType:
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
class NoRecursionCheck:
|
|
241
|
+
pass
|
|
242
|
+
|
|
243
|
+
class CheckPrefix:
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
class KeyIsNcName:
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
UnrecognizedDocMemberTypes = {
|
|
251
|
+
"/documentInfo": dict,
|
|
252
|
+
"/documentInfo/documentType": str,
|
|
253
|
+
}
|
|
254
|
+
UnrecognizedDocRequiredMembers = {
|
|
255
|
+
"/": {"documentInfo"},
|
|
256
|
+
"/documentInfo/": {"documentType","taxonomy"},
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
JsonMemberTypes = {
|
|
260
|
+
# keys are json pointer with * meaning any id, and *:* meaning any SQName or QName, for array no index is used
|
|
261
|
+
# report
|
|
262
|
+
"/documentInfo": dict,
|
|
263
|
+
"/facts": dict,
|
|
264
|
+
"/*:*": (int,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
265
|
+
# documentInfo
|
|
266
|
+
"/documentInfo/baseURL": URIType,
|
|
267
|
+
"/documentInfo/documentType": str,
|
|
268
|
+
"/documentInfo/features": dict,
|
|
269
|
+
"/documentInfo/features/*:*": (int,float,bool,str,type(None)),
|
|
270
|
+
"/documentInfo/namespaces": dict,
|
|
271
|
+
"/documentInfo/namespaces/*": URIType,
|
|
272
|
+
"/documentInfo/linkTypes": dict,
|
|
273
|
+
"/documentInfo/linkTypes/*": str,
|
|
274
|
+
"/documentInfo/linkGroups": dict,
|
|
275
|
+
"/documentInfo/linkGroups/*": str,
|
|
276
|
+
"/documentInfo/taxonomy": list,
|
|
277
|
+
"/documentInfo/taxonomy/": str,
|
|
278
|
+
"/documentInfo/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
279
|
+
# facts
|
|
280
|
+
"/facts/*": dict,
|
|
281
|
+
"/facts/*/value": (str,type(None)),
|
|
282
|
+
"/facts/*/decimals": int,
|
|
283
|
+
"/facts/*/dimensions": dict,
|
|
284
|
+
"/facts/*/links": dict,
|
|
285
|
+
"/facts/*/links/*": dict,
|
|
286
|
+
"/facts/*/links/*/*": list,
|
|
287
|
+
"/facts/*/links/*/*/": str,
|
|
288
|
+
# dimensions
|
|
289
|
+
"/facts/*/dimensions/concept": QNameType,
|
|
290
|
+
"/facts/*/dimensions/entity": SQNameType,
|
|
291
|
+
"/facts/*/dimensions/period": str,
|
|
292
|
+
"/facts/*/dimensions/unit": str,
|
|
293
|
+
"/facts/*/dimensions/language": LangType,
|
|
294
|
+
"/facts/*/dimensions/noteId": str,
|
|
295
|
+
"/facts/*/dimensions/*:*": (str,type(None)),
|
|
296
|
+
# custom properties on fact are unchecked
|
|
297
|
+
"/facts/*/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
298
|
+
}
|
|
299
|
+
JsonRequiredMembers = {
|
|
300
|
+
"/": {"documentInfo"},
|
|
301
|
+
"/documentInfo/": {"documentType","taxonomy"},
|
|
302
|
+
"/facts/*/": {"value","dimensions"},
|
|
303
|
+
"/facts/*/dimensions/": {"concept"}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
CsvMemberTypes = {
|
|
307
|
+
# report
|
|
308
|
+
"/documentInfo": dict,
|
|
309
|
+
"/tableTemplates": dict,
|
|
310
|
+
"/tables": dict,
|
|
311
|
+
"/parameters": dict,
|
|
312
|
+
"/parameters/*": str,
|
|
313
|
+
"/parameterURL": str,
|
|
314
|
+
"/dimensions": dict,
|
|
315
|
+
"/decimals": (int,str),
|
|
316
|
+
"/links": dict,
|
|
317
|
+
"/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
318
|
+
# documentInfo
|
|
319
|
+
"/documentInfo/baseURL": URIType,
|
|
320
|
+
"/documentInfo/documentType": str,
|
|
321
|
+
"/documentInfo/features": dict,
|
|
322
|
+
"/documentInfo/features/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck),
|
|
323
|
+
"/documentInfo/final": dict,
|
|
324
|
+
"/documentInfo/namespaces": dict,
|
|
325
|
+
"/documentInfo/namespaces/*": URIType,
|
|
326
|
+
"/documentInfo/linkTypes": dict,
|
|
327
|
+
"/documentInfo/linkTypes/*": str,
|
|
328
|
+
"/documentInfo/linkGroups": dict,
|
|
329
|
+
"/documentInfo/linkGroups/*": str,
|
|
330
|
+
"/documentInfo/taxonomy": list,
|
|
331
|
+
"/documentInfo/taxonomy/": str,
|
|
332
|
+
"/documentInfo/extends": list,
|
|
333
|
+
"/documentInfo/extends/": URIType,
|
|
334
|
+
"/documentInfo/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
335
|
+
# documentInfo/final
|
|
336
|
+
"/documentInfo/final/namespaces": bool,
|
|
337
|
+
"/documentInfo/final/taxonomy": bool,
|
|
338
|
+
"/documentInfo/final/linkTypes": bool,
|
|
339
|
+
"/documentInfo/final/linkGroups": bool,
|
|
340
|
+
"/documentInfo/final/features": bool,
|
|
341
|
+
"/documentInfo/final/tableTemplates": bool,
|
|
342
|
+
"/documentInfo/final/tables": bool,
|
|
343
|
+
"/documentInfo/final/dimensions": bool,
|
|
344
|
+
"/documentInfo/final/final": bool,
|
|
345
|
+
"/documentInfo/final/parameters": bool,
|
|
346
|
+
"/documentInfo/final/parameterURL": bool,
|
|
347
|
+
# table templates
|
|
348
|
+
"/tableTemplates/*": dict,
|
|
349
|
+
"/tableTemplates/*/rowIdColumn": str,
|
|
350
|
+
"/tableTemplates/*/columns": dict,
|
|
351
|
+
"/tableTemplates/*/decimals": (int,str),
|
|
352
|
+
"/tableTemplates/*/dimensions": dict,
|
|
353
|
+
"/tableTemplates/*/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
354
|
+
"/tableTemplates/*/dimensions/concept": str,
|
|
355
|
+
"/tableTemplates/*/dimensions/entity": str,
|
|
356
|
+
"/tableTemplates/*/dimensions/period": str,
|
|
357
|
+
"/tableTemplates/*/dimensions/unit": str,
|
|
358
|
+
"/tableTemplates/*/dimensions/language": str,
|
|
359
|
+
"/tableTemplates/*/dimensions/*:*": str,
|
|
360
|
+
"/tableTemplates/*/dimensions/$*": str,
|
|
361
|
+
#"/tableTemplates/*/transposed": bool,
|
|
362
|
+
# columns
|
|
363
|
+
"/tableTemplates/*/columns/*": dict,
|
|
364
|
+
"/tableTemplates/*/columns/*/comment": bool,
|
|
365
|
+
"/tableTemplates/*/columns/*/decimals": (int,str),
|
|
366
|
+
"/tableTemplates/*/columns/*/dimensions": dict,
|
|
367
|
+
"/tableTemplates/*/columns/*/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
368
|
+
# dimensions (column)
|
|
369
|
+
"/tableTemplates/*/columns/*/dimensions/concept": str,
|
|
370
|
+
"/tableTemplates/*/columns/*/dimensions/entity": str,
|
|
371
|
+
"/tableTemplates/*/columns/*/dimensions/period": str,
|
|
372
|
+
"/tableTemplates/*/columns/*/dimensions/unit": str,
|
|
373
|
+
"/tableTemplates/*/columns/*/dimensions/language": str,
|
|
374
|
+
"/tableTemplates/*/columns/*/dimensions/*:*": str,
|
|
375
|
+
"/tableTemplates/*/columns/*/dimensions/$*": str,
|
|
376
|
+
# property groups (column)
|
|
377
|
+
"/tableTemplates/*/columns/*/propertiesFrom": list,
|
|
378
|
+
"/tableTemplates/*/columns/*/propertiesFrom/": str,
|
|
379
|
+
"/tableTemplates/*/columns/*/propertyGroups": dict,
|
|
380
|
+
"/tableTemplates/*/columns/*/propertyGroups/*": dict,
|
|
381
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
382
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/decimals": (int,str),
|
|
383
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions": dict,
|
|
384
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions/concept": str,
|
|
385
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions/entity": str,
|
|
386
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions/period": str,
|
|
387
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions/unit": str,
|
|
388
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions/language": str,
|
|
389
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions/*:*": str,
|
|
390
|
+
"/tableTemplates/*/columns/*/propertyGroups/*/dimensions/$*": str,
|
|
391
|
+
# dimensions (top level)
|
|
392
|
+
"/dimensions/concept": str,
|
|
393
|
+
"/dimensions/entity": str,
|
|
394
|
+
"/dimensions/period": str,
|
|
395
|
+
"/dimensions/unit": str,
|
|
396
|
+
"/dimensions/language": str,
|
|
397
|
+
"/dimensions/noteId": str,
|
|
398
|
+
"/dimensions/*:*": str,
|
|
399
|
+
"/dimensions/$*": str,
|
|
400
|
+
# tables
|
|
401
|
+
"/tables/*": dict,
|
|
402
|
+
"/tables/*/url": str,
|
|
403
|
+
"/tables/*/template": str,
|
|
404
|
+
"/tables/*/optional": bool,
|
|
405
|
+
"/tables/*/parameters": dict,
|
|
406
|
+
"/tables/*/parameters/*": str,
|
|
407
|
+
"/tables/*/*:*": (int,float,bool,str,dict,list,type(None),NoRecursionCheck,CheckPrefix), # custom extensions
|
|
408
|
+
# links
|
|
409
|
+
"/links/*": (dict,KeyIsNcName),
|
|
410
|
+
# link group
|
|
411
|
+
"/links/*/*": (dict,KeyIsNcName),
|
|
412
|
+
# fact links
|
|
413
|
+
"/links/*/*/*": list,
|
|
414
|
+
# fact IDs
|
|
415
|
+
"/links/*/*/*/*": str,
|
|
416
|
+
}
|
|
417
|
+
CsvRequiredMembers = {
|
|
418
|
+
"/": {"documentInfo"},
|
|
419
|
+
"/documentInfo/": {"documentType"},
|
|
420
|
+
"/tableTemplates/*/": {"columns"},
|
|
421
|
+
"/tables/*/": {"url"}
|
|
422
|
+
}
|
|
423
|
+
EMPTY_SET = set()
|
|
424
|
+
|
|
425
|
+
def jsonGet(tbl, key, default=None):
|
|
426
|
+
if isinstance(tbl, dict):
|
|
427
|
+
return tbl.get(key, default)
|
|
428
|
+
return default
|
|
429
|
+
|
|
430
|
+
# singleton special values
|
|
431
|
+
class Singleton(str):
|
|
432
|
+
def __init__(self, value):
|
|
433
|
+
self.value = value
|
|
434
|
+
def __str__(self):
|
|
435
|
+
return self.value
|
|
436
|
+
|
|
437
|
+
EMPTY_CELL = Singleton("")
|
|
438
|
+
NONE_CELL = Singleton("")
|
|
439
|
+
INVALID_REFERENCE_TARGET = Singleton("")
|
|
440
|
+
|
|
441
|
+
def csvCellValue(cellValue):
|
|
442
|
+
# CSV table in Appendix A
|
|
443
|
+
if cellValue == "#nil": # nil value
|
|
444
|
+
return None
|
|
445
|
+
elif cellValue == "": # empty cell
|
|
446
|
+
return EMPTY_CELL
|
|
447
|
+
elif cellValue == "#none":
|
|
448
|
+
return NONE_CELL
|
|
449
|
+
elif cellValue == "#empty": # empty string
|
|
450
|
+
return ""
|
|
451
|
+
elif isinstance(cellValue, str) and cellValue.startswith("#"):
|
|
452
|
+
if cellValue.startswith("##"):
|
|
453
|
+
return cellValue[1:]
|
|
454
|
+
else:
|
|
455
|
+
raise OIMException("xbrlce:unknownSpecialValue",
|
|
456
|
+
_("Unknown special value %(specialValue)s"),
|
|
457
|
+
specialValue=cellValue)
|
|
458
|
+
else:
|
|
459
|
+
return cellValue
|
|
460
|
+
|
|
461
|
+
def xlUnicodeChar(match):
|
|
462
|
+
return chr(int(match.group(1), 16))
|
|
463
|
+
|
|
464
|
+
def xlValue(cell): # excel values may have encoded unicode, such as _0000D_
|
|
465
|
+
v = cell.value
|
|
466
|
+
if isinstance(v, str):
|
|
467
|
+
v = xlUnicodePattern.sub(xlUnicodeChar, v).replace('\r\n','\n').replace('\r','\n')
|
|
468
|
+
elif v is None:
|
|
469
|
+
v = ""
|
|
470
|
+
elif isinstance(v, float):
|
|
471
|
+
return str(round(v, 14)) # Deal with general numbers which may be imprecise
|
|
472
|
+
else:
|
|
473
|
+
v = str(v)
|
|
474
|
+
return csvCellValue(v)
|
|
475
|
+
|
|
476
|
+
def parseMetadataCellValues(metadataTable):
|
|
477
|
+
for dimName in metadataTable.keys():
|
|
478
|
+
dimValue = metadataTable[dimName]
|
|
479
|
+
# CSV table in Appendix A (similar to "cellValue"
|
|
480
|
+
if dimValue is None or dimValue == "#nil":
|
|
481
|
+
metadataTable[dimName] = None
|
|
482
|
+
elif dimValue == "" and dimName != "period": # empty cell except for period
|
|
483
|
+
metadataTable[dimName] = EMPTY_CELL
|
|
484
|
+
elif dimValue == "#none":
|
|
485
|
+
metadataTable[dimName] = NONE_CELL
|
|
486
|
+
elif isinstance(dimValue, str) and dimValue.startswith("##"):
|
|
487
|
+
metadataTable[dimName] = dimValue[1:]
|
|
488
|
+
|
|
489
|
+
def xlTrimHeaderRow(row):
|
|
490
|
+
numEmptyCellsAtEndOfRow = 0
|
|
491
|
+
for i in range(len(row)-1, -1, -1):
|
|
492
|
+
if row[i] in (None, ""):
|
|
493
|
+
numEmptyCellsAtEndOfRow += 1
|
|
494
|
+
else:
|
|
495
|
+
break
|
|
496
|
+
if numEmptyCellsAtEndOfRow:
|
|
497
|
+
return row[:-numEmptyCellsAtEndOfRow]
|
|
498
|
+
return row
|
|
499
|
+
|
|
500
|
+
class OIMException(Exception):
|
|
501
|
+
def __init__(self, code=None, message=None, **kwargs):
|
|
502
|
+
self.code = code
|
|
503
|
+
self.message = message
|
|
504
|
+
self.msgArgs = kwargs
|
|
505
|
+
self.args = ( self.__repr__(), )
|
|
506
|
+
def __repr__(self):
|
|
507
|
+
if self.code and self.message:
|
|
508
|
+
return _('[{0}] exception {1}').format(self.code, self.message % self.msgArgs)
|
|
509
|
+
else:
|
|
510
|
+
return "Errors noted in log"
|
|
511
|
+
|
|
512
|
+
class NotOIMException(Exception):
|
|
513
|
+
def __init__(self, **kwargs):
|
|
514
|
+
self.args = ( self.__repr__(), )
|
|
515
|
+
def __repr__(self):
|
|
516
|
+
return _('[NotOIM] not an OIM document')
|
|
517
|
+
|
|
518
|
+
class FactProduced():
|
|
519
|
+
def clear(self):
|
|
520
|
+
self.modelFact = None
|
|
521
|
+
self.dimensionsUsed = set()
|
|
522
|
+
self.invalidReferenceTarget = None
|
|
523
|
+
|
|
524
|
+
PER_ISO = 0
|
|
525
|
+
PER_INCLUSIVE_DATES = 1
|
|
526
|
+
PER_SINGLE_DAY = 2
|
|
527
|
+
PER_MONTH = 3
|
|
528
|
+
PER_YEAR = 4
|
|
529
|
+
PER_QTR = 5
|
|
530
|
+
PER_HALF = 6
|
|
531
|
+
PER_WEEK = 7
|
|
532
|
+
ONE_DAY = dayTimeDuration("P1D")
|
|
533
|
+
ONE_MONTH = yearMonthDuration("P1M")
|
|
534
|
+
ONE_YEAR = yearMonthDuration("P1Y")
|
|
535
|
+
ONE_QTR = yearMonthDuration("P3M")
|
|
536
|
+
ONE_HALF = yearMonthDuration("P6M")
|
|
537
|
+
|
|
538
|
+
periodForms = ((PER_ISO, re.compile("([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-2][0-9]([:]?)[0-5][0-9]+)?(/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})?(Z|[+-][0-2][0-9]([:]?)[0-5][0-9]+)?)$")),
|
|
539
|
+
(PER_INCLUSIVE_DATES, re.compile("([0-9]{4}-[0-9]{2}-[0-9]{2})[.][.]([0-9]{4}-[0-9]{2}-[0-9]{2})$")),
|
|
540
|
+
(PER_SINGLE_DAY, re.compile("([0-9]{4}-[0-9]{2}-[0-9]{2})(@(start|end))?$")),
|
|
541
|
+
(PER_MONTH, re.compile("([0-9]{4}-[0-9]{2})(@(start|end))?$")),
|
|
542
|
+
(PER_YEAR, re.compile("([0-9]{4})(@(start|end))?$")),
|
|
543
|
+
(PER_QTR, re.compile("([0-9]{4})Q([1-4])(@(start|end))?$")),
|
|
544
|
+
(PER_HALF, re.compile("([0-9]{4})H([1-2])(@(start|end))?$")),
|
|
545
|
+
(PER_WEEK, re.compile("([0-9]{4}W[1-5]?[0-9])(@(start|end))?$")))
|
|
546
|
+
|
|
547
|
+
def csvPeriod(cellValue, startOrEnd=None):
|
|
548
|
+
if cellValue is EMPTY_CELL or cellValue is NONE_CELL:
|
|
549
|
+
return NONE_CELL # Forever period (absent in xBRL-JSON)
|
|
550
|
+
if cellValue is None: # #nil is not valid for date
|
|
551
|
+
return cellValue # stays None
|
|
552
|
+
isoDuration = None
|
|
553
|
+
for perType, perFormMatch in periodForms:
|
|
554
|
+
m = perFormMatch.match(cellValue)
|
|
555
|
+
if m:
|
|
556
|
+
try:
|
|
557
|
+
if perType == PER_ISO:
|
|
558
|
+
if not m.group(4) and startOrEnd: # instant date
|
|
559
|
+
return "referenceTargetNotDuration"
|
|
560
|
+
isoDuration = cellValue
|
|
561
|
+
startendSuffixGroup = 0
|
|
562
|
+
elif perType == PER_INCLUSIVE_DATES:
|
|
563
|
+
isoDuration = "{}/{}".format(dateTime(m.group(1)), dateTime(m.group(2)) + ONE_DAY)
|
|
564
|
+
startendSuffixGroup = 0
|
|
565
|
+
elif perType == PER_SINGLE_DAY:
|
|
566
|
+
isoDuration = "{}/{}".format(dateTime(m.group(1)), dateTime(m.group(1)) + ONE_DAY)
|
|
567
|
+
startendSuffixGroup = 3
|
|
568
|
+
elif perType == PER_MONTH:
|
|
569
|
+
moStart = dateTime(m.group(1) + "-01")
|
|
570
|
+
isoDuration = "{}/{}".format(moStart, moStart + ONE_MONTH)
|
|
571
|
+
startendSuffixGroup = 3
|
|
572
|
+
elif perType == PER_YEAR:
|
|
573
|
+
yrStart = dateTime(m.group(1) + "-01-01")
|
|
574
|
+
isoDuration = "{}/{}".format(yrStart, yrStart + ONE_YEAR)
|
|
575
|
+
startendSuffixGroup = 3
|
|
576
|
+
elif perType == PER_QTR:
|
|
577
|
+
qtrStart = dateTime(m.group(1) + "-{:02}-01".format(int(m.group(2))*3 - 2))
|
|
578
|
+
isoDuration = "{}/{}".format(qtrStart, qtrStart + ONE_QTR)
|
|
579
|
+
startendSuffixGroup = 4
|
|
580
|
+
elif perType == PER_HALF:
|
|
581
|
+
qtrStart = dateTime(m.group(1) + "-{:02}-01".format(int(m.group(2))*6 - 5))
|
|
582
|
+
isoDuration = "{}/{}".format(qtrStart, qtrStart + ONE_HALF)
|
|
583
|
+
startendSuffixGroup = 4
|
|
584
|
+
elif perType == PER_WEEK:
|
|
585
|
+
weekStart = dateTime(isodate.parse_date(m.group(1)))
|
|
586
|
+
isoDuration = "{}T00:00:00/{}T00:00:00".format(weekStart, weekStart + datetime.timedelta(7))
|
|
587
|
+
startendSuffixGroup = 3
|
|
588
|
+
if startendSuffixGroup and m.group(startendSuffixGroup):
|
|
589
|
+
if startOrEnd:
|
|
590
|
+
# period specifier is being applied to an instant date
|
|
591
|
+
return "referenceTargetNotDuration"
|
|
592
|
+
startOrEnd = m.group(startendSuffixGroup)
|
|
593
|
+
except ValueError:
|
|
594
|
+
return None
|
|
595
|
+
if isoDuration:
|
|
596
|
+
if startOrEnd == "start":
|
|
597
|
+
return isoDuration.partition("/")[0]
|
|
598
|
+
elif startOrEnd == "end":
|
|
599
|
+
return isoDuration.partition("/")[2]
|
|
600
|
+
return isoDuration
|
|
601
|
+
return None
|
|
602
|
+
|
|
603
|
+
def increaseMaxFieldSize():
|
|
604
|
+
# https://stackoverflow.com/a/15063941
|
|
605
|
+
maxInt = sys.maxsize
|
|
606
|
+
|
|
607
|
+
while True:
|
|
608
|
+
# decrease the maxInt value by factor 10
|
|
609
|
+
# as long as the OverflowError occurs.
|
|
610
|
+
try:
|
|
611
|
+
csv.field_size_limit(maxInt)
|
|
612
|
+
break
|
|
613
|
+
except OverflowError:
|
|
614
|
+
maxInt = int(maxInt/10)
|
|
615
|
+
|
|
616
|
+
def idDeduped(modelXbrl, id):
|
|
617
|
+
for i in range(99999):
|
|
618
|
+
if i == 0:
|
|
619
|
+
candidateId = id
|
|
620
|
+
else:
|
|
621
|
+
candidateId = "{}.{}".format(id, i)
|
|
622
|
+
if candidateId not in modelXbrl.modelDocument.idObjects:
|
|
623
|
+
return candidateId
|
|
624
|
+
return None
|
|
625
|
+
|
|
626
|
+
def checkForDuplicates(modelXbrl, allowedDups, footnoteIDs):
|
|
627
|
+
duplicateTypeArg = DuplicateTypeArgMap[allowedDups]
|
|
628
|
+
for duplicateFactSet in getDuplicateFactSetsWithType(modelXbrl.facts, duplicateTypeArg.duplicateType()):
|
|
629
|
+
fList = duplicateFactSet.facts
|
|
630
|
+
f0 = fList[0]
|
|
631
|
+
modelXbrl.error("oime:disallowedDuplicateFacts",
|
|
632
|
+
"%(disallowance)s duplicate fact values %(element)s: %(values)s, %(contextIDs)s.",
|
|
633
|
+
modelObject=fList, disallowance=DisallowedDescription[allowedDups], element=f0.qname,
|
|
634
|
+
contextIDs=", ".join(sorted(set(f.contextID for f in fList))),
|
|
635
|
+
values=", ".join(strTruncate(f.value,64) for f in fList))
|
|
636
|
+
|
|
637
|
+
def getTaxonomyContextElement(modelXbrl: ModelXbrl.ModelXbrl) -> str:
|
|
638
|
+
# https://www.xbrl.org/Specification/xbrl-xml/REC-2021-10-13/xbrl-xml-REC-2021-10-13.html#sec-dimensions
|
|
639
|
+
# The spec states that if in the DTS:
|
|
640
|
+
# 1. neither segment nor scenario is present, scenario is used.
|
|
641
|
+
# 2. segment is present and scenario is not, segment is used.
|
|
642
|
+
# 3. scenario is present and segment is not, scenario is used.
|
|
643
|
+
# 4. segment and scenario are present and facts are valid against both of them, scenario is used.
|
|
644
|
+
# 5. segment and scenario are present and facts are only valid against scenario, scenario is used.
|
|
645
|
+
# 6. segment and scenario are present and facts are only valid against segment, segment is used.
|
|
646
|
+
# 7. segment and scenario are present and facts are invalid against both, the choice is made arbitrarily.
|
|
647
|
+
# We don't yet inspect dimensional validity and therefore incorrectly use scenario in case 6.
|
|
648
|
+
taxonomyContextRefTypes = {
|
|
649
|
+
modelRelationship.contextElement
|
|
650
|
+
for hasHypercubeRelationship in (XbrlConst.all, XbrlConst.notAll)
|
|
651
|
+
for modelRelationship in modelXbrl.relationshipSet(hasHypercubeRelationship).modelRelationships
|
|
652
|
+
}
|
|
653
|
+
return taxonomyContextRefTypes.pop() if len(taxonomyContextRefTypes) == 1 else OIMDefaultContextElement
|
|
654
|
+
|
|
655
|
+
def _loadFromOIM(cntlr, error, warning, modelXbrl, oimFile, mappedUri):
|
|
656
|
+
from openpyxl import load_workbook
|
|
657
|
+
from openpyxl.cell import Cell
|
|
658
|
+
|
|
659
|
+
_return = None # modelDocument or an exception
|
|
660
|
+
|
|
661
|
+
try:
|
|
662
|
+
currentAction = "initializing"
|
|
663
|
+
startingErrorCount = len(modelXbrl.errors) if modelXbrl else 0
|
|
664
|
+
startedAt = time.time()
|
|
665
|
+
|
|
666
|
+
currentAction = "determining file type"
|
|
667
|
+
isJSON = False
|
|
668
|
+
# isCSV means metadata loaded from separate JSON file (but instance data can be in excel or CSV)
|
|
669
|
+
isCSV = False # oimFile.endswith(".csv") # this option is not currently supported
|
|
670
|
+
instanceFileName = os.path.splitext(oimFile)[0] + ".xbrl"
|
|
671
|
+
|
|
672
|
+
currentAction = "loading and parsing OIM file"
|
|
673
|
+
loadDictErrors = []
|
|
674
|
+
def openCsvReader(csvFilePath, fileType):
|
|
675
|
+
_file = modelXbrl.fileSource.file(csvFilePath, binary=True)[0]
|
|
676
|
+
bytes = _file.read(16) # test encoding
|
|
677
|
+
try:
|
|
678
|
+
m = EBCDIC_Bytes_Pattern.match(bytes)
|
|
679
|
+
if m and not NEVER_EBCDIC_Bytes_Pattern.findall(bytes):
|
|
680
|
+
raise OIMException("xbrlce:invalidCSVFileFormat",
|
|
681
|
+
_("CSV file MUST use utf-8 encoding: %(file)s, appears to be EBCDIC"),
|
|
682
|
+
file=csvFilePath)
|
|
683
|
+
m = UTF_7_16_Bytes_Pattern.match(bytes)
|
|
684
|
+
if m:
|
|
685
|
+
raise OIMException("xbrlce:invalidCSVFileFormat",
|
|
686
|
+
_("CSV file MUST use utf-8 encoding: %(file)s, appears to be %(encoding)s"),
|
|
687
|
+
file=csvFilePath, encoding=m.lastgroup)
|
|
688
|
+
_file.close()
|
|
689
|
+
except UnicodeDecodeError as ex:
|
|
690
|
+
raise OIMException("xbrlce:invalidCSVFileFormat",
|
|
691
|
+
_("CSV file MUST use utf-8 encoding: %(file)s, appears to be %(encoding)s"),
|
|
692
|
+
file=csvFilePath, encoding=m.lastgroup)
|
|
693
|
+
_file = modelXbrl.fileSource.file(csvFilePath, encoding='utf-8-sig')[0]
|
|
694
|
+
if CSV_HAS_HEADER_ROW:
|
|
695
|
+
try:
|
|
696
|
+
chars = _file.read(1024)
|
|
697
|
+
_dialect = csv.Sniffer().sniff(chars, delimiters=[',', '\t', ';', '|']) # also check for disallowed potential separators
|
|
698
|
+
if _dialect.lineterminator not in ("\r", "\n", "\r\n"):
|
|
699
|
+
raise OIMException("xbrlce:invalidCSVFileFormat",
|
|
700
|
+
_("CSV line ending is not CR, LF or CR LF, file %(file)s"),
|
|
701
|
+
file=csvFilePath)
|
|
702
|
+
if _dialect.delimiter not in (","):
|
|
703
|
+
raise OIMException({CSV_PARAMETER_FILE: "xbrlce:invalidParameterCSVFile",
|
|
704
|
+
CSV_FACTS_FILE: "xbrlce:invalidHeaderValue"}[fileType],
|
|
705
|
+
_("CSV deliminator %(deliminator)s is not comma: file %(file)s"),
|
|
706
|
+
file=csvFilePath, deliminator=repr(_dialect.delimiter))
|
|
707
|
+
except csv.Error as ex:
|
|
708
|
+
# possibly can't br sniffed because there's only one column in the rows
|
|
709
|
+
_dialect = None
|
|
710
|
+
for char in chars:
|
|
711
|
+
if char in (",", "\n", "\r"):
|
|
712
|
+
_dialect = "excel"
|
|
713
|
+
break
|
|
714
|
+
elif char == "\t":
|
|
715
|
+
_dialect = "excel-tab"
|
|
716
|
+
break
|
|
717
|
+
if not _dialect:
|
|
718
|
+
raise OIMException("xbrlce:invalidCSVFileFormat",
|
|
719
|
+
_("CSV file %(file)s: %(error)s"),
|
|
720
|
+
file=csvFilePath, error=str(ex))
|
|
721
|
+
except UnicodeDecodeError as ex:
|
|
722
|
+
raise OIMException("xbrlce:invalidCSVFileFormat",
|
|
723
|
+
_("CSV file must use utf-8 encoding %(file)s: %(error)s"),
|
|
724
|
+
file=csvFilePath, error=str(ex))
|
|
725
|
+
_file.seek(0)
|
|
726
|
+
else:
|
|
727
|
+
# check for comma or tab in first line
|
|
728
|
+
_dialect = "excel" # fallback if no first line tab is determinable
|
|
729
|
+
for char in _file.read(1024):
|
|
730
|
+
if char in (",", "\n", "\r", ";", "|"): # ;, | force invalid parameter file detection
|
|
731
|
+
_dialect = "excel"
|
|
732
|
+
break
|
|
733
|
+
elif char == "\t": # only way to sniff first row deliminator if value contains SQName semicolon
|
|
734
|
+
_dialect = "excel-tab"
|
|
735
|
+
break
|
|
736
|
+
_file.seek(0)
|
|
737
|
+
|
|
738
|
+
# Must increase the max supported CSV field size before opening the CSV reader.
|
|
739
|
+
# Otherwise large HTML values will trigger csv.ERROR: field larger than field limit.
|
|
740
|
+
increaseMaxFieldSize()
|
|
741
|
+
return csv.reader(_file, _dialect, doublequote=True)
|
|
742
|
+
|
|
743
|
+
def ldError(msgCode, msgText, **kwargs):
|
|
744
|
+
loadDictErrors.append((msgCode, msgText, kwargs))
|
|
745
|
+
def loadDict(keyValuePairs):
|
|
746
|
+
_dict = {}
|
|
747
|
+
_valueKeyDict = {}
|
|
748
|
+
for key, value in keyValuePairs:
|
|
749
|
+
if isinstance(value, dict):
|
|
750
|
+
if key in ("namespaces", "linkTypes", "linkGroups"):
|
|
751
|
+
normalizedDict = {}
|
|
752
|
+
normalizedValueKeyDict = {}
|
|
753
|
+
if DUPJSONKEY in value:
|
|
754
|
+
normalizedDict[DUPJSONKEY] = value[DUPJSONKEY]
|
|
755
|
+
if DUPJSONVALUE in value:
|
|
756
|
+
normalizedDict[DUPJSONVALUE] = value[DUPJSONVALUE]
|
|
757
|
+
for _key, _value in value.items():
|
|
758
|
+
if not isinstance(_value, str):
|
|
759
|
+
continue # skip dup key/value entries
|
|
760
|
+
# _key = _key.strip() # per !178 keys have only normalized values, don't normalize key
|
|
761
|
+
# _value = _value.strip()
|
|
762
|
+
if _key in normalizedDict: # don't put the duplicate in the dictionary but report it as error
|
|
763
|
+
if DUPJSONKEY not in normalizedDict:
|
|
764
|
+
normalizedDict[DUPJSONKEY] = []
|
|
765
|
+
normalizedDict[DUPJSONKEY].append((_key, _value, normalizedDict[_key]))
|
|
766
|
+
else: # do put into dictionary, only report if it's a map object
|
|
767
|
+
normalizedDict[_key] = _value
|
|
768
|
+
if _value in normalizedValueKeyDict:
|
|
769
|
+
if DUPJSONVALUE not in normalizedDict:
|
|
770
|
+
normalizedDict[DUPJSONVALUE] = []
|
|
771
|
+
normalizedDict[DUPJSONVALUE].append((_value, _key, normalizedValueKeyDict[_value]))
|
|
772
|
+
else:
|
|
773
|
+
normalizedValueKeyDict[_value] = _key
|
|
774
|
+
if not XmlValidate.NCNamePattern.match(_key):
|
|
775
|
+
ldError("{}:invalidJSONStructure",
|
|
776
|
+
_("The %(map)s alias \"%(alias)s\" must be a canonical NCName value"),
|
|
777
|
+
modelObject=modelXbrl, map=key, alias=_key)
|
|
778
|
+
if UrlInvalidPattern.match(_value):
|
|
779
|
+
ldError("{}:invalidJSONStructure",
|
|
780
|
+
_("The %(map)s alias \"%(alias)s\" URI must be a canonical URI value: \"%(URI)s\"."),
|
|
781
|
+
modelObject=modelXbrl, map=key, alias=_key, URI=_value)
|
|
782
|
+
elif not (_value and UrlUtil.isAbsolute(_value)) or UrlInvalidPattern.match(_value):
|
|
783
|
+
ldError("oimce:invalidURI",
|
|
784
|
+
_("The %(map)s \"%(alias)s\" URI is invalid: \"%(URI)s\"."),
|
|
785
|
+
modelObject=modelXbrl, map=key, alias=_key, URI=_value)
|
|
786
|
+
value.clear() # replace with normalized values
|
|
787
|
+
for _key, _value in normalizedDict.items():
|
|
788
|
+
value[_key] = _value
|
|
789
|
+
if DUPJSONKEY in value:
|
|
790
|
+
for _errKey, _errValue, _otherValue in value[DUPJSONKEY]:
|
|
791
|
+
if key in ("namespaces", "linkTypes", "linkGroups"):
|
|
792
|
+
ldError("{}:invalidJSON", # {} expanded when loadDictErrors are processed
|
|
793
|
+
_("The %(map)s alias \"%(prefix)s\" is used on uri \"%(uri1)s\" and uri \"\"%(uri2)s."),
|
|
794
|
+
modelObject=modelXbrl, map=key, prefix=_errKey, uri1=_errValue, uri2=_otherValue)
|
|
795
|
+
else:
|
|
796
|
+
ldError("{}:invalidJSON", # {} expanded when loadDictErrors are processed
|
|
797
|
+
_("The %(obj)s key \"%(key)s\" is used on multiple objects."),
|
|
798
|
+
modelObject=modelXbrl, obj=key, key=_errKey)
|
|
799
|
+
del value[DUPJSONKEY]
|
|
800
|
+
if DUPJSONVALUE in value:
|
|
801
|
+
if key in ("namespaces", "linkTypes", "linkGroups"):
|
|
802
|
+
for _errValue, _errKey, _otherKey in value[DUPJSONVALUE]:
|
|
803
|
+
ldError("oimce:multipleAliasesForURI",
|
|
804
|
+
_("The \"%(map)s\" value \"%(uri)s\" is used on alias \"%(alias1)s\" and alias \"%(alias2)s\"."),
|
|
805
|
+
modelObject=modelXbrl, map=key, uri=_errValue, alias1=_errKey, alias2=_otherKey)
|
|
806
|
+
del value[DUPJSONVALUE]
|
|
807
|
+
if key in _dict: # don't put the duplicate in the dictionary but report it as error
|
|
808
|
+
if DUPJSONKEY not in _dict:
|
|
809
|
+
_dict[DUPJSONKEY] = []
|
|
810
|
+
_dict[DUPJSONKEY].append((key, value, _dict[key]))
|
|
811
|
+
else: # do put into dictionary, only report if it's a map object
|
|
812
|
+
_dict[key] = value
|
|
813
|
+
if isinstance(value, str):
|
|
814
|
+
if value in _valueKeyDict:
|
|
815
|
+
if DUPJSONVALUE not in _dict:
|
|
816
|
+
_dict[DUPJSONVALUE] = []
|
|
817
|
+
_dict[DUPJSONVALUE].append((value, key, _valueKeyDict[value]))
|
|
818
|
+
else:
|
|
819
|
+
_valueKeyDict[value] = key
|
|
820
|
+
return _dict
|
|
821
|
+
|
|
822
|
+
primaryOimFile = oimFile
|
|
823
|
+
extensionProperties = {} # key is property QName, value is property path
|
|
824
|
+
|
|
825
|
+
def loadOimObject(oimFile, extendingFile, visitedFiles, extensionChain, primaryReportParameters=None): # returns oimObject, oimWb
|
|
826
|
+
# isXL means metadata loaded from Excel (but instance data can be in excel or CSV)
|
|
827
|
+
isXL = oimFile.endswith(".xlsx") or oimFile.endswith(".xls")
|
|
828
|
+
# same logic as modelDocument.load
|
|
829
|
+
normalizedUrl = modelXbrl.modelManager.cntlr.webCache.normalizeUrl(oimFile, extendingFile)
|
|
830
|
+
if modelXbrl.fileSource.isMappedUrl(normalizedUrl):
|
|
831
|
+
mappedUrl = modelXbrl.fileSource.mappedUrl(normalizedUrl)
|
|
832
|
+
elif PackageManager.isMappedUrl(normalizedUrl):
|
|
833
|
+
mappedUrl = PackageManager.mappedUrl(normalizedUrl)
|
|
834
|
+
else:
|
|
835
|
+
mappedUrl = modelXbrl.modelManager.disclosureSystem.mappedUrl(normalizedUrl)
|
|
836
|
+
if modelXbrl.fileSource.isInArchive(mappedUrl):
|
|
837
|
+
filepath = mappedUrl
|
|
838
|
+
else:
|
|
839
|
+
filepath = modelXbrl.modelManager.cntlr.webCache.getfilename(mappedUrl) # , reload=reloadCache, checkModifiedTime=kwargs.get("checkModifiedTime",False))
|
|
840
|
+
if filepath:
|
|
841
|
+
url = modelXbrl.modelManager.cntlr.webCache.normalizeUrl(filepath)
|
|
842
|
+
if filepath is None:
|
|
843
|
+
if extendingFile is None:
|
|
844
|
+
raise OIMException(
|
|
845
|
+
"oime:unresolvableFile",
|
|
846
|
+
_("Unable to resolve file %(oimFile)s. A taxonomy package may be required to load this report."),
|
|
847
|
+
oimFile=oimFile,
|
|
848
|
+
)
|
|
849
|
+
else:
|
|
850
|
+
raise OIMException(
|
|
851
|
+
"xbrlce:unresolvableBaseMetadataFile",
|
|
852
|
+
_("Unable to resolve extended metadata file %(extendingFile)s, referenced from %(oimFile)s. A taxonomy package may be required to load this report."),
|
|
853
|
+
extendingFile=extendingFile,
|
|
854
|
+
oimFile=oimFile,
|
|
855
|
+
)
|
|
856
|
+
if filepath.endswith(".csv") or ("metadata" in filepath and filepath.endswith(".json")):
|
|
857
|
+
errPrefix = "xbrlce"
|
|
858
|
+
else:
|
|
859
|
+
errPrefix = "xbrlje"
|
|
860
|
+
# prevent recursion
|
|
861
|
+
if filepath in extensionChain:
|
|
862
|
+
raise OIMException("{}:cycleInExtensionChain".format(errPrefix),
|
|
863
|
+
_("File MUST NOT extend itself: %(file)s cycles to %(extendingFile)s"),
|
|
864
|
+
file=filepath, extendingFile=extendingFile)
|
|
865
|
+
elif filepath in visitedFiles:
|
|
866
|
+
return None
|
|
867
|
+
visitedFiles.add(filepath)
|
|
868
|
+
extensionChain.add(filepath)
|
|
869
|
+
if not isXL:
|
|
870
|
+
try:
|
|
871
|
+
_file = modelXbrl.fileSource.file(filepath, encoding="utf-8-sig")[0]
|
|
872
|
+
with _file as f:
|
|
873
|
+
chars = f.read(16) # test encoding
|
|
874
|
+
m = UTF_7_16_Pattern.match(chars)
|
|
875
|
+
if m:
|
|
876
|
+
raise OIMException("{}:invalidJSON".format(errPrefix),
|
|
877
|
+
_("File MUST use utf-8 encoding: %(file)s, appears to be %(encoding)s"),
|
|
878
|
+
file=filepath, encoding=m.lastgroup)
|
|
879
|
+
else:
|
|
880
|
+
f.seek(0)
|
|
881
|
+
oimObject = json.load(f, object_pairs_hook=loadDict)
|
|
882
|
+
except UnicodeDecodeError as ex:
|
|
883
|
+
raise OIMException("{}:invalidJSON".format(errPrefix),
|
|
884
|
+
_("File MUST use utf-8 encoding: %(file)s, error %(error)s"),
|
|
885
|
+
file=filepath, error=str(ex))
|
|
886
|
+
except json.JSONDecodeError as ex:
|
|
887
|
+
raise OIMException("{}:invalidJSON".format(errPrefix),
|
|
888
|
+
"JSON error while %(action)s, %(file)s, error %(error)s",
|
|
889
|
+
file=filepath, action=currentAction, error=ex)
|
|
890
|
+
# check for top-level key duplicates
|
|
891
|
+
if isinstance(oimObject, dict) and DUPJSONKEY in oimObject:
|
|
892
|
+
for _errKey, _errValue, _otherValue in oimObject[DUPJSONKEY]:
|
|
893
|
+
error("{}:invalidJSON".format(errPrefix),
|
|
894
|
+
_("The key %(key)s is used on multiple objects"),
|
|
895
|
+
modelObject=modelXbrl, key=_errKey)
|
|
896
|
+
del oimObject[DUPJSONKEY]
|
|
897
|
+
oimWb = None
|
|
898
|
+
elif isXL:
|
|
899
|
+
_file = modelXbrl.fileSource.file(filepath, binary=True)[0]
|
|
900
|
+
with _file as f:
|
|
901
|
+
oimWb = load_workbook(f, data_only=True)
|
|
902
|
+
if "metadata" not in oimWb:
|
|
903
|
+
raise OIMException("xbrlwe:missingWorkbookWorksheets",
|
|
904
|
+
_("Unable to identify worksheet tabs for metadata"))
|
|
905
|
+
_foundMatch = False
|
|
906
|
+
for row in range(1,10): # allow metadata to be indented or surrounded by column and row title columns
|
|
907
|
+
for col in range(1,10):
|
|
908
|
+
_metadata = xlValue(oimWb["metadata"].cell(row=row,column=col))
|
|
909
|
+
if _metadata and JSONmetadataPattern.match(_metadata): # find JSON metadata cell
|
|
910
|
+
_foundMatch = True
|
|
911
|
+
break
|
|
912
|
+
if _foundMatch:
|
|
913
|
+
break
|
|
914
|
+
try:
|
|
915
|
+
oimObject = json.loads(_metadata, object_pairs_hook=loadDict)
|
|
916
|
+
except UnicodeDecodeError as ex:
|
|
917
|
+
raise OIMException("{}:invalidJSON".format(errPrefix),
|
|
918
|
+
_("File MUST use utf-8 encoding: %(file)s \"metadata\" worksheet, error %(error)s"),
|
|
919
|
+
file=filepath, error=str(ex))
|
|
920
|
+
except json.JSONDecodeError as ex:
|
|
921
|
+
raise OIMException("{}:invalidJSON".format(errPrefix),
|
|
922
|
+
"JSON error while %(action)s, %(file)s \"metadata\" worksheet, error %(error)s",
|
|
923
|
+
file=filepath, action=currentAction, error=ex)
|
|
924
|
+
# allow report setup or extension objects processing
|
|
925
|
+
for pluginXbrlMethod in pluginClassMethods("LoadFromOim.DocumentSetup"):
|
|
926
|
+
pluginXbrlMethod(modelXbrl, oimObject, oimFile)
|
|
927
|
+
# identify document type (JSON or CSV)
|
|
928
|
+
documentInfo = jsonGet(oimObject, "documentInfo", {})
|
|
929
|
+
documentType = jsonGet(documentInfo, "documentType")
|
|
930
|
+
documentBase = jsonGet(documentInfo, "baseURL")
|
|
931
|
+
if documentType in jsonDocumentTypes:
|
|
932
|
+
isCSV = False
|
|
933
|
+
isJSON = True
|
|
934
|
+
errPrefix = "xbrlje"
|
|
935
|
+
oimMemberTypes = JsonMemberTypes
|
|
936
|
+
oimRequiredMembers = JsonRequiredMembers
|
|
937
|
+
elif documentType in csvDocumentTypes:
|
|
938
|
+
isJSON = False
|
|
939
|
+
isCSV = not isXL
|
|
940
|
+
errPrefix = "xbrlce"
|
|
941
|
+
oimMemberTypes = CsvMemberTypes
|
|
942
|
+
oimRequiredMembers = CsvRequiredMembers
|
|
943
|
+
else: # if wrong type defer to type checking
|
|
944
|
+
isCSV = False
|
|
945
|
+
isJSON = False
|
|
946
|
+
#errPrefix was set earlier based on file name
|
|
947
|
+
oimMemberTypes = UnrecognizedDocMemberTypes
|
|
948
|
+
oimRequiredMembers = UnrecognizedDocRequiredMembers
|
|
949
|
+
isCSVorXL = isCSV or isXL
|
|
950
|
+
|
|
951
|
+
# report loadDict errors
|
|
952
|
+
for msgCode, msgText, kwargs in loadDictErrors:
|
|
953
|
+
error(msgCode.format(errPrefix), msgText, href=filepath, **kwargs)
|
|
954
|
+
del loadDictErrors[:]
|
|
955
|
+
|
|
956
|
+
invalidMemberTypes = []
|
|
957
|
+
invalidSQNames = []
|
|
958
|
+
missingRequiredMembers = []
|
|
959
|
+
unexpectedMembers = []
|
|
960
|
+
def showPathObj(parts, obj): # this can be replaced with jsonPath syntax if appropriate
|
|
961
|
+
try:
|
|
962
|
+
shortObjStr = json.dumps(obj)
|
|
963
|
+
except TypeError:
|
|
964
|
+
shortObjStr = str(obj)
|
|
965
|
+
if len(shortObjStr) > 34:
|
|
966
|
+
shortObjStr = "{:.32}...".format(shortObjStr)
|
|
967
|
+
return "/{}={}".format("/".join(str(p) for p in parts), shortObjStr)
|
|
968
|
+
def checkMemberTypes(obj, path, pathParts):
|
|
969
|
+
if (isinstance(obj,dict)):
|
|
970
|
+
for missingMbr in oimRequiredMembers.get(path,EMPTY_SET) - obj.keys():
|
|
971
|
+
missingRequiredMembers.append(path + missingMbr)
|
|
972
|
+
for mbrName, mbrObj in obj.items():
|
|
973
|
+
mbrPath = path + mbrName
|
|
974
|
+
pathParts.append(mbrName)
|
|
975
|
+
# print("mbrName {} mbrObj {}".format(mbrName, mbrObj))
|
|
976
|
+
if mbrPath in oimMemberTypes:
|
|
977
|
+
mbrTypes = oimMemberTypes[mbrPath]
|
|
978
|
+
if (mbrTypes is SQNameType or (isinstance(mbrTypes,tuple) and SQNameType in mbrTypes)):
|
|
979
|
+
if not isinstance(mbrObj, str) or not SQNamePattern.match(mbrObj):
|
|
980
|
+
invalidSQNames.append(showPathObj(pathParts, mbrObj))
|
|
981
|
+
elif (not ((mbrTypes is QNameType or (isinstance(mbrTypes,tuple) and QNameType in mbrTypes)) and isinstance(mbrObj, str) and XmlValidate.QNamePattern.match(mbrObj)) and
|
|
982
|
+
not ((mbrTypes is LangType or (isinstance(mbrTypes,tuple) and LangType in mbrTypes)) and isinstance(mbrObj, str) and XmlValidate.languagePattern.match(mbrObj)) and
|
|
983
|
+
not ((mbrTypes is URIType or (isinstance(mbrTypes,tuple) and URIType in mbrTypes)) and isinstance(mbrObj, str) and UrlUtil.isValidUriReference(mbrObj) and not WhitespaceUntrimmedPattern.match(mbrObj)) and
|
|
984
|
+
#not (mbrTypes is IdentifierType and isinstance(mbrObj, str) and isinstance(mbrObj, str) and IdentifierPattern.match(mbrObj)) and
|
|
985
|
+
not ((mbrTypes is int or (isinstance(mbrTypes,tuple) and int in mbrTypes)) and isinstance(mbrObj, str) and CanonicalIntegerPattern.match(mbrObj)) and
|
|
986
|
+
not isinstance(mbrObj, mbrTypes)):
|
|
987
|
+
invalidMemberTypes.append(showPathObj(pathParts, mbrObj))
|
|
988
|
+
elif ":" in mbrName and path + "*:*" in oimMemberTypes:
|
|
989
|
+
_mbrTypes = oimMemberTypes[path + "*:*"]
|
|
990
|
+
if not (XmlValidate.QNamePattern.match(mbrName) and isinstance(mbrObj, _mbrTypes)):
|
|
991
|
+
invalidMemberTypes.append(showPathObj(pathParts, mbrObj))
|
|
992
|
+
elif isinstance(_mbrTypes,tuple):
|
|
993
|
+
if CheckPrefix in _mbrTypes:
|
|
994
|
+
extensionProperties[mbrName] = showPathObj(pathParts, mbrObj)
|
|
995
|
+
if NoRecursionCheck in _mbrTypes:
|
|
996
|
+
continue # custom types, block recursive check
|
|
997
|
+
mbrPath = path + "*:*" # for recursion
|
|
998
|
+
elif path + "*" in oimMemberTypes:
|
|
999
|
+
mbrTypes = oimMemberTypes[path + "*"]
|
|
1000
|
+
if (not ((mbrTypes is URIType or (isinstance(mbrTypes,tuple) and isinstance(mbrObj, str) and URIType in mbrTypes)) and UrlUtil.isValidUriReference(mbrObj)) and
|
|
1001
|
+
not isinstance(mbrObj, mbrTypes)):
|
|
1002
|
+
invalidMemberTypes.append(showPathObj(pathParts, mbrObj))
|
|
1003
|
+
if isinstance(mbrTypes,tuple) and KeyIsNcName in mbrTypes and not XmlValidate.NCNamePattern.match(mbrName):
|
|
1004
|
+
invalidMemberTypes.append(showPathObj(pathParts, mbrObj))
|
|
1005
|
+
mbrPath = path + "*" # for recursion
|
|
1006
|
+
else:
|
|
1007
|
+
unexpectedMembers.append(showPathObj(pathParts, mbrObj))
|
|
1008
|
+
if isinstance(mbrObj, (dict,list)):
|
|
1009
|
+
checkMemberTypes(mbrObj, mbrPath + "/", pathParts)
|
|
1010
|
+
pathParts.pop() # remove mbrName
|
|
1011
|
+
if (isinstance(obj,list)):
|
|
1012
|
+
mbrNdx = 1
|
|
1013
|
+
for mbrObj in obj:
|
|
1014
|
+
mbrPath = path # list entry just uses path ending in /
|
|
1015
|
+
pathParts.append(mbrNdx)
|
|
1016
|
+
if mbrPath in oimMemberTypes:
|
|
1017
|
+
mbrTypes = oimMemberTypes[mbrPath]
|
|
1018
|
+
if (not (mbrTypes is IdentifierType and isinstance(mbrObj, str) and isinstance(mbrObj, str) and IdentifierPattern.match(mbrObj)) and
|
|
1019
|
+
not ((mbrTypes is URIType or (isinstance(mbrTypes,tuple) and URIType in mbrTypes)) and isinstance(mbrObj, str) and UrlUtil.isValidUriReference(mbrObj) and not WhitespaceUntrimmedPattern.match(mbrObj)) and
|
|
1020
|
+
not isinstance(mbrObj, mbrTypes)):
|
|
1021
|
+
invalidMemberTypes.append(showPathObj(pathParts, mbrObj))
|
|
1022
|
+
if isinstance(mbrObj, (dict,list)):
|
|
1023
|
+
checkMemberTypes(mbrObj, mbrPath + "/", pathParts)
|
|
1024
|
+
pathParts.pop() # remove mbrNdx
|
|
1025
|
+
mbrNdx += 1
|
|
1026
|
+
checkMemberTypes(oimObject, "/", [])
|
|
1027
|
+
numErrorsBeforeJsonCheck = len(modelXbrl.errors)
|
|
1028
|
+
if not isJSON and not isCSV and not isXL:
|
|
1029
|
+
error("oimce:unsupportedDocumentType",
|
|
1030
|
+
_("Unrecognized /documentInfo/docType: %(documentType)s"),
|
|
1031
|
+
documentType=documentType)
|
|
1032
|
+
extensionChain.discard(filepath)
|
|
1033
|
+
return {}
|
|
1034
|
+
if missingRequiredMembers or unexpectedMembers:
|
|
1035
|
+
msg = []
|
|
1036
|
+
if missingRequiredMembers:
|
|
1037
|
+
msg.append(_("Required element(s) are missing from metadata: %(missing)s"))
|
|
1038
|
+
if unexpectedMembers:
|
|
1039
|
+
msg.append(_("Unexpected element(s) in metadata: %(unexpected)s"))
|
|
1040
|
+
error("{}:invalidJSONStructure".format(errPrefix),
|
|
1041
|
+
"\n ".join(msg), documentType=documentType,
|
|
1042
|
+
sourceFileLine=oimFile, missing=", ".join(missingRequiredMembers), unexpected=", ".join(unexpectedMembers))
|
|
1043
|
+
if invalidMemberTypes:
|
|
1044
|
+
error("{}:invalidJSONStructure".format(errPrefix),
|
|
1045
|
+
_("Invalid JSON structure member types in metadata: %(members)s"),
|
|
1046
|
+
sourceFileLine=oimFile, members=", ".join(invalidMemberTypes))
|
|
1047
|
+
if invalidSQNames:
|
|
1048
|
+
error("oimce:invalidSQName".format(errPrefix),
|
|
1049
|
+
_("Invalid SQNames in metadata: %(members)s"),
|
|
1050
|
+
sourceFileLine=oimFile, members=", ".join(invalidMemberTypes))
|
|
1051
|
+
|
|
1052
|
+
if isCSV and not primaryReportParameters:
|
|
1053
|
+
primaryReportParameters = oimObject.setdefault("parameters", {})
|
|
1054
|
+
|
|
1055
|
+
# read reportParameters if in a CSV file relative to parent metadata file
|
|
1056
|
+
if isinstance(oimObject.get("parameterURL"), str):
|
|
1057
|
+
parameterURL = oimObject["parameterURL"]
|
|
1058
|
+
parameterFilePath = os.path.join(os.path.dirname(primaryOimFile), parameterURL)
|
|
1059
|
+
if modelXbrl.fileSource.exists(parameterFilePath):
|
|
1060
|
+
problems = []
|
|
1061
|
+
badIdentifiers = []
|
|
1062
|
+
identifiersInThisFile = set()
|
|
1063
|
+
for i, row in enumerate(openCsvReader(parameterFilePath, CSV_PARAMETER_FILE)):
|
|
1064
|
+
if i == 0:
|
|
1065
|
+
if row != ["name", "value"]:
|
|
1066
|
+
problems.append(_("The first row must only consist of \"name\" and \"value\" but contains: {}").format(",".join(row)))
|
|
1067
|
+
elif len(row) > 0 and row[0]:
|
|
1068
|
+
name = row[0]
|
|
1069
|
+
if not IdentifierPattern.match(name):
|
|
1070
|
+
badIdentifiers.append(_("Row {} column 1 is not a valid identifier: {}").format(i+1, name))
|
|
1071
|
+
elif len(row) < 2 or not row[1]:
|
|
1072
|
+
problems.append(_("Row {} value column 2 missing").format(i+1))
|
|
1073
|
+
elif any(cell for cell in row[2:]):
|
|
1074
|
+
problems.append(_("Row {} columns 3 - {} must be empty").format(i+1, len(row)))
|
|
1075
|
+
# no longer illegal to override primary report parameters... but between csv files is it illegal?
|
|
1076
|
+
#elif row[0] in primaryReportParameters:
|
|
1077
|
+
# if primaryReportParameters[row[0]] != row[1]:
|
|
1078
|
+
# error("xbrlce:illegalReportParameterRedefinition",
|
|
1079
|
+
# _("Report parameter %(name)s redefined in file %(file)s, report value %(value1)s, csv value %(value2)s"),
|
|
1080
|
+
# file=parameterURL, name=row[0], value1=primaryReportParameters[row[0]], value2=row[1])
|
|
1081
|
+
elif name in identifiersInThisFile:
|
|
1082
|
+
problems.append(_("Row {} column 1 is has a repeated identifier: {}").format(i+1, name))
|
|
1083
|
+
else:
|
|
1084
|
+
identifiersInThisFile.add(name)
|
|
1085
|
+
primaryReportParameters[name] = row[1]
|
|
1086
|
+
elif any(cell for cell in row):
|
|
1087
|
+
problems.append(_("Row {} has no identifier, all columns must be empty").format(i+1))
|
|
1088
|
+
if badIdentifiers:
|
|
1089
|
+
error("xbrlce:invalidIdentifier",
|
|
1090
|
+
_("Report parameter file %(file)s:\n %(issues)s"),
|
|
1091
|
+
file=parameterURL, issues=", \n".join(badIdentifiers))
|
|
1092
|
+
if problems:
|
|
1093
|
+
error("xbrlce:invalidParameterCSVFile",
|
|
1094
|
+
_("Report parameter file %(file)s issues:\n %(issues)s"),
|
|
1095
|
+
file=parameterURL, issues=", \n".join(problems))
|
|
1096
|
+
else:
|
|
1097
|
+
error("xbrlce:missingParametersFile",
|
|
1098
|
+
_("Report parameter file is missing: %(file)s"),
|
|
1099
|
+
file=parameterURL)
|
|
1100
|
+
|
|
1101
|
+
if isCSVorXL: # normalize relative taxonomy URLs to primary document or nearest absolute parent
|
|
1102
|
+
t = documentInfo.get("taxonomy",())
|
|
1103
|
+
for i, tUrl in enumerate(t):
|
|
1104
|
+
t[i] = modelXbrl.modelManager.cntlr.webCache.normalizeUrl(tUrl, normalizedUrl)
|
|
1105
|
+
|
|
1106
|
+
if isCSVorXL and "extends" in documentInfo:
|
|
1107
|
+
# process extension
|
|
1108
|
+
for extendedFile in documentInfo["extends"]:
|
|
1109
|
+
try:
|
|
1110
|
+
extendedOimObject = loadOimObject(extendedFile, mappedUrl, visitedFiles, extensionChain)
|
|
1111
|
+
except IOError:
|
|
1112
|
+
error("{}:unresolvableBaseMetadataFile".format(errPrefix),
|
|
1113
|
+
_("Extending document file not found: %(extendingFile)s, referenced from %(extendedFile)s"),
|
|
1114
|
+
extendingFile=extendedFile, extendedFile=oimFile)
|
|
1115
|
+
raise OIMException()
|
|
1116
|
+
if extendedOimObject is None:
|
|
1117
|
+
continue # None returned when directed cycle blocks reloading same file
|
|
1118
|
+
# extended must be CSV
|
|
1119
|
+
extendedDocumentInfo = extendedOimObject.get("documentInfo", EMPTY_DICT)
|
|
1120
|
+
extendedDocumentType = extendedDocumentInfo.get("documentType")
|
|
1121
|
+
extendedFinal = extendedDocumentInfo.get("final", EMPTY_DICT)
|
|
1122
|
+
if extendedDocumentType != documentType:
|
|
1123
|
+
error("{}:multipleDocumentTypesInExtensionChain".format(errPrefix),
|
|
1124
|
+
_("Extended documentType %(extendedDocumentType)s must same as extending documentType %(documentType)s in file %(extendedFile)s"),
|
|
1125
|
+
extendedFile=extendedFile, extendedDocumentType=extendedDocumentType, documentType=documentType)
|
|
1126
|
+
raise OIMException()
|
|
1127
|
+
oimParameters = oimObject.setdefault("parameters", {})
|
|
1128
|
+
for paramName, paramValue in extendedOimObject.get("parameters",{}).items():
|
|
1129
|
+
if paramName in oimParameters and oimParameters[paramName] != paramValue:
|
|
1130
|
+
error("xbrlce:illegalReportParameterRedefinition",
|
|
1131
|
+
_("Report parameter %(name)s redefined in file %(file)s, extended value %(value1)s, extending value %(value2)s"),
|
|
1132
|
+
file=extendedFile, name=paramName, value1=oimParameters[paramName], value2=paramValue)
|
|
1133
|
+
else:
|
|
1134
|
+
oimParameters[paramName] = paramValue
|
|
1135
|
+
for parent, extendedParent, excludedObjectNames in (
|
|
1136
|
+
(documentInfo, extendedDocumentInfo, {"documentType", "extends"}),
|
|
1137
|
+
(oimObject, extendedOimObject, {"documentInfo"})):
|
|
1138
|
+
for objectName in extendedFinal:
|
|
1139
|
+
if objectName not in excludedObjectNames and objectName not in extendedParent and objectName in parent:
|
|
1140
|
+
error("xbrlce:illegalExtensionOfFinalProperty",
|
|
1141
|
+
_("Extended file %(extendedFile)s redefines final object %(finalObjectName)s"),
|
|
1142
|
+
extendedFile=extendedFile, finalObjectName=objectName)
|
|
1143
|
+
for objectName in extendedParent.keys() - excludedObjectNames:
|
|
1144
|
+
if objectName in csvExtensibleObjects:
|
|
1145
|
+
for parProp, parPropValue in parent.get(objectName,EMPTY_DICT).items():
|
|
1146
|
+
if extendedFinal.get(objectName, False) and parProp not in extendedParent.get(objectName,EMPTY_DICT):
|
|
1147
|
+
error("xbrlce:illegalExtensionOfFinalProperty",
|
|
1148
|
+
_("Extended file %(extendedFile)s specifies final object %(objectName)s property %(property)s"),
|
|
1149
|
+
extendedFile=oimFile, objectName=objectName, property=parProp)
|
|
1150
|
+
for extProp, extPropValue in extendedParent.get(objectName,EMPTY_DICT).items():
|
|
1151
|
+
if extProp in parent.get(objectName,EMPTY_DICT):
|
|
1152
|
+
if json.dumps(extPropValue,sort_keys=True) != json.dumps(parent[objectName][extProp],sort_keys=True): # ordered dicts, especially nested are not comparable
|
|
1153
|
+
error("xbrlce:conflictingMetadataValue" if extendedFinal.get(objectName, False)
|
|
1154
|
+
else "xbrlce:conflictingMetadataValue",
|
|
1155
|
+
_("Extended file %(extendedFile)s redefines object %(objectName)s property %(property)s"),
|
|
1156
|
+
extendedFile=extendedFile, objectName=objectName, property=extProp)
|
|
1157
|
+
else:
|
|
1158
|
+
if objectName not in parent:
|
|
1159
|
+
parent[objectName] = {}
|
|
1160
|
+
parent[objectName][extProp] = extPropValue
|
|
1161
|
+
elif objectName in parent:
|
|
1162
|
+
if objectName == "taxonomy":
|
|
1163
|
+
for extPropValue in extendedParent["taxonomy"]:
|
|
1164
|
+
if extPropValue not in parent["taxonomy"]:
|
|
1165
|
+
parent["taxonomy"].append(extPropValue)
|
|
1166
|
+
elif extendedParent[objectName] != parent[objectName]:
|
|
1167
|
+
error("xbrlce:illegalRedefinitionOfNonExtensibleProperty",
|
|
1168
|
+
_("Extended file %(extendedFile)s redefines object %(objectName)s"),
|
|
1169
|
+
extendedFile=extendedFile, objectName=objectName)
|
|
1170
|
+
else:
|
|
1171
|
+
parent[objectName] = extendedParent[objectName]
|
|
1172
|
+
|
|
1173
|
+
if extendingFile is None: # entry oimFile
|
|
1174
|
+
if ("taxonomy" in documentInfo or isCSV) and not documentInfo.get("taxonomy",()):
|
|
1175
|
+
error("oime:noTaxonomy",
|
|
1176
|
+
_("The list of taxonomies MUST NOT be empty."))
|
|
1177
|
+
if len(modelXbrl.errors) > numErrorsBeforeJsonCheck:
|
|
1178
|
+
raise OIMException()
|
|
1179
|
+
|
|
1180
|
+
oimObject["=entryParameters"] = (isJSON, isCSV, isXL, isCSVorXL, oimWb, documentInfo, documentType, documentBase)
|
|
1181
|
+
|
|
1182
|
+
extensionChain.discard(filepath)
|
|
1183
|
+
return oimObject
|
|
1184
|
+
|
|
1185
|
+
errorIndexBeforeLoadOim = len(modelXbrl.errors)
|
|
1186
|
+
oimObject = loadOimObject(oimFile, None, set(), set())
|
|
1187
|
+
try:
|
|
1188
|
+
isJSON, isCSV, isXL, isCSVorXL, oimWb, oimDocumentInfo, documentType, documentBase = oimObject["=entryParameters"]
|
|
1189
|
+
except KeyError:
|
|
1190
|
+
raise OIMException() # no document
|
|
1191
|
+
del oimObject["=entryParameters"]
|
|
1192
|
+
|
|
1193
|
+
currentAction = "identifying Metadata objects"
|
|
1194
|
+
taxonomyRefs = oimDocumentInfo.get("taxonomy", EMPTY_LIST)
|
|
1195
|
+
namespaces = oimDocumentInfo.get("namespaces", EMPTY_DICT)
|
|
1196
|
+
linkTypes = oimDocumentInfo.get("linkTypes", EMPTY_DICT)
|
|
1197
|
+
linkGroups = oimDocumentInfo.get("linkGroups", EMPTY_DICT)
|
|
1198
|
+
featuresDict = oimDocumentInfo.get("features", EMPTY_DICT)
|
|
1199
|
+
documentInfoProperties = {"documentType", "features", "namespaces", "linkTypes", "linkGroups", "taxonomy", "baseURL"}
|
|
1200
|
+
oimObjectProperties = {}
|
|
1201
|
+
factProperties = {"decimals", "dimensions", "links", "value"}
|
|
1202
|
+
canonicalValuesFeature = False
|
|
1203
|
+
if isJSON:
|
|
1204
|
+
errPrefix = "xbrlje"
|
|
1205
|
+
valErrPrefix = "xbrlje"
|
|
1206
|
+
OIMReservedAliasURIs["namespaces"] = NSReservedAliasURIs.copy()
|
|
1207
|
+
OIMReservedAliasURIs["namespaces"].update(JSONNSReservedAliasURIs)
|
|
1208
|
+
OIMReservedURIAlias["namespaces"] = NSReservedURIAlias.copy()
|
|
1209
|
+
OIMReservedURIAlias["namespaces"].update(JSONNSReservedURIAliases)
|
|
1210
|
+
factItems = oimObject.get("facts",{}).items()
|
|
1211
|
+
footnotes = oimObject.get("facts",{}).values() # shares this object
|
|
1212
|
+
canonicalValuesFeature = featuresDict.get("xbrl:canonicalValues") in (True, "true")
|
|
1213
|
+
else: # isCSVorXL
|
|
1214
|
+
errPrefix = "xbrlce"
|
|
1215
|
+
valErrPrefix = "xbrlce"
|
|
1216
|
+
OIMReservedAliasURIs["namespaces"] = NSReservedAliasURIs.copy()
|
|
1217
|
+
OIMReservedAliasURIs["namespaces"].update(CSVNSReservedAliasURIs)
|
|
1218
|
+
OIMReservedURIAlias["namespaces"] = NSReservedURIAlias.copy()
|
|
1219
|
+
OIMReservedURIAlias["namespaces"].update(CSVNSReservedURIAliases)
|
|
1220
|
+
reportDimensions = oimObject.get("dimensions", EMPTY_DICT)
|
|
1221
|
+
reportDecimals = oimObject.get("decimals", None)
|
|
1222
|
+
reportParameters = oimObject.get("parameters", {}) # fresh empty dict because csv-loaded parameters get added
|
|
1223
|
+
parseMetadataCellValues(reportParameters)
|
|
1224
|
+
tableTemplates = oimObject.get("tableTemplates", EMPTY_DICT)
|
|
1225
|
+
tables = oimObject.get("tables", EMPTY_DICT)
|
|
1226
|
+
footnotes = (oimObject.get("links", {}), )
|
|
1227
|
+
final = oimObject.get("final", EMPTY_DICT)
|
|
1228
|
+
documentInfoProperties.add("extends")
|
|
1229
|
+
documentInfoProperties.add("final")
|
|
1230
|
+
reportProperties = {"documentInfo", "tableTemplates", "tables", "parameters", "parameterURL", "dimensions", "decimals", "links"}
|
|
1231
|
+
columnProperties = {"comment", "decimals", "dimensions", "propertyGroups", "parameterURL", "propertiesFrom"}
|
|
1232
|
+
|
|
1233
|
+
entityNaQName = qname(re.sub("/xbrl-(json|csv)$","/entities",documentType), "NA")
|
|
1234
|
+
allowedDuplicatesFeature = ALL
|
|
1235
|
+
v = featuresDict.get("xbrl:allowedDuplicates")
|
|
1236
|
+
if v is not None:
|
|
1237
|
+
if v in AllowedDuplicatesFeatureValues:
|
|
1238
|
+
allowedDuplicatesFeature = AllowedDuplicatesFeatureValues[v]
|
|
1239
|
+
else:
|
|
1240
|
+
error("{}:invalidJSONStructure".format(errPrefix),
|
|
1241
|
+
_("The xbbrl:allowedDuplicates feature has an invalid value: %(value)s"),
|
|
1242
|
+
value=v)
|
|
1243
|
+
|
|
1244
|
+
# check extension properties (where metadata specifies CheckPrefix)
|
|
1245
|
+
for extPropSQName, extPropertyPath in extensionProperties.items():
|
|
1246
|
+
extPropPrefix = extPropSQName.partition(":")[0]
|
|
1247
|
+
if extPropPrefix not in namespaces:
|
|
1248
|
+
error("oimce:unboundPrefix",
|
|
1249
|
+
_("The extension property QName prefix was not defined in namespaces: %(extensionProperty)s."),
|
|
1250
|
+
modelObject=modelXbrl, extensionProperty=extPropertyPath)
|
|
1251
|
+
|
|
1252
|
+
# check features
|
|
1253
|
+
for featureSQName, isActive in featuresDict.items():
|
|
1254
|
+
featurePrefix = featureSQName.partition(":")[0]
|
|
1255
|
+
if featurePrefix not in namespaces:
|
|
1256
|
+
error("oimce:unboundPrefix",
|
|
1257
|
+
_("The feature QName prefix was not defined in namespaces: %(feature)s."),
|
|
1258
|
+
modelObject=modelXbrl, feature=featureSQName)
|
|
1259
|
+
|
|
1260
|
+
# check maps
|
|
1261
|
+
for alias, uris in NSReservedAliasURIs.items():
|
|
1262
|
+
for uri in uris:
|
|
1263
|
+
NSReservedURIAlias[uri] = alias
|
|
1264
|
+
|
|
1265
|
+
for map in ("namespaces", "linkTypes", "linkGroups"):
|
|
1266
|
+
for key, value in oimDocumentInfo.get(map, EMPTY_DICT).items():
|
|
1267
|
+
if key in OIMReservedAliasURIs[map] and value not in OIMReservedAliasURIs[map][key]:
|
|
1268
|
+
error("oimce:invalidURIForReservedAlias",
|
|
1269
|
+
_("The %(map)s URI \"%(uri)s\" is used on standard alias \"%(alias)s\" which requires URI \"%(standardUri)s\"."),
|
|
1270
|
+
modelObject=modelXbrl, map=map, alias=key, uri=value, standardUri=OIMReservedAliasURIs[map][key][0])
|
|
1271
|
+
elif value in OIMReservedURIAlias[map] and key != OIMReservedURIAlias[map][value]:
|
|
1272
|
+
error("oimce:invalidAliasForReservedURI",
|
|
1273
|
+
_("The %(map)s URI \"%(uri)s\" is bound to alias \"%(key)s\" instead of standard alias \"%(alias)s\"."),
|
|
1274
|
+
modelObject=modelXbrl, map=key, key=key, uri=value, alias=OIMReservedURIAlias[map][value])
|
|
1275
|
+
|
|
1276
|
+
# check baseURL
|
|
1277
|
+
if documentBase and not UrlUtil.isAbsolute(documentBase):
|
|
1278
|
+
error("oime:invalidBaseURL",
|
|
1279
|
+
_("The base-url must be absolute: \"%(url)s\"."),
|
|
1280
|
+
modelObject=modelXbrl, url=documentBase)
|
|
1281
|
+
|
|
1282
|
+
factProduced = FactProduced() # pass back fact info to csv Fact producer
|
|
1283
|
+
|
|
1284
|
+
if isCSVorXL:
|
|
1285
|
+
currentAction = "loading CSV facts tables"
|
|
1286
|
+
_dir = os.path.dirname(oimFile)
|
|
1287
|
+
|
|
1288
|
+
def csvFacts():
|
|
1289
|
+
parseMetadataCellValues(reportDimensions)
|
|
1290
|
+
for tableId, table in tables.items():
|
|
1291
|
+
_file = tablePath = None
|
|
1292
|
+
try: # note that decoder errors may occur late during streaming of rows
|
|
1293
|
+
tableTemplateId = table.get("template", tableId)
|
|
1294
|
+
tableTemplate = tableTemplates[tableTemplateId]
|
|
1295
|
+
# tableIsTransposed = tableTemplate.get("transposed", False)
|
|
1296
|
+
tableDecimals = tableTemplate.get("decimals")
|
|
1297
|
+
tableDimensions = tableTemplate.get("dimensions", EMPTY_DICT)
|
|
1298
|
+
parseMetadataCellValues(tableDimensions)
|
|
1299
|
+
tableIsOptional = table.get("optional", False)
|
|
1300
|
+
tableParameters = table.get("parameters", EMPTY_DICT)
|
|
1301
|
+
rowIdColName = tableTemplate.get("rowIdColumn")
|
|
1302
|
+
tableUrl = table["url"]
|
|
1303
|
+
tableParameterColNames = set()
|
|
1304
|
+
hasHeaderError = False # set to true blocks handling file beyond header row
|
|
1305
|
+
|
|
1306
|
+
# compile column dependencies
|
|
1307
|
+
factDimensions = {} # keys are column, values are dimensions object
|
|
1308
|
+
factDecimals = {} # keys are column
|
|
1309
|
+
propertyGroups = {}
|
|
1310
|
+
propertiesFrom = {}
|
|
1311
|
+
dimensionsColumns = set()
|
|
1312
|
+
commentColumns = set()
|
|
1313
|
+
extensionColumnProperties = defaultdict(dict)
|
|
1314
|
+
for colId, colProperties in tableTemplate["columns"].items():
|
|
1315
|
+
isCommentColumn = colProperties.get("comment") == True
|
|
1316
|
+
if isCommentColumn:
|
|
1317
|
+
commentColumns.add(colId)
|
|
1318
|
+
else:
|
|
1319
|
+
factDimensions[colId] = colProperties.get("dimensions")
|
|
1320
|
+
factDecimals[colId] = colProperties.get("decimals")
|
|
1321
|
+
isFactColumn = "dimensions" in colProperties
|
|
1322
|
+
if "propertiesFrom" in colProperties:
|
|
1323
|
+
isFactColumn = True
|
|
1324
|
+
propertiesFrom[colId] = colProperties["propertiesFrom"]
|
|
1325
|
+
if not isFactColumn and not isCommentColumn:
|
|
1326
|
+
dimensionsColumns.add(colId) # neither comment nor fact column
|
|
1327
|
+
isPropertyGroupColumn = "propertyGroups" in colProperties
|
|
1328
|
+
if isPropertyGroupColumn:
|
|
1329
|
+
propertyGroups[colId] = colProperties["propertyGroups"]
|
|
1330
|
+
for extPropSQName, prop in colProperties.items():
|
|
1331
|
+
if extPropSQName not in columnProperties:
|
|
1332
|
+
extensionColumnProperties[colId][extPropSQName] = prop
|
|
1333
|
+
# check table parameters
|
|
1334
|
+
tableParameterReferenceNames = set()
|
|
1335
|
+
def checkParamRef(paramValue, factColName=None, dimName=None):
|
|
1336
|
+
if _isParamRef(paramValue):
|
|
1337
|
+
paramName = _getParamRefName(paramValue)
|
|
1338
|
+
tableParameterReferenceNames.add(paramName)
|
|
1339
|
+
unitDims = set()
|
|
1340
|
+
for factColName, colDims in factDimensions.items():
|
|
1341
|
+
if colDims is not None:
|
|
1342
|
+
factDims = set()
|
|
1343
|
+
for inheritedDims in (colDims, tableDimensions, reportDimensions):
|
|
1344
|
+
for dimName, dimValue in inheritedDims.items():
|
|
1345
|
+
checkParamRef(dimValue, factColName, dimName)
|
|
1346
|
+
factDims.add(dimName)
|
|
1347
|
+
parseMetadataCellValues(colDims)
|
|
1348
|
+
for _factDecimals in (factDecimals.get(factColName), tableDecimals, reportDecimals):
|
|
1349
|
+
if "decimals" not in factDims:
|
|
1350
|
+
checkParamRef(_factDecimals, factColName, "decimals")
|
|
1351
|
+
|
|
1352
|
+
if hasHeaderError:
|
|
1353
|
+
return
|
|
1354
|
+
# determine whether table is a CSV file or an Excel range.
|
|
1355
|
+
# Local range can be sheetname! or !rangename
|
|
1356
|
+
# url to workbook with range must be url#sheet! or url#!range or url!range (unencoded !)
|
|
1357
|
+
tableWb = None
|
|
1358
|
+
_file = None
|
|
1359
|
+
_rowIterator = None
|
|
1360
|
+
_cellValue = None
|
|
1361
|
+
if isXL and not ("#" in tableUrl or ".xlsx" in tableUrl or ".csv" in tableUrl):
|
|
1362
|
+
# local Workbook range
|
|
1363
|
+
tableWb = oimWb
|
|
1364
|
+
_cellValue = xlValue
|
|
1365
|
+
xlSheetName, _sep, xlNamedRange = tableUrl.partition('!')
|
|
1366
|
+
else:
|
|
1367
|
+
# check if there's a reference to an Excel workbook file
|
|
1368
|
+
if "#" in tableUrl:
|
|
1369
|
+
tableUrl, _sep, sheetAndRange = tableUrl.partition("#")
|
|
1370
|
+
xlSheetName, _sep, xlNamedRange = sheetAndRange.partition('!')
|
|
1371
|
+
tablePath = os.path.join(_dir, tableUrl)
|
|
1372
|
+
# Remove unnecessary relative segments within path. Effected paths are handled fine
|
|
1373
|
+
# when loading from directories, but this fails when loading from ZIP archives.
|
|
1374
|
+
# OIM conformance suites expect this to be supported:
|
|
1375
|
+
# oim-conf-2021-10-13.zip/300-csv-conformant-processor/V-11,
|
|
1376
|
+
# "/300-csv-conformant-processor/./helloWorld-value-date-table2-facts.csv"
|
|
1377
|
+
# oim-conf-2021-10-13.zip/300-csv-conformant-processor/V-12
|
|
1378
|
+
# "/300-csv-conformant-processor/./helloWorld-SQNameSpecial-facts.csv"
|
|
1379
|
+
tablePath = os.path.normpath(tablePath)
|
|
1380
|
+
if not modelXbrl.fileSource.exists(tablePath):
|
|
1381
|
+
if not tableIsOptional:
|
|
1382
|
+
error("xbrlce:missingRequiredCSVFile",
|
|
1383
|
+
_("Table %(table)s missing, url: %(url)s"),
|
|
1384
|
+
table=tableId, url=tableUrl)
|
|
1385
|
+
continue
|
|
1386
|
+
if tableUrl.endswith(".xlsx"):
|
|
1387
|
+
_file = modelXbrl.fileSource.file(tablePath, binary=True)[0]
|
|
1388
|
+
tableWb = load_workbook(_file, data_only=True)
|
|
1389
|
+
_cellValue = xlValue
|
|
1390
|
+
else:
|
|
1391
|
+
# must be CSV
|
|
1392
|
+
_rowIterator = openCsvReader(tablePath, CSV_FACTS_FILE)
|
|
1393
|
+
_cellValue = csvCellValue
|
|
1394
|
+
# if tableIsTransposed:
|
|
1395
|
+
# _rowIterator = transposer(_rowIterator)
|
|
1396
|
+
if tableWb is not None:
|
|
1397
|
+
hasSheetname = xlSheetName and xlSheetName in tableWb
|
|
1398
|
+
hasNamedRange = xlNamedRange and xlNamedRange in tableWb.defined_names
|
|
1399
|
+
if xlSheetName and not hasSheetname:
|
|
1400
|
+
if tableIsOptional:
|
|
1401
|
+
continue
|
|
1402
|
+
raise OIMException("xbrlwe:missingTable",
|
|
1403
|
+
_("Referenced table tab(s): %(missing)s"),
|
|
1404
|
+
missing=tableUrl)
|
|
1405
|
+
if xlNamedRange and not hasNamedRange:
|
|
1406
|
+
if tableIsOptional:
|
|
1407
|
+
continue
|
|
1408
|
+
raise OIMException("xbrlwe:missingTableNamedRange",
|
|
1409
|
+
_("Referenced named ranges tab(s): %(missing)s"),
|
|
1410
|
+
missing=tableRangeName)
|
|
1411
|
+
if hasNamedRange: # check type of range
|
|
1412
|
+
defn = tableWb.defined_names[xlNamedRange]
|
|
1413
|
+
if defn.type != "RANGE":
|
|
1414
|
+
raise OIMException("xbrlwe:unusableRange",
|
|
1415
|
+
_("Referenced range does not refer to a range: %(tableRange)s"),
|
|
1416
|
+
tableRange=tableRangeName)
|
|
1417
|
+
_rowIterator = []
|
|
1418
|
+
if hasNamedRange:
|
|
1419
|
+
for _tableName, _xlCellsRange in tableWb.defined_names[xlNamedRange].destinations:
|
|
1420
|
+
rows = tableWb[_tableName][_xlCellsRange]
|
|
1421
|
+
if isinstance(rows, Cell):
|
|
1422
|
+
_rowIterator.append((rows, ))
|
|
1423
|
+
else:
|
|
1424
|
+
_rowIterator.extend(rows)
|
|
1425
|
+
else: # use whole table
|
|
1426
|
+
_rowIterator = tableWb[xlSheetName]
|
|
1427
|
+
# if tableIsTransposed:
|
|
1428
|
+
# _rowIterator = transposer(_rowIterator)
|
|
1429
|
+
|
|
1430
|
+
rowIds = set()
|
|
1431
|
+
paramRefColNames = set()
|
|
1432
|
+
potentialInvalidReferenceTargets = {} # dimName: referenceTarget
|
|
1433
|
+
for rowIndex, row in enumerate(_rowIterator):
|
|
1434
|
+
if rowIndex == 0:
|
|
1435
|
+
header = [_cellValue(cell) for cell in row]
|
|
1436
|
+
emptyHeaderCols = set()
|
|
1437
|
+
if isXL: # trim empty cells
|
|
1438
|
+
header = xlTrimHeaderRow(header)
|
|
1439
|
+
colNameIndex = dict((name, colIndex) for colIndex, name in enumerate(header))
|
|
1440
|
+
idColIndex = colNameIndex.get(rowIdColName)
|
|
1441
|
+
for colIndex, colName in enumerate(header):
|
|
1442
|
+
if colName == "":
|
|
1443
|
+
emptyHeaderCols.add(colIndex)
|
|
1444
|
+
elif not IdentifierPattern.match(colName):
|
|
1445
|
+
hasHeaderError = True
|
|
1446
|
+
error("xbrlce:invalidHeaderValue",
|
|
1447
|
+
_("Table %(table)s CSV file header column %(column)s is not a valid identifier: %(identifier)s, url: %(url)s"),
|
|
1448
|
+
table=tableId, column=colIndex+1, identifier=colName, url=tableUrl)
|
|
1449
|
+
elif colName not in factDimensions and colName not in commentColumns:
|
|
1450
|
+
hasHeaderError = True
|
|
1451
|
+
error("xbrlce:unknownColumn",
|
|
1452
|
+
_("Table %(table)s CSV file header column %(column)s is not in table template definition: %(identifier)s, url: %(url)s"),
|
|
1453
|
+
table=tableId, column=colIndex+1, identifier=colName, url=tableUrl)
|
|
1454
|
+
elif colNameIndex[colName] != colIndex:
|
|
1455
|
+
error("xbrlce:repeatedColumnIdentifier",
|
|
1456
|
+
_("Table %(table)s CSV file header columns %(column)s and %(column2)s repeat identifier: %(identifier)s, url: %(url)s"),
|
|
1457
|
+
table=tableId, column=colIndex+1, column2=colNameIndex[colName]+1, identifier=colName, url=tableUrl)
|
|
1458
|
+
if colName in tableParameterReferenceNames and colName not in commentColumns:
|
|
1459
|
+
paramRefColNames.add(colName)
|
|
1460
|
+
checkedDims = set()
|
|
1461
|
+
checkedParams = set()
|
|
1462
|
+
def dimChecks():
|
|
1463
|
+
for colName, colDims in factDimensions.items():
|
|
1464
|
+
if colDims:
|
|
1465
|
+
yield colDims, "column {} dimension".format(colName)
|
|
1466
|
+
# no way to check parameterGroup dimensions at header-row processing time
|
|
1467
|
+
for dims, source in ((tableDimensions, "table dimension"),
|
|
1468
|
+
(reportDimensions, "report dimension"),
|
|
1469
|
+
):
|
|
1470
|
+
yield dims, source
|
|
1471
|
+
for colName, dec in factDecimals.items():
|
|
1472
|
+
yield {"decimals": dec}, "column {} decimals".format(colName)
|
|
1473
|
+
for dec, source in ((tableDecimals, "table decimals"),
|
|
1474
|
+
(reportDecimals, "report decimals")):
|
|
1475
|
+
if source:
|
|
1476
|
+
yield {"decimals": dec}, source
|
|
1477
|
+
for inheritedDims, dimSource in dimChecks():
|
|
1478
|
+
for dimName, dimValue in inheritedDims.items():
|
|
1479
|
+
if dimName not in checkedDims:
|
|
1480
|
+
dimValue = inheritedDims[dimName]
|
|
1481
|
+
# resolve column-relative dimensions
|
|
1482
|
+
if isinstance(dimValue, str):
|
|
1483
|
+
if dimValue.startswith("$"):
|
|
1484
|
+
dimValue = dimValue[1:]
|
|
1485
|
+
if not dimValue.startswith("$"):
|
|
1486
|
+
dimValue, _sep, dimAttr = dimValue.partition("@")
|
|
1487
|
+
if _sep and dimAttr not in ("start", "end"):
|
|
1488
|
+
hasHeaderError = True
|
|
1489
|
+
error("xbrlce:invalidPeriodSpecifier",
|
|
1490
|
+
_("Table %(table)s %(source)s %(dimension)s period-specifier invalid: %(target)s, url: %(url)s"),
|
|
1491
|
+
table=tableId, source=dimSource, dimension=dimName, target=dimAttr, url=tableUrl)
|
|
1492
|
+
if dimValue not in checkedParams:
|
|
1493
|
+
checkedParams.add(dimValue)
|
|
1494
|
+
if dimValue in ("rowNumber", ) or (dimValue in header and dimValue not in commentColumns) or dimValue in tableParameters or dimValue in reportParameters:
|
|
1495
|
+
checkedDims.add(dimValue)
|
|
1496
|
+
else:
|
|
1497
|
+
potentialInvalidReferenceTargets[dimName] = dimValue
|
|
1498
|
+
elif ":" in dimName and ":" in dimValue:
|
|
1499
|
+
dimConcept = modelXbrl.qnameConcepts.get(qname(dimName, namespaces))
|
|
1500
|
+
if dimConcept is not None and dimConcept.isExplicitDimension:
|
|
1501
|
+
memConcept = modelXbrl.qnameConcepts.get(qname(dimValue, namespaces))
|
|
1502
|
+
if memConcept is not None and modelXbrl.dimensionDefaultConcepts.get(dimConcept) == memConcept:
|
|
1503
|
+
error("xbrlce:invalidDimensionValue",
|
|
1504
|
+
_("Table %(table)s %(source)s %(dimension)s value must not be the default member %(member)s, url: %(url)s"),
|
|
1505
|
+
table=tableId, source=dimSource, dimension=dimName, member=dimValue, url=tableUrl)
|
|
1506
|
+
for commentCol in commentColumns:
|
|
1507
|
+
colNameIndex.pop(commentCol,None) # remove comment columns from col name index
|
|
1508
|
+
unreportedFactDimensionColumns = factDimensions.keys() - set(header)
|
|
1509
|
+
reportedDimensionsColumns = dimensionsColumns & set(header)
|
|
1510
|
+
if hasHeaderError:
|
|
1511
|
+
break # stop processing table
|
|
1512
|
+
else:
|
|
1513
|
+
rowId = None
|
|
1514
|
+
paramColsWithValue = set()
|
|
1515
|
+
paramColsUsed = set()
|
|
1516
|
+
emptyCols = set()
|
|
1517
|
+
emptyHeaderColsWithValue = []
|
|
1518
|
+
if isXL and all(cell.value in (None, "") for cell in row): # skip empty excel rows
|
|
1519
|
+
continue
|
|
1520
|
+
rowPropGroups = {} # colName, propGroupObject for property groups in this row
|
|
1521
|
+
rowPropGroupsUsed = set() # colNames used by propertiesFrom of fact col producing a fact
|
|
1522
|
+
hasRowError = False
|
|
1523
|
+
rowPropGrpParamRefs = set()
|
|
1524
|
+
for propGrpName, propGrpObjects in propertyGroups.items():
|
|
1525
|
+
propGrpColIndex = colNameIndex.get(propGrpName, 999999999)
|
|
1526
|
+
if propGrpColIndex < len(row):
|
|
1527
|
+
propGrpColValue = _cellValue(row[propGrpColIndex])
|
|
1528
|
+
if propGrpColValue is NONE_CELL:
|
|
1529
|
+
error("xbrlce:illegalUseOfNone",
|
|
1530
|
+
_("Table %(table)s row %(row)s column %(column)s must not have #none, from %(source)s, url: %(url)s"),
|
|
1531
|
+
table=tableId, row=rowIndex+1, column=colName, url=tableUrl, source=dimSource)
|
|
1532
|
+
hasRowError = True
|
|
1533
|
+
elif propGrpColValue in propGrpObjects:
|
|
1534
|
+
rowPropGroups[propGrpName] = propGrpObjects[propGrpColValue]
|
|
1535
|
+
elif propGrpColValue is not EMPTY_CELL:
|
|
1536
|
+
error("xbrlce:unknownPropertyGroup",
|
|
1537
|
+
_("Table %(table)s unknown property group row %(row)s column %(column)s group %(propertyGroup)s, url: %(url)s"),
|
|
1538
|
+
table=tableId, row=rowIndex+1, column=rowIdColName, url=tableUrl, propertyGroup=propGrpName)
|
|
1539
|
+
hasRowError = True
|
|
1540
|
+
if hasRowError:
|
|
1541
|
+
continue
|
|
1542
|
+
for colIndex, colValue in enumerate(row):
|
|
1543
|
+
if colIndex >= len(header):
|
|
1544
|
+
if _cellValue(colValue) != EMPTY_CELL:
|
|
1545
|
+
emptyHeaderColsWithValue.append(colIndex)
|
|
1546
|
+
continue
|
|
1547
|
+
cellPropGroup = {}
|
|
1548
|
+
propGroupDimSource = {}
|
|
1549
|
+
colName = header[colIndex]
|
|
1550
|
+
if colName == "":
|
|
1551
|
+
if _cellValue(colValue) != EMPTY_CELL:
|
|
1552
|
+
emptyHeaderColsWithValue.append(colIndex)
|
|
1553
|
+
continue
|
|
1554
|
+
if colName in commentColumns:
|
|
1555
|
+
continue
|
|
1556
|
+
propFromColNames = propertiesFrom.get(colName,EMPTY_LIST)
|
|
1557
|
+
for propFromColName in propFromColNames:
|
|
1558
|
+
if propFromColName in rowPropGroups:
|
|
1559
|
+
for prop, val in rowPropGroups[propFromColName].items():
|
|
1560
|
+
if ":" in prop:
|
|
1561
|
+
# Extension property
|
|
1562
|
+
continue
|
|
1563
|
+
if isinstance(val, dict):
|
|
1564
|
+
_valDict = cellPropGroup.setdefault(prop, {})
|
|
1565
|
+
for dim, _val in val.items():
|
|
1566
|
+
_valDict[dim] = _val
|
|
1567
|
+
propGroupDimSource[dim] = propFromColName
|
|
1568
|
+
if _isParamRef(_val):
|
|
1569
|
+
rowPropGrpParamRefs.add(_getParamRefName(_val))
|
|
1570
|
+
else:
|
|
1571
|
+
cellPropGroup[prop] = val
|
|
1572
|
+
propGroupDimSource[prop] = propFromColName
|
|
1573
|
+
if _isParamRef(val):
|
|
1574
|
+
rowPropGrpParamRefs.add(_getParamRefName(val))
|
|
1575
|
+
if factDimensions[colName] is None:
|
|
1576
|
+
value = _cellValue(row[colNameIndex[colName]])
|
|
1577
|
+
if value is EMPTY_CELL or value is NONE_CELL:
|
|
1578
|
+
emptyCols.add(colName)
|
|
1579
|
+
elif colName in paramRefColNames:
|
|
1580
|
+
paramColsWithValue.add(colName)
|
|
1581
|
+
if not cellPropGroup:
|
|
1582
|
+
continue # not a fact column
|
|
1583
|
+
for rowPropGrpParamRef in rowPropGrpParamRefs:
|
|
1584
|
+
value = None
|
|
1585
|
+
if rowPropGrpParamRef in colNameIndex:
|
|
1586
|
+
value = _cellValue(row[colNameIndex[rowPropGrpParamRef]])
|
|
1587
|
+
elif rowPropGrpParamRef in tableParameters:
|
|
1588
|
+
value = tableParameters.get(rowPropGrpParamRef)
|
|
1589
|
+
elif rowPropGrpParamRef in reportParameters:
|
|
1590
|
+
value = reportParameters.get(rowPropGrpParamRef)
|
|
1591
|
+
if value in (None, EMPTY_CELL, NONE_CELL):
|
|
1592
|
+
emptyCols.add(rowPropGrpParamRef)
|
|
1593
|
+
# assemble row and fact Ids
|
|
1594
|
+
if idColIndex is not None and not rowId:
|
|
1595
|
+
if idColIndex < len(row):
|
|
1596
|
+
rowId = _cellValue(row[idColIndex])
|
|
1597
|
+
if not rowId:
|
|
1598
|
+
error("xbrlce:missingRowIdentifier",
|
|
1599
|
+
_("Table %(table)s missing row %(row)s column %(column)s row identifier, url: %(url)s"),
|
|
1600
|
+
table=tableId, row=rowIndex+1, column=rowIdColName, url=tableUrl)
|
|
1601
|
+
elif not RowIdentifierPattern.match(rowId):
|
|
1602
|
+
error("xbrlce:invalidRowIdentifier",
|
|
1603
|
+
_("Table %(table)s row %(row)s column %(column)s is not valid as a row identifier: %(identifier)s, url: %(url)s"),
|
|
1604
|
+
table=tableId, row=rowIndex+1, column=rowIdColName, identifier=rowId, url=tableUrl)
|
|
1605
|
+
elif rowId in rowIds:
|
|
1606
|
+
error("xbrlce:repeatedRowIdentifier",
|
|
1607
|
+
_("Table %(table)s row %(row)s column %(column)s is a duplicate: %(identifier)s, url: %(url)s"),
|
|
1608
|
+
table=tableId, row=rowIndex+1, column=rowIdColName, identifier=rowId, url=tableUrl)
|
|
1609
|
+
else:
|
|
1610
|
+
rowIds.add(rowId)
|
|
1611
|
+
paramColsUsed.add(rowIdColName)
|
|
1612
|
+
factId = "{}.r_{}.{}".format(tableId, rowId or rowIndex, colName) # pre-pend r_ to rowId col value or row number if no rowId col value
|
|
1613
|
+
fact = {}
|
|
1614
|
+
# if this is an id column
|
|
1615
|
+
cellValue = _cellValue(colValue) # nil facts return None, #empty string is ""
|
|
1616
|
+
if cellValue is EMPTY_CELL: # no fact produced
|
|
1617
|
+
continue
|
|
1618
|
+
if cellValue is NONE_CELL:
|
|
1619
|
+
error("xbrlce:illegalUseOfNone",
|
|
1620
|
+
_("Table %(table)s row %(row)s column %(column)s must not have #none, from %(source)s, url: %(url)s"),
|
|
1621
|
+
table=tableId, row=rowIndex+1, column=colName, url=tableUrl, source=dimSource)
|
|
1622
|
+
continue
|
|
1623
|
+
if cellPropGroup:
|
|
1624
|
+
for propFromColName in propFromColNames:
|
|
1625
|
+
rowPropGroupsUsed.add(propFromColName)
|
|
1626
|
+
if colName in extensionColumnProperties: # merge extension properties to fact
|
|
1627
|
+
fact.update(extensionColumnProperties[colName])
|
|
1628
|
+
fact["value"] = cellValue
|
|
1629
|
+
fact["dimensions"] = colFactDims = {}
|
|
1630
|
+
noValueDimNames = set()
|
|
1631
|
+
factDimensionSourceCol = {} # track consumption of column value dynamically
|
|
1632
|
+
factDimensionPropGrpCol = {}
|
|
1633
|
+
for inheritedDims, dimSource in ((factDimensions[colName], "column dimension"),
|
|
1634
|
+
(cellPropGroup.get("dimensions",EMPTY_DICT), "propertyGroup {}".format(propFromColNames)),
|
|
1635
|
+
(tableDimensions, "table dimension"),
|
|
1636
|
+
(reportDimensions, "report dimension")):
|
|
1637
|
+
for dimName, dimValue in inheritedDims.items():
|
|
1638
|
+
if dimSource.startswith("propertyGroup"):
|
|
1639
|
+
factDimensionPropGrpCol[dimName] = propGroupDimSource[dimName]
|
|
1640
|
+
if dimName not in colFactDims and dimName not in noValueDimNames:
|
|
1641
|
+
dimValue = inheritedDims[dimName]
|
|
1642
|
+
dimAttr = None
|
|
1643
|
+
# resolve column-relative dimensions
|
|
1644
|
+
if isinstance(dimValue, str) and dimValue.startswith("$"):
|
|
1645
|
+
dimValue = dimValue[1:]
|
|
1646
|
+
if not dimValue.startswith("$"):
|
|
1647
|
+
paramName, _sep, dimAttr = dimValue.partition("@")
|
|
1648
|
+
if paramName == "rowNumber":
|
|
1649
|
+
dimValue = str(rowIndex)
|
|
1650
|
+
elif paramName in colNameIndex:
|
|
1651
|
+
dimValue = _cellValue(row[colNameIndex[paramName]])
|
|
1652
|
+
if dimValue is EMPTY_CELL or dimValue is NONE_CELL: # csv file empty cell or none
|
|
1653
|
+
dimValue = NONE_CELL
|
|
1654
|
+
else:
|
|
1655
|
+
factDimensionSourceCol[dimName] = paramName
|
|
1656
|
+
elif paramName in tableParameters:
|
|
1657
|
+
dimValue = tableParameters[paramName]
|
|
1658
|
+
factDimensionSourceCol[dimName] = paramName
|
|
1659
|
+
elif paramName in reportParameters:
|
|
1660
|
+
dimValue = reportParameters[paramName]
|
|
1661
|
+
factDimensionSourceCol[dimName] = paramName
|
|
1662
|
+
elif paramName in unreportedFactDimensionColumns:
|
|
1663
|
+
dimValue = NONE_CELL
|
|
1664
|
+
else:
|
|
1665
|
+
dimValue = INVALID_REFERENCE_TARGET
|
|
1666
|
+
# else if in parameters?
|
|
1667
|
+
if dimName == "period" and dimValue is not INVALID_REFERENCE_TARGET:
|
|
1668
|
+
_dimValue = csvPeriod(dimValue, dimAttr)
|
|
1669
|
+
if _dimValue == "referenceTargetNotDuration":
|
|
1670
|
+
error("xbrlce:referenceTargetNotDuration",
|
|
1671
|
+
_("Table %(table)s row %(row)s column %(column)s has instant date with period reference \"%(date)s\", from %(source)s, url: %(url)s"),
|
|
1672
|
+
table=tableId, row=rowIndex+1, column=colName, date=dimValue, url=tableUrl, source=dimSource)
|
|
1673
|
+
dimValue = NONE_CELL
|
|
1674
|
+
elif _dimValue is None: # bad format, raised value error
|
|
1675
|
+
error("xbrlce:invalidPeriodRepresentation",
|
|
1676
|
+
_("Table %(table)s row %(row)s column %(column)s has lexical syntax issue with date \"%(date)s\", from %(source)s, url: %(url)s"),
|
|
1677
|
+
table=tableId, row=rowIndex+1, column=colName, date=dimValue, url=tableUrl, source=dimSource)
|
|
1678
|
+
dimValue = NONE_CELL
|
|
1679
|
+
else:
|
|
1680
|
+
dimValue = _dimValue
|
|
1681
|
+
if dimValue is NONE_CELL:
|
|
1682
|
+
noValueDimNames.add(dimName)
|
|
1683
|
+
else:
|
|
1684
|
+
colFactDims[dimName] = dimValue
|
|
1685
|
+
if factDecimals.get(colName) is not None:
|
|
1686
|
+
dimValue = factDecimals[colName]
|
|
1687
|
+
dimSource = "column decimals"
|
|
1688
|
+
elif "decimals" in cellPropGroup:
|
|
1689
|
+
dimValue = cellPropGroup["decimals"]
|
|
1690
|
+
dimSource = "propertyGroup " + propFromColName
|
|
1691
|
+
if _isParamRef(dimValue):
|
|
1692
|
+
factDimensionPropGrpCol["decimals"] = _getParamRefName(dimValue)
|
|
1693
|
+
else:
|
|
1694
|
+
factDimensionPropGrpCol["decimals"] = dimValue
|
|
1695
|
+
elif tableDecimals is not None:
|
|
1696
|
+
dimValue = tableDecimals
|
|
1697
|
+
dimSource = "table decimals"
|
|
1698
|
+
elif reportDecimals is not None:
|
|
1699
|
+
dimValue = reportDecimals
|
|
1700
|
+
dimSource = "report decimals"
|
|
1701
|
+
else:
|
|
1702
|
+
dimValue = None
|
|
1703
|
+
dimSource = "absent"
|
|
1704
|
+
if dimValue is not None:
|
|
1705
|
+
validCsvCell = False
|
|
1706
|
+
if isinstance(dimValue, str) and dimValue.startswith("$"):
|
|
1707
|
+
paramName = dimValue[1:].partition("@")[0]
|
|
1708
|
+
if paramName in colNameIndex:
|
|
1709
|
+
dimSource += " from CSV column " + paramName
|
|
1710
|
+
dimValue = _cellValue(row[colNameIndex[paramName]])
|
|
1711
|
+
validCsvCell = XmlValidate.integerPattern.match(dimValue or "") is not None # is None if is_XL
|
|
1712
|
+
if dimValue is not NONE_CELL and dimValue != "" and dimValue != "#none":
|
|
1713
|
+
factDimensionSourceCol["decimals"] = paramName
|
|
1714
|
+
elif paramName in tableParameters:
|
|
1715
|
+
dimSource += " from table parameter " + paramName
|
|
1716
|
+
dimValue = tableParameters[paramName]
|
|
1717
|
+
if dimValue != "" and dimValue != "#none" and XmlValidate.integerPattern.match(dimValue):
|
|
1718
|
+
dimValue = int(dimValue)
|
|
1719
|
+
elif paramName in reportParameters:
|
|
1720
|
+
dimSource += " from report parameter " + paramName
|
|
1721
|
+
dimValue = reportParameters[paramName]
|
|
1722
|
+
if dimValue != "" and dimValue != "#none" and XmlValidate.integerPattern.match(dimValue):
|
|
1723
|
+
dimValue = int(dimValue)
|
|
1724
|
+
else:
|
|
1725
|
+
dimValue = INVALID_REFERENCE_TARGET
|
|
1726
|
+
validCsvCell = True # must wait to see if it's used later
|
|
1727
|
+
if dimValue is INVALID_REFERENCE_TARGET:
|
|
1728
|
+
fact["decimals"] = dimValue # allow referencing if not overridden by decimals suffix
|
|
1729
|
+
elif dimValue is not NONE_CELL and dimValue != "" and dimValue != "#none":
|
|
1730
|
+
if isinstance(dimValue, int) or validCsvCell:
|
|
1731
|
+
fact["decimals"] = dimValue
|
|
1732
|
+
else:
|
|
1733
|
+
error("xbrlce:invalidDecimalsValue",
|
|
1734
|
+
_("Table %(table)s row %(row)s column %(column)s has invalid decimals \"%(decimals)s\", from %(source)s, url: %(url)s"),
|
|
1735
|
+
table=tableId, row=rowIndex+1, column=colName, decimals=dimValue, url=tableUrl, source=dimSource)
|
|
1736
|
+
yield (factId, fact)
|
|
1737
|
+
if factProduced.invalidReferenceTarget:
|
|
1738
|
+
error("xbrlce:invalidReferenceTarget",
|
|
1739
|
+
_("Table %(table)s %(dimension)s target not in table columns, parameters or report parameters: %(target)s, url: %(url)s"),
|
|
1740
|
+
table=tableId, dimension=factProduced.invalidReferenceTarget, target=potentialInvalidReferenceTargets.get(factProduced.invalidReferenceTarget), url=tableUrl)
|
|
1741
|
+
break # stop processing table
|
|
1742
|
+
for dimName, dimSource in factDimensionSourceCol.items():
|
|
1743
|
+
if dimName in factProduced.dimensionsUsed:
|
|
1744
|
+
paramColsUsed.add(dimSource)
|
|
1745
|
+
for dimName in factProduced.dimensionsUsed:
|
|
1746
|
+
if dimName in factDimensionPropGrpCol:
|
|
1747
|
+
paramColsUsed.add(factDimensionPropGrpCol[dimName])
|
|
1748
|
+
|
|
1749
|
+
unmappedParamCols = (paramColsWithValue | rowPropGrpParamRefs | reportedDimensionsColumns) - paramColsUsed - emptyCols
|
|
1750
|
+
if unmappedParamCols:
|
|
1751
|
+
error("xbrlce:unmappedCellValue",
|
|
1752
|
+
_("Table %(table)s row %(row)s unmapped parameter columns %(columns)s, url: %(url)s"),
|
|
1753
|
+
table=tableId, row=rowIndex+1, columns=", ".join(sorted(unmappedParamCols)), url=tableUrl)
|
|
1754
|
+
unmappedPropGrps = rowPropGroups.keys() - rowPropGroupsUsed
|
|
1755
|
+
if unmappedPropGrps:
|
|
1756
|
+
error("xbrlce:unmappedCellValue",
|
|
1757
|
+
_("Table %(table)s row %(row)s unmapped property group columns %(columns)s, url: %(url)s"),
|
|
1758
|
+
table=tableId, row=rowIndex+1, columns=", ".join(sorted(unmappedPropGrps)), url=tableUrl)
|
|
1759
|
+
if emptyHeaderColsWithValue:
|
|
1760
|
+
error("xbrlce:unmappedCellValue",
|
|
1761
|
+
_("Table %(table)s row %(row)s empty-header columns with unmapped values in columns %(columns)s, url: %(url)s"),
|
|
1762
|
+
table=tableId, row=rowIndex+1, columns=", ".join(str(c) for c in emptyHeaderColsWithValue), url=tableUrl)
|
|
1763
|
+
|
|
1764
|
+
except UnicodeDecodeError as ex:
|
|
1765
|
+
raise OIMException("{}:invalidJSON".format(errPrefix),
|
|
1766
|
+
_("File MUST use utf-8 encoding: %(file)s, error %(error)s"),
|
|
1767
|
+
file=tablePath, error=str(ex))
|
|
1768
|
+
tableWb = None # dereference
|
|
1769
|
+
_rowIterator = None # dereference
|
|
1770
|
+
if _file is not None:
|
|
1771
|
+
_file.close()
|
|
1772
|
+
|
|
1773
|
+
factItems = csvFacts()
|
|
1774
|
+
|
|
1775
|
+
currentAction = "identifying default dimensions"
|
|
1776
|
+
if modelXbrl is not None:
|
|
1777
|
+
ValidateXbrlDimensions.loadDimensionDefaults(modelXbrl) # needs dimension defaults
|
|
1778
|
+
|
|
1779
|
+
currentAction = "validating OIM"
|
|
1780
|
+
|
|
1781
|
+
# create the instance document
|
|
1782
|
+
currentAction = "creating instance document"
|
|
1783
|
+
# relativize taxonomyRefs to base where feasible
|
|
1784
|
+
txBase = os.path.dirname(documentBase or (modelXbrl.entryLoadingUrl if modelXbrl else ""))
|
|
1785
|
+
for i, tUrl in enumerate(taxonomyRefs or ()):
|
|
1786
|
+
if not UrlUtil.isAbsolute(tUrl) and isLegacyAbs(tUrl) and not UrlUtil.isAbsolute(txBase) and isLegacyAbs(txBase):
|
|
1787
|
+
taxonomyRefs[i] = os.path.relpath(tUrl, txBase)
|
|
1788
|
+
prevErrLen = len(modelXbrl.errors) # track any xbrl validation errors
|
|
1789
|
+
xbrliNamespacePrefix = None
|
|
1790
|
+
for prefix, ns in namespaces.items():
|
|
1791
|
+
if ns == XbrlConst.xbrli:
|
|
1792
|
+
xbrliNamespacePrefix = prefix
|
|
1793
|
+
break
|
|
1794
|
+
if modelXbrl: # pull loader implementation
|
|
1795
|
+
modelXbrl.blockDpmDBrecursion = True
|
|
1796
|
+
modelXbrl.modelDocument = _return = ModelDocument.create(
|
|
1797
|
+
modelXbrl,
|
|
1798
|
+
ModelDocument.Type.INSTANCE,
|
|
1799
|
+
instanceFileName,
|
|
1800
|
+
schemaRefs=taxonomyRefs,
|
|
1801
|
+
isEntry=True,
|
|
1802
|
+
initialComment="extracted from OIM {}".format(mappedUri),
|
|
1803
|
+
xbrliNamespacePrefix=xbrliNamespacePrefix,
|
|
1804
|
+
documentEncoding="utf-8",
|
|
1805
|
+
base=documentBase or modelXbrl.entryLoadingUrl)
|
|
1806
|
+
modelXbrl.modelDocument.inDTS = True
|
|
1807
|
+
else: # API implementation
|
|
1808
|
+
modelXbrl = ModelXbrl.create(
|
|
1809
|
+
cntlr.modelManager,
|
|
1810
|
+
ModelDocument.Type.INSTANCE,
|
|
1811
|
+
instanceFileName,
|
|
1812
|
+
schemaRefs=taxonomyRefs,
|
|
1813
|
+
isEntry=True,
|
|
1814
|
+
initialComment="extracted from OIM {}".format(mappedUri),
|
|
1815
|
+
xbrliNamespacePrefix=xbrliNamespacePrefix,
|
|
1816
|
+
base=documentBase)
|
|
1817
|
+
_return = modelXbrl.modelDocument
|
|
1818
|
+
|
|
1819
|
+
# Updating the namespace map on the document is expensive,
|
|
1820
|
+
# and increases cost as the size of the document grows.
|
|
1821
|
+
# To minimize the effect of this, we can add any known and valid
|
|
1822
|
+
# prefix/namespace combinations to the document preemptively.
|
|
1823
|
+
for prefix, namespace in namespaces.items():
|
|
1824
|
+
if not prefix or not namespace:
|
|
1825
|
+
continue
|
|
1826
|
+
# First, test if the prefix/namespace combination is valid
|
|
1827
|
+
try:
|
|
1828
|
+
etree.Element('nsmap', nsmap={prefix: namespace})
|
|
1829
|
+
except ValueError:
|
|
1830
|
+
# Skip if not valid
|
|
1831
|
+
continue
|
|
1832
|
+
XmlUtil.setXmlns(modelXbrl.modelDocument, prefix, namespace)
|
|
1833
|
+
|
|
1834
|
+
if len(modelXbrl.errors) > prevErrLen:
|
|
1835
|
+
error("oime:invalidTaxonomy",
|
|
1836
|
+
_("Unable to obtain a valid taxonomy from URLs provided"),
|
|
1837
|
+
modelObject=modelXbrl)
|
|
1838
|
+
|
|
1839
|
+
currentAction = "identifying default dimensions"
|
|
1840
|
+
if modelXbrl is not None:
|
|
1841
|
+
ValidateXbrlDimensions.loadDimensionDefaults(modelXbrl) # needs dimension defaults
|
|
1842
|
+
|
|
1843
|
+
# validate statically defined templates
|
|
1844
|
+
if isCSVorXL:
|
|
1845
|
+
currentAction = "checking statically defined dimensions in CSV templates"
|
|
1846
|
+
prevErrLen = len(modelXbrl.errors) # track any xbrl validation errors
|
|
1847
|
+
reportParametersUsed = set()
|
|
1848
|
+
|
|
1849
|
+
def checkIdentifier(identifier, *pathSegs):
|
|
1850
|
+
if not IdentifierPattern.match(identifier):
|
|
1851
|
+
error("xbrlce:invalidIdentifier",
|
|
1852
|
+
_("Invalid identifier: %(identifier)s at %(path)s"),
|
|
1853
|
+
sourceFileLine=oimFile, identifier=identifier, path="/".join(pathSegs))
|
|
1854
|
+
return False
|
|
1855
|
+
return True # identifier is ok
|
|
1856
|
+
|
|
1857
|
+
def checkSQName(sqname, *pathSegs):
|
|
1858
|
+
if not SQNamePattern.match(sqname):
|
|
1859
|
+
error("oimce:invalidSQName",
|
|
1860
|
+
_("Invalid SQName: %(sqname)s"),
|
|
1861
|
+
sourceFileLine=oimFile, sqname=sqname, path="/".join(pathSegs))
|
|
1862
|
+
return False
|
|
1863
|
+
return True # SQName is ok
|
|
1864
|
+
|
|
1865
|
+
def checkDim(tblTmpl, dimName, dimValue, *pathSegs):
|
|
1866
|
+
if dimValue is not None:
|
|
1867
|
+
if isinstance(dimValue,str) and dimValue.startswith("#") and not SpecialValuePattern.match(dimValue):
|
|
1868
|
+
error("xbrlce:unknownSpecialValue",
|
|
1869
|
+
_("Unknown special value: %(value)s at %(path)s"),
|
|
1870
|
+
modelObject=modelXbrl, value=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1871
|
+
elif dimValue == "#nil" and ":" not in dimName and dimName not in ("concept", "period", "value", "entity", "unit"):
|
|
1872
|
+
error("xbrlce:invalidJSONStructure",
|
|
1873
|
+
_("Invalid value: %(value)s at %(path)s"),
|
|
1874
|
+
modelObject=modelXbrl, value=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1875
|
+
elif _isParamRef(dimValue):
|
|
1876
|
+
paramName, _sep, periodSpecifier = dimValue[1:].partition("@")
|
|
1877
|
+
if _sep and periodSpecifier not in ("start", "end"):
|
|
1878
|
+
error("xbrlce:invalidPeriodSpecifier",
|
|
1879
|
+
_("Parameter period-specifier invalid: %(periodSpecifier)s at %(path)s"),
|
|
1880
|
+
periodSpecifier=periodSpecifier, path="/".join(pathSegs+(dimName,)))
|
|
1881
|
+
if not IdentifierPattern.match(paramName):
|
|
1882
|
+
error("xbrlce:invalidReference",
|
|
1883
|
+
_("Parameter reference invalid: %(target)s at %(path)s"),
|
|
1884
|
+
target=paramName, path="/".join(pathSegs+(dimName,)))
|
|
1885
|
+
reportParametersUsed.add(paramName)
|
|
1886
|
+
if tblTmpl:
|
|
1887
|
+
tblTmpl.setdefault("_parametersUsed",set()).add(paramName)
|
|
1888
|
+
elif not (isinstance(dimValue,str) and dimValue.startswith("$")):
|
|
1889
|
+
if dimName == "concept":
|
|
1890
|
+
if dimValue != "#none":
|
|
1891
|
+
if not isinstance(dimValue,str) or ":" not in dimValue or not XmlValidate.QNamePattern.match(dimValue): # allow #nil
|
|
1892
|
+
error("xbrlce:invalidConceptQName",
|
|
1893
|
+
_("Concept does not match lexical QName pattern: %(concept)s at %(path)s"),
|
|
1894
|
+
modelObject=modelXbrl, concept=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1895
|
+
else:
|
|
1896
|
+
conceptQn = qname(dimValue, namespaces)
|
|
1897
|
+
if conceptQn is None: # bad prefix
|
|
1898
|
+
error("oimce:unboundPrefix",
|
|
1899
|
+
_("The QName prefix could not be resolved with available namespaces: %(concept)s at %(path)s"),
|
|
1900
|
+
modelObject=modelXbrl, concept=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1901
|
+
elif conceptQn.localName != "note" or conceptQn.namespaceURI not in nsOims:
|
|
1902
|
+
concept = modelXbrl.qnameConcepts.get(conceptQn)
|
|
1903
|
+
if concept is None:
|
|
1904
|
+
error("oime:unknownConcept",
|
|
1905
|
+
_("The concept QName could not be resolved with available DTS: %(concept)s at %(path)s"),
|
|
1906
|
+
modelObject=modelXbrl, concept=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1907
|
+
elif concept.isItem and concept.isAbstract:
|
|
1908
|
+
error("oime:valueForAbstractConcept",
|
|
1909
|
+
_("Value provided for abstract concept by %(concept)s at %(path)s"),
|
|
1910
|
+
modelObject=modelXbrl, concept=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1911
|
+
elif ((concept.instanceOfType(UNSUPPORTED_DATA_TYPES) and not concept.instanceOfType(XbrlConst.dtrSQNameNamesItemTypes))
|
|
1912
|
+
or concept.isTuple):
|
|
1913
|
+
error("oime:unsupportedConceptDataType",
|
|
1914
|
+
_("Concept has unsupported data type, %(dataType)s: %(concept)s at %(path)s"),
|
|
1915
|
+
modelObject=modelXbrl, concept=dimValue, dataType=concept.typeQname, path="/".join(pathSegs+(dimName,)))
|
|
1916
|
+
elif dimName == "unit":
|
|
1917
|
+
if dimValue == "xbrli:pure":
|
|
1918
|
+
error("oime:illegalPureUnit",
|
|
1919
|
+
_("Unit MUST NOT have single numerator measure xbrli:pure with no denominators: %(unit)s at %(path)s"),
|
|
1920
|
+
modelObject=modelXbrl, unit=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1921
|
+
elif dimValue != "#none" and not UnitPattern.match( PrefixedQName.sub(UnitPrefixedQNameSubstitutionChar, dimValue) ):
|
|
1922
|
+
error("oimce:invalidUnitStringRepresentation",
|
|
1923
|
+
_("Unit string representation is lexically invalid, %(unit)s at %(path)s"),
|
|
1924
|
+
modelObject=modelXbrl, unit=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1925
|
+
elif dimName == "entity":
|
|
1926
|
+
if dimValue != "#none":
|
|
1927
|
+
checkSQName(dimValue or "", *(pathSegs+(dimName,)) )
|
|
1928
|
+
dimQname = qname(dimValue, namespaces)
|
|
1929
|
+
if dimQname == entityNaQName:
|
|
1930
|
+
error("oime:invalidUseOfReservedIdentifier",
|
|
1931
|
+
_("The entity core dimension MUST NOT have a scheme of 'https://xbrl.org/.../entities' with an identifier of 'NA': %(entity)s at %(path)s"),
|
|
1932
|
+
modelObject=modelXbrl, entity=dimQname, path="/".join(pathSegs+(dimName,)))
|
|
1933
|
+
elif dimName == "period":
|
|
1934
|
+
if dimValue != "#none" and not PeriodPattern.match(csvPeriod(dimValue) or ""):
|
|
1935
|
+
error("xbrlce:invalidPeriodRepresentation",
|
|
1936
|
+
_("The period has lexically invalid dateTime %(period)s at %(path)s"),
|
|
1937
|
+
modelObject=modelXbrl, period=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1938
|
+
elif dimName == "language":
|
|
1939
|
+
if dimValue != "#none" and not XmlValidate.languagePattern.match(dimValue or ""):
|
|
1940
|
+
error("xbrlce:invalidLanguageCode",
|
|
1941
|
+
_("The language is lexically invalid %(language)s at %(path)s"),
|
|
1942
|
+
modelObject=modelXbrl, language=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1943
|
+
elif dimName == "decimals":
|
|
1944
|
+
if dimValue != "#none" and not isinstance(dimValue,int) and not XmlValidate.integerPattern.match(str(dimValue) or ""):
|
|
1945
|
+
error("xbrlce:invalidDecimalsValue",
|
|
1946
|
+
_("Decimals is lexically invalid %(language)s at %(path)s"),
|
|
1947
|
+
modelObject=modelXbrl, language=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1948
|
+
elif dimName == "xbrl:noteId":
|
|
1949
|
+
error("xbrlce:invalidJSONStructure",
|
|
1950
|
+
_("NoteId dimension must not be explicitly defined at %(path)s"),
|
|
1951
|
+
modelObject=modelXbrl, qname=dimName, path="/".join(pathSegs+(dimName,)))
|
|
1952
|
+
elif dimName.startswith("xbrl:"):
|
|
1953
|
+
error("xbrlce:invalidJSONStructure",
|
|
1954
|
+
_("Taxonomy-defined dimension must not have xbrl prefix: %(qname)s at %(path)s"),
|
|
1955
|
+
modelObject=modelXbrl, qname=dimName, path="/".join(pathSegs+(dimName,)))
|
|
1956
|
+
elif ":" in dimName: # taxonomy defined dimension
|
|
1957
|
+
dimQname = qname(dimName, namespaces)
|
|
1958
|
+
dimConcept = modelXbrl.qnameConcepts.get(dimQname)
|
|
1959
|
+
if dimConcept is None:
|
|
1960
|
+
error("oime:unknownDimension",
|
|
1961
|
+
_("Taxonomy-defined dimension QName not be resolved with available DTS: %(qname)s at %(path)s"),
|
|
1962
|
+
modelObject=modelXbrl, qname=dimQname, path="/".join(pathSegs+(dimName,)))
|
|
1963
|
+
elif dimConcept.isExplicitDimension:
|
|
1964
|
+
mem = qname(dimValue, namespaces)
|
|
1965
|
+
if mem is None:
|
|
1966
|
+
error("{}:invalidDimensionValue".format(valErrPrefix),
|
|
1967
|
+
_("Taxonomy-defined explicit dimension value is invalid: %(memberQName)s at %(path)s"),
|
|
1968
|
+
modelObject=modelXbrl, memberQName=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1969
|
+
elif dimConcept.isTypedDimension:
|
|
1970
|
+
# a modelObject xml element is needed for all of the instance functions to manage the typed dim
|
|
1971
|
+
_type = dimConcept.typedDomainElement.type
|
|
1972
|
+
if (_type is not None and
|
|
1973
|
+
_type.qname != XbrlConst.qnXbrliDateItemType and
|
|
1974
|
+
(_type.localName in ("complexType", "union", "list", "ENTITY", "ENTITIES", "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "NOTATION")
|
|
1975
|
+
or _type.isDerivedFrom(XbrlConst.dtrPrefixedContentTypes))):
|
|
1976
|
+
error("oime:unsupportedDimensionDataType",
|
|
1977
|
+
_("Taxonomy-defined typed dimension value is complex: %(memberQName)s at %(path)s"),
|
|
1978
|
+
modelObject=modelXbrl, memberQName=dimValue, path="/".join(pathSegs+(dimName,)))
|
|
1979
|
+
if pathSegs[-1] in ("/dimensions", "dimensions") and dimName not in builtInDimensionKeys and not SQNamePattern.match(dimName):
|
|
1980
|
+
error("oimce:invalidSQName",
|
|
1981
|
+
_("Invalid SQName: %(sqname)s"),
|
|
1982
|
+
sourceFileLine=oimFile, sqname=dimName, path="/".join(pathSegs))
|
|
1983
|
+
|
|
1984
|
+
# check reportParameterNames
|
|
1985
|
+
for reportParameterName in reportParameters.keys():
|
|
1986
|
+
checkIdentifier(reportParameterName, "/parameters")
|
|
1987
|
+
for dimName, dimValue in reportDimensions.items():
|
|
1988
|
+
checkDim(None, dimName, dimValue, "/dimensions")
|
|
1989
|
+
checkDim(None, "decimals", reportDecimals, "/")
|
|
1990
|
+
|
|
1991
|
+
# check table template statically defined dimensions, regardless of use
|
|
1992
|
+
for tblTmplId, tblTmpl in tableTemplates.items():
|
|
1993
|
+
checkIdentifier(tblTmplId, "/tableTemplates")
|
|
1994
|
+
propertyGroupCols = set()
|
|
1995
|
+
columns = tblTmpl.get("columns",EMPTY_DICT)
|
|
1996
|
+
for columnId, column in columns.items():
|
|
1997
|
+
checkIdentifier(columnId, "/tableTemplates", tblTmplId, "columns", columnId)
|
|
1998
|
+
if "propertyGroups" in column:
|
|
1999
|
+
propertyGroupCols.add(columnId)
|
|
2000
|
+
isCommentColumn = column.get("comment") == True
|
|
2001
|
+
isFactColumn = "dimensions" in column or "propertiesFrom" in column
|
|
2002
|
+
isPropertyGroupColumn = "propertyGroups" in column
|
|
2003
|
+
if (isPropertyGroupColumn and isFactColumn) or (isCommentColumn and (isPropertyGroupColumn or isFactColumn)):
|
|
2004
|
+
error("xbrlce:conflictingColumnType",
|
|
2005
|
+
_("Conflicting column type at %(path)s"),
|
|
2006
|
+
path="/tableTemplates/{}/columns/{}".format(tblTmplId, columnId))
|
|
2007
|
+
if not isFactColumn and "decimals" in column:
|
|
2008
|
+
error("xbrlce:misplacedDecimalsOnNonFactColumn",
|
|
2009
|
+
_("Column has decimals on a non-fact column at %(path)s"),
|
|
2010
|
+
path="/tableTemplates/{}/columns/{}".format(tblTmplId, columnId))
|
|
2011
|
+
|
|
2012
|
+
for dimName, dimValue in tblTmpl.get("dimensions",EMPTY_DICT).items():
|
|
2013
|
+
checkDim(tblTmpl, dimName, dimValue, "/tableTemplates", tblTmplId, "dimensions")
|
|
2014
|
+
checkDim(tblTmpl, "decimals", tblTmpl.get("decimals",None), "/tableTemplates", tblTmplId)
|
|
2015
|
+
for columnId, column in columns.items():
|
|
2016
|
+
for dimName, dimValue in column.get("dimensions",EMPTY_DICT).items():
|
|
2017
|
+
checkDim(tblTmpl, dimName, dimValue, "/tableTemplates", tblTmplId, "columns", columnId, "dimensions")
|
|
2018
|
+
checkDim(tblTmpl, "decimals", column.get("decimals",None), "/tableTemplates", tblTmplId, "columns", columnId)
|
|
2019
|
+
for propGrpName, propGrp in column.get("propertyGroups",EMPTY_DICT).items():
|
|
2020
|
+
checkIdentifier(propGrpName, "/tableTemplates", tblTmplId, "columns", columnId, "propertyGroups", propGrpName)
|
|
2021
|
+
for dimName, dimValue in propGrp.get("dimensions",EMPTY_DICT).items():
|
|
2022
|
+
checkDim(tblTmpl, dimName, dimValue, "/tableTemplates", tblTmplId, "columns", columnId, "propertyGroups", propGrpName, "dimensions")
|
|
2023
|
+
checkDim(tblTmpl, "decimals", propGrp.get("decimals",None), "/tableTemplates", tblTmplId, "columns", columnId, "propertyGroups", propGrpName)
|
|
2024
|
+
decPGs = set()
|
|
2025
|
+
dimPGs = defaultdict(set)
|
|
2026
|
+
for propertyFrom in column.get("propertiesFrom",()):
|
|
2027
|
+
if propertyFrom not in propertyGroupCols:
|
|
2028
|
+
error("xbrlce:invalidPropertyGroupColumnReference",
|
|
2029
|
+
_("PropertiesFrom value is not a column in table: %(propertyFrom)s at %(path)s"),
|
|
2030
|
+
modelObject=modelXbrl, propertyFrom=propertyFrom, path="/tableTemplates/{}/columns/{}/propertiesFrom".format(tblTmplId,columnId))
|
|
2031
|
+
else:
|
|
2032
|
+
for propGrp in columns[propertyFrom].get("propertyGroups",EMPTY_DICT).values():
|
|
2033
|
+
if "decimals" in propGrp:
|
|
2034
|
+
decPGs.add(propertyFrom)
|
|
2035
|
+
for dim in propGrp.get("dimensions",EMPTY_DICT).keys():
|
|
2036
|
+
dimPGs[dim].add(propertyFrom)
|
|
2037
|
+
if len(decPGs) > 1:
|
|
2038
|
+
error("xbrlce:repeatedPropertyGroupDecimalsProperty",
|
|
2039
|
+
_("PropertiesFrom references repeat decimals property: %(propFroms)s at %(path)s."),
|
|
2040
|
+
propFroms=", ".join(decPGs), path="/tableTemplates/{}/columns/{}/propertiesFrom".format(tblTmplId,columnId))
|
|
2041
|
+
if any(len(dimCols) > 1 for dimCols in dimPGs.values()):
|
|
2042
|
+
error("xbrlce:repeatedPropertyGroupDimension",
|
|
2043
|
+
_("PropertiesFrom references repeat dimensions from: %(propFroms)s, dimension: %(dimensions)s at %(path)s."),
|
|
2044
|
+
propFroms=", ".join(sorted(set(c for d,cs in dimPGs.items() if len(cs) > 1 for c in cs))),
|
|
2045
|
+
dimensions=", ".join(sorted(d for d,cs in dimPGs.items() if len(cs) > 1)),
|
|
2046
|
+
path="/tableTemplates/{}/columns/{}/propertiesFrom".format(tblTmplId,columnId))
|
|
2047
|
+
|
|
2048
|
+
|
|
2049
|
+
rowIdColName = tblTmpl.get("rowIdColumn")
|
|
2050
|
+
if rowIdColName:
|
|
2051
|
+
if rowIdColName not in columns:
|
|
2052
|
+
error("xbrlce:undefinedRowIdColumn",
|
|
2053
|
+
_("RowIdColumn is not defined in columns: %(rowIdColumn)s at %(path)s"),
|
|
2054
|
+
rowIdColumn=rowIdColName, path="/tableTemplates/{}".format(tblTmplId))
|
|
2055
|
+
elif columns[rowIdColName].get("comment") == True:
|
|
2056
|
+
error("xbrlce:invalidRowIdColumn",
|
|
2057
|
+
_("RowIdColumn must not be a comment column: %(rowIdColumn)s at %(path)s"),
|
|
2058
|
+
rowIdColumn=rowIdColName, path="/tableTemplates/{}".format(tblTmplId))
|
|
2059
|
+
|
|
2060
|
+
# table static checks
|
|
2061
|
+
for tableId, table in tables.items():
|
|
2062
|
+
checkIdentifier(tableId, "/tables")
|
|
2063
|
+
tblTmplId = table.get("template", tableId)
|
|
2064
|
+
if checkIdentifier(tblTmplId, "/tables/{}/template".format(tableId)) and tblTmplId not in tableTemplates:
|
|
2065
|
+
error("xbrlce:unknownTableTemplate",
|
|
2066
|
+
_("Referenced template is missing: %(tableTemplateId)s at %(path)s"),
|
|
2067
|
+
modelObject=modelXbrl, tableTemplateId=tblTmplId, path="/tables/{}/template".format(tableId))
|
|
2068
|
+
tblTmpl = tableTemplates.get(tblTmplId)
|
|
2069
|
+
for tblParamName in table.get("parameters", EMPTY_DICT):
|
|
2070
|
+
if not IdentifierPattern.match(tblParamName):
|
|
2071
|
+
error("xbrlce:invalidParameterName",
|
|
2072
|
+
_("Parameter name is not a valid identifier: %(tableParameterName)s at path: %(path)s"),
|
|
2073
|
+
tableParameterName=tblParamName, path="/tables/{}/parameters".format(tableId))
|
|
2074
|
+
# check for table parameter usage by its template
|
|
2075
|
+
if tblTmpl and tblParamName not in tblTmpl.get("_parametersUsed",EMPTY_SET):
|
|
2076
|
+
error("xbrlce:unreferencedParameter",
|
|
2077
|
+
_("Parameter name is not referenced: %(tableParameterName)s at path: %(path)s"),
|
|
2078
|
+
tableParameterName=tblParamName, path="/tables/{}/parameters".format(tableId))
|
|
2079
|
+
|
|
2080
|
+
unreferencedReportParams = reportParameters.keys() - reportParametersUsed
|
|
2081
|
+
if unreferencedReportParams:
|
|
2082
|
+
error("xbrlce:unreferencedParameter",
|
|
2083
|
+
_("Report parameters not referenced: %(parameters)s"),
|
|
2084
|
+
parameters=", ".join(sorted(unreferencedReportParams)))
|
|
2085
|
+
|
|
2086
|
+
|
|
2087
|
+
if len(modelXbrl.errors) > prevErrLen:
|
|
2088
|
+
return NotOIMException() # no point to going ahead.
|
|
2089
|
+
|
|
2090
|
+
firstCntxUnitFactElt = None
|
|
2091
|
+
|
|
2092
|
+
cntxTbl = {}
|
|
2093
|
+
unitTbl = {}
|
|
2094
|
+
xbrlNoteTbl = {} # fact ID: note fact
|
|
2095
|
+
noteFactIDsNotReferenced = set()
|
|
2096
|
+
|
|
2097
|
+
currentAction = "creating facts"
|
|
2098
|
+
factNum = 0 # for synthetic fact number
|
|
2099
|
+
if isJSON:
|
|
2100
|
+
syntheticFactFormat = "_f{{:0{}}}".format(int(log10(len(factItems) or 1))) #want
|
|
2101
|
+
else:
|
|
2102
|
+
syntheticFactFormat = "_f{}" #want
|
|
2103
|
+
|
|
2104
|
+
numFactCreationXbrlErrors = 0
|
|
2105
|
+
|
|
2106
|
+
contextElement = getTaxonomyContextElement(modelXbrl)
|
|
2107
|
+
for id, fact in factItems:
|
|
2108
|
+
factProduced.clear()
|
|
2109
|
+
|
|
2110
|
+
dimensions = fact.get("dimensions", EMPTY_DICT)
|
|
2111
|
+
if "concept" not in dimensions:
|
|
2112
|
+
error("oime:missingConceptDimension",
|
|
2113
|
+
_("The concept core dimension MUST be present on fact: %(id)s."),
|
|
2114
|
+
modelObject=modelXbrl, id=id)
|
|
2115
|
+
continue
|
|
2116
|
+
if not id:
|
|
2117
|
+
id = syntheticFactFormat.format(factNum)
|
|
2118
|
+
factNum += 1
|
|
2119
|
+
conceptSQName = dimensions["concept"]
|
|
2120
|
+
if conceptSQName is INVALID_REFERENCE_TARGET:
|
|
2121
|
+
factProduced.invalidReferenceTarget = "concept"
|
|
2122
|
+
continue
|
|
2123
|
+
factProduced.dimensionsUsed.add("concept")
|
|
2124
|
+
if isCSVorXL and (not isinstance(conceptSQName,str) or ":" not in conceptSQName or not XmlValidate.QNamePattern.match(conceptSQName or "")): # allow #nil
|
|
2125
|
+
error("xbrlce:invalidConceptQName",
|
|
2126
|
+
_("Concept does not match lexical QName pattern: %(concept)s."),
|
|
2127
|
+
modelObject=modelXbrl, concept=conceptSQName)
|
|
2128
|
+
continue
|
|
2129
|
+
conceptPrefix = conceptSQName.partition(":")[0]
|
|
2130
|
+
if conceptPrefix not in namespaces:
|
|
2131
|
+
error("oimce:unboundPrefix",
|
|
2132
|
+
_("The concept QName prefix was not defined in namespaces: %(concept)s."),
|
|
2133
|
+
modelObject=modelXbrl, concept=conceptSQName)
|
|
2134
|
+
continue
|
|
2135
|
+
conceptQn = qname(conceptSQName, namespaces)
|
|
2136
|
+
if conceptQn.localName == "note" and conceptQn.namespaceURI in nsOims:
|
|
2137
|
+
xbrlNoteTbl[id] = fact
|
|
2138
|
+
if "language" not in dimensions:
|
|
2139
|
+
error("oime:missingLanguageForNoteFact",
|
|
2140
|
+
_("Missing language dimension for footnote fact %(id)s"),
|
|
2141
|
+
modelObject=modelXbrl, id=id)
|
|
2142
|
+
else:
|
|
2143
|
+
factProduced.dimensionsUsed.add("language")
|
|
2144
|
+
if isCSVorXL:
|
|
2145
|
+
dimensions["noteId"] = id # infer this dimension
|
|
2146
|
+
elif "noteId" not in dimensions:
|
|
2147
|
+
error("oime:missingNoteIDDimension",
|
|
2148
|
+
_("Missing noteId dimension for footnote fact %(id)s"),
|
|
2149
|
+
modelObject=modelXbrl, id=id)
|
|
2150
|
+
elif dimensions.get("noteId") != id:
|
|
2151
|
+
error("oime:invalidNoteIDValue",
|
|
2152
|
+
_("The noteId dimension value, %(noteId)s, must be the same as footnote fact id, %(id)s"),
|
|
2153
|
+
modelObject=modelXbrl, id=id, noteId=dimensions["noteId"])
|
|
2154
|
+
else:
|
|
2155
|
+
noteFactIDsNotReferenced.add(id)
|
|
2156
|
+
if dimensions.get("unit") and not isCSVorXL:
|
|
2157
|
+
error("oime:misplacedUnitDimension",
|
|
2158
|
+
_("The unit core dimension MUST NOT be present on footnote fact %(id)s: %(unit)s."),
|
|
2159
|
+
modelObject=modelXbrl, id=id, unit=dimensions.get("unit"))
|
|
2160
|
+
if fact.get("decimals") and not isCSVorXL:
|
|
2161
|
+
error("oime:misplacedDecimalsProperty",
|
|
2162
|
+
_("The decimals property MUST NOT be present on footnote fact %(id)s: %(decimals)s"),
|
|
2163
|
+
modelObject=modelXbrl, id=id, decimals=decimals)
|
|
2164
|
+
unexpectedDimensions = [d for d in dimensions if d in ("entity", "period") or ":" in d]
|
|
2165
|
+
if unexpectedDimensions:
|
|
2166
|
+
error("oime:misplacedNoteFactDimension",
|
|
2167
|
+
_("Unexpected dimension(s) for footnote fact %(id)s: %(dimensions)s"),
|
|
2168
|
+
modelObject=modelXbrl, id=id, dimensions=", ".join(sorted(unexpectedDimensions)))
|
|
2169
|
+
try:
|
|
2170
|
+
unacceptableTopElts = set()
|
|
2171
|
+
unacceptablePrefixes = set()
|
|
2172
|
+
valueXhtmlElts = etree.XML(htmlBodyTemplate.format(fact.get("value","")))
|
|
2173
|
+
for elt in valueXhtmlElts.iterchildren():
|
|
2174
|
+
if not elt.tag.startswith(xhtmlTagPrefix):
|
|
2175
|
+
unacceptableTopElts.add(elt.tag)
|
|
2176
|
+
for elt in valueXhtmlElts.iter():
|
|
2177
|
+
if elt.tag.startswith(xhtmlTagPrefix) and elt.prefix:
|
|
2178
|
+
unacceptablePrefixes.add(elt.prefix)
|
|
2179
|
+
if unacceptableTopElts:
|
|
2180
|
+
error("oime:invalidXHTMLFragment",
|
|
2181
|
+
_("xbrl:note MUST have xhtml top level elements in the default xhtml namespace, fact %(id)s, elements %(elements)s"),
|
|
2182
|
+
modelObject=modelXbrl, id=id, elements=", ".join(sorted(unacceptableTopElts)))
|
|
2183
|
+
if unacceptablePrefixes:
|
|
2184
|
+
error("oime:xhtmlElementInNonDefaultNamespace",
|
|
2185
|
+
_("xbrl:note MUST have xhtml elements in the default xhtml namespace, fact %(id)s, non-default prefixes: %(prefixes)s"),
|
|
2186
|
+
modelObject=modelXbrl, id=id, prefixes=", ".join(sorted(unacceptablePrefixes)))
|
|
2187
|
+
except (etree.XMLSyntaxError,
|
|
2188
|
+
UnicodeDecodeError) as err:
|
|
2189
|
+
error("oime:invalidXHTMLFragment",
|
|
2190
|
+
_("Xhtml error for footnote fact %(id)s: %(error)s"),
|
|
2191
|
+
modelObject=modelXbrl, id=id, error=str(err))
|
|
2192
|
+
continue
|
|
2193
|
+
elif "noteId" in dimensions:
|
|
2194
|
+
error("oime:misplacedNoteIDDimension",
|
|
2195
|
+
_("Unexpected noteId dimension on non-footnote fact, id %(id)s"),
|
|
2196
|
+
modelObject=modelXbrl, id=id, noteId=dimensions["noteId"])
|
|
2197
|
+
concept = modelXbrl.qnameConcepts.get(conceptQn)
|
|
2198
|
+
if concept is None:
|
|
2199
|
+
error("oime:unknownConcept",
|
|
2200
|
+
_("The concept QName could not be resolved with available DTS: %(concept)s."),
|
|
2201
|
+
modelObject=modelXbrl, concept=conceptQn)
|
|
2202
|
+
continue
|
|
2203
|
+
attrs = {}
|
|
2204
|
+
if ((concept.instanceOfType(UNSUPPORTED_DATA_TYPES) and not concept.instanceOfType(XbrlConst.dtrSQNameNamesItemTypes))
|
|
2205
|
+
or concept.isTuple):
|
|
2206
|
+
error("oime:unsupportedConceptDataType",
|
|
2207
|
+
_("Concept has unsupported data type, %(value)s: %(concept)s."),
|
|
2208
|
+
modelObject=modelXbrl, concept=conceptSQName, value=fact["value"])
|
|
2209
|
+
continue
|
|
2210
|
+
elif concept.isItem:
|
|
2211
|
+
if concept.isAbstract:
|
|
2212
|
+
error("oime:valueForAbstractConcept",
|
|
2213
|
+
_("Value provided for abstract concept by fact %(factId)s, concept %(concept)s."),
|
|
2214
|
+
modelObject=modelXbrl, factId=id, concept=conceptSQName)
|
|
2215
|
+
continue # skip creating fact because context would be bad
|
|
2216
|
+
if "language" in dimensions:
|
|
2217
|
+
lang = dimensions["language"]
|
|
2218
|
+
if lang is INVALID_REFERENCE_TARGET:
|
|
2219
|
+
factProduced.invalidReferenceTarget = "language"
|
|
2220
|
+
continue
|
|
2221
|
+
if concept.type.isOimTextFactType:
|
|
2222
|
+
if isJSON and not lang.islower():
|
|
2223
|
+
error("xbrlje:invalidLanguageCodeCase",
|
|
2224
|
+
_("Language MUST be lower case: \"%(lang)s\", fact %(factId)s, concept %(concept)s."),
|
|
2225
|
+
modelObject=modelXbrl, factId=id, concept=conceptSQName, lang=lang)
|
|
2226
|
+
factProduced.dimensionsUsed.add("language")
|
|
2227
|
+
attrs["{http://www.w3.org/XML/1998/namespace}lang"] = lang
|
|
2228
|
+
elif not isCSVorXL:
|
|
2229
|
+
error("oime:misplacedLanguageDimension",
|
|
2230
|
+
_("Language \"%(lang)s\" provided for non-text concept by fact %(factId)s, concept %(concept)s."),
|
|
2231
|
+
modelObject=modelXbrl, factId=id, concept=conceptSQName, lang=lang)
|
|
2232
|
+
continue # skip creating fact because language would be bad
|
|
2233
|
+
entityAsQn = entityNaQName
|
|
2234
|
+
entitySQName = dimensions.get("entity")
|
|
2235
|
+
if entitySQName is INVALID_REFERENCE_TARGET:
|
|
2236
|
+
factProduced.invalidReferenceTarget = "entity"
|
|
2237
|
+
continue
|
|
2238
|
+
if entitySQName is not None and entitySQName is not NONE_CELL:
|
|
2239
|
+
factProduced.dimensionsUsed.add("entity")
|
|
2240
|
+
if not SQNamePattern.match(entitySQName):
|
|
2241
|
+
error("oimce:invalidSQName",
|
|
2242
|
+
_("Entity has an invalid value: %(entity)s."),
|
|
2243
|
+
modelObject=modelXbrl, entity=entitySQName)
|
|
2244
|
+
continue
|
|
2245
|
+
entityPrefix = entitySQName.partition(":")[0]
|
|
2246
|
+
if entityPrefix not in namespaces:
|
|
2247
|
+
error("oimce:unboundPrefix",
|
|
2248
|
+
_("Entity QName prefix was not defined in namespaces: %(entity)s."),
|
|
2249
|
+
modelObject=modelXbrl, entity=entitySQName)
|
|
2250
|
+
else:
|
|
2251
|
+
entityAsQn = qname(entitySQName, namespaces)
|
|
2252
|
+
if entityAsQn == entityNaQName:
|
|
2253
|
+
error("oime:invalidUseOfReservedIdentifier",
|
|
2254
|
+
_("The entity core dimension MUST NOT have a scheme of 'https://xbrl.org/.../entities' with an identifier of 'NA': %(entity)s."),
|
|
2255
|
+
modelObject=modelXbrl, entity=entitySQName)
|
|
2256
|
+
continue
|
|
2257
|
+
if "period" in dimensions:
|
|
2258
|
+
period = dimensions["period"]
|
|
2259
|
+
if period is INVALID_REFERENCE_TARGET:
|
|
2260
|
+
factProduced.invalidReferenceTarget = "period"
|
|
2261
|
+
continue
|
|
2262
|
+
elif period is NONE_CELL:
|
|
2263
|
+
period = "forever"
|
|
2264
|
+
elif period is None or not PeriodPattern.match(period):
|
|
2265
|
+
error("xbrlce:invalidPeriodRepresentation" if isCSVorXL else "oimce:invalidPeriodRepresentation",
|
|
2266
|
+
_("The fact %(factId)s, concept %(element)s has a lexically invalid period dateTime %(periodError)s"),
|
|
2267
|
+
modelObject=modelXbrl, factId=id, element=conceptQn, periodError=period)
|
|
2268
|
+
continue
|
|
2269
|
+
else:
|
|
2270
|
+
factProduced.dimensionsUsed.add("period")
|
|
2271
|
+
else:
|
|
2272
|
+
period = "forever"
|
|
2273
|
+
cntxKey = ( # hashable context key
|
|
2274
|
+
("periodType", concept.periodType),
|
|
2275
|
+
("entity", entityAsQn),
|
|
2276
|
+
("period", period)) + tuple(sorted(
|
|
2277
|
+
(dimName, dimVal["value"] if isinstance(dimVal,dict) else dimVal)
|
|
2278
|
+
for dimName, dimVal in dimensions.items()
|
|
2279
|
+
if ":" in dimName))
|
|
2280
|
+
_start, _sep, _end = period.rpartition('/')
|
|
2281
|
+
if period == "forever":
|
|
2282
|
+
_periodType = "forever"
|
|
2283
|
+
elif _start == _end or not _start:
|
|
2284
|
+
_periodType = "instant"
|
|
2285
|
+
else:
|
|
2286
|
+
_periodType = "duration"
|
|
2287
|
+
if concept.periodType == "instant" and _periodType == "forever":
|
|
2288
|
+
error("oime:missingPeriodDimension",
|
|
2289
|
+
_("Missing period for %(periodType)s fact %(factId)s."),
|
|
2290
|
+
modelObject=modelXbrl, factId=id, periodType=concept.periodType, period=period)
|
|
2291
|
+
continue # skip creating fact because context would be bad
|
|
2292
|
+
elif ((concept.periodType == "duration" and (_periodType != "forever" and (not _start or _start == _end))) or
|
|
2293
|
+
(concept.periodType == "instant" and _start and _start != _end)):
|
|
2294
|
+
error("oime:invalidPeriodDimension",
|
|
2295
|
+
_("Invalid period for %(periodType)s fact %(factId)s period %(period)s."),
|
|
2296
|
+
modelObject=modelXbrl, factId=id, periodType=concept.periodType, period=period)
|
|
2297
|
+
continue # skip creating fact because context would be bad
|
|
2298
|
+
elif cntxKey in cntxTbl:
|
|
2299
|
+
_cntx = cntxTbl[cntxKey]
|
|
2300
|
+
for dimName, dimVal in cntxKey[3:]:
|
|
2301
|
+
factProduced.dimensionsUsed.add(dimName)
|
|
2302
|
+
else:
|
|
2303
|
+
cntxId = 'c-{:02}'.format(len(cntxTbl) + 1)
|
|
2304
|
+
qnameDims = {}
|
|
2305
|
+
hasDimErr = False
|
|
2306
|
+
for dimName, dimVal in dimensions.items():
|
|
2307
|
+
if ":" in dimName:
|
|
2308
|
+
if dimVal is INVALID_REFERENCE_TARGET:
|
|
2309
|
+
factProduced.invalidReferenceTarget = dimName
|
|
2310
|
+
hasDimErr = True
|
|
2311
|
+
break
|
|
2312
|
+
factProduced.dimensionsUsed.add(dimName)
|
|
2313
|
+
dimQname = qname(dimName, namespaces)
|
|
2314
|
+
if isJSON and dimQname.namespaceURI in nsOims:
|
|
2315
|
+
error("xbrlje:invalidJSONStructure",
|
|
2316
|
+
_("Fact %(factId)s taxonomy-defined dimension QName must not be xbrl prefixed: %(qname)s."),
|
|
2317
|
+
modelObject=modelXbrl, factId=id, qname=dimQname)
|
|
2318
|
+
continue
|
|
2319
|
+
dimConcept = modelXbrl.qnameConcepts.get(dimQname)
|
|
2320
|
+
if dimConcept is None:
|
|
2321
|
+
error("oime:unknownDimension",
|
|
2322
|
+
_("Fact %(factId)s taxonomy-defined dimension QName not be resolved with available DTS: %(qname)s."),
|
|
2323
|
+
modelObject=modelXbrl, factId=id, qname=dimQname)
|
|
2324
|
+
continue
|
|
2325
|
+
if dimVal is None:
|
|
2326
|
+
memberAttrs = {"{http://www.w3.org/2001/XMLSchema-instance}nil": "true"}
|
|
2327
|
+
else:
|
|
2328
|
+
memberAttrs = None
|
|
2329
|
+
if isinstance(dimVal, dict):
|
|
2330
|
+
dimVal = dimVal["value"]
|
|
2331
|
+
elif dimVal is not None:
|
|
2332
|
+
dimVal = str(dimVal) # may be int or boolean
|
|
2333
|
+
if dimConcept.isExplicitDimension:
|
|
2334
|
+
mem = qname(dimVal, namespaces)
|
|
2335
|
+
if mem is None:
|
|
2336
|
+
error("{}:invalidDimensionValue".format(valErrPrefix),
|
|
2337
|
+
_("Fact %(factId)s taxonomy-defined explicit dimension value is invalid: %(memberQName)s."),
|
|
2338
|
+
modelObject=modelXbrl, factId=id, memberQName=dimVal)
|
|
2339
|
+
continue
|
|
2340
|
+
memConcept = modelXbrl.qnameConcepts.get(mem)
|
|
2341
|
+
if memConcept is not None and modelXbrl.dimensionDefaultConcepts.get(dimConcept) == memConcept:
|
|
2342
|
+
error("{}:invalidDimensionValue".format("oime" if valErrPrefix == "xbrlje" else valErrPrefix),
|
|
2343
|
+
_("Fact %(factId)s taxonomy-defined explicit dimension value must not be the default member: %(memberQName)s."),
|
|
2344
|
+
modelObject=modelXbrl, factId=id, memberQName=dimVal)
|
|
2345
|
+
continue
|
|
2346
|
+
elif dimConcept.isTypedDimension:
|
|
2347
|
+
# a modelObject xml element is needed for all of the instance functions to manage the typed dim
|
|
2348
|
+
if dimConcept.typedDomainElement.baseXsdType in ("ENTITY", "ENTITIES", "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "NOTATION") or (
|
|
2349
|
+
dimConcept.typedDomainElement.instanceOfType(XbrlConst.dtrPrefixedContentTypes) and not dimConcept.typedDomainElement.instanceOfType(XbrlConst.dtrSQNameNamesTypes)) or (
|
|
2350
|
+
dimConcept.typedDomainElement.type is not None and
|
|
2351
|
+
dimConcept.typedDomainElement.type.qname != XbrlConst.qnXbrliDateUnion and
|
|
2352
|
+
(dimConcept.typedDomainElement.type.localName == "complexType" or
|
|
2353
|
+
any(c.localName in ("union","list") for c in dimConcept.typedDomainElement.type.iterchildren()))):
|
|
2354
|
+
error("oime:unsupportedDimensionDataType",
|
|
2355
|
+
_("Fact %(factId)s taxonomy-defined typed dimension value is not supported: %(memberQName)s."),
|
|
2356
|
+
modelObject=modelXbrl, factId=id, memberQName=dimVal)
|
|
2357
|
+
continue
|
|
2358
|
+
if (canonicalValuesFeature and dimVal is not None and
|
|
2359
|
+
not CanonicalXmlTypePattern.get(dimConcept.typedDomainElement.baseXsdType, NoCanonicalPattern).match(dimVal)):
|
|
2360
|
+
error("xbrlje:nonCanonicalValue",
|
|
2361
|
+
_("Numeric typed dimension must have canonical %(type)s value \"%(value)s\": %(concept)s."),
|
|
2362
|
+
modelObject=modelXbrl, type=dimConcept.typedDomainElement.baseXsdType, concept=dimConcept, value=dimVal)
|
|
2363
|
+
mem = XmlUtil.addChild(modelXbrl.modelDocument, dimConcept.typedDomainElement.qname, text=dimVal, attributes=memberAttrs, appendChild=False)
|
|
2364
|
+
else:
|
|
2365
|
+
mem = None # absent typed dimension
|
|
2366
|
+
if mem is not None:
|
|
2367
|
+
qnameDims[dimQname] = DimValuePrototype(modelXbrl, None, dimQname, mem, contextElement)
|
|
2368
|
+
if hasDimErr:
|
|
2369
|
+
continue
|
|
2370
|
+
try:
|
|
2371
|
+
_start, _sep, _end = period.rpartition('/')
|
|
2372
|
+
if period == "forever":
|
|
2373
|
+
startDateTime = endDateTime = None
|
|
2374
|
+
elif _start == _end or not _start:
|
|
2375
|
+
startDateTime = None
|
|
2376
|
+
endDateTime = dateTime(_end, type=DATETIME)
|
|
2377
|
+
else:
|
|
2378
|
+
startDateTime = dateTime(_start, type=DATETIME)
|
|
2379
|
+
endDateTime = dateTime(_end, type=DATETIME)
|
|
2380
|
+
numFactCreationXbrlErrors -= len(modelXbrl.errors) # track any xbrl validation errors
|
|
2381
|
+
prevErrLen = len(modelXbrl.errors)
|
|
2382
|
+
_cntx = modelXbrl.createContext(
|
|
2383
|
+
entityAsQn.namespaceURI,
|
|
2384
|
+
entityAsQn.localName,
|
|
2385
|
+
_periodType,
|
|
2386
|
+
startDateTime,
|
|
2387
|
+
endDateTime,
|
|
2388
|
+
None, # no dimensional validity checking (like formula does)
|
|
2389
|
+
qnameDims, [], [],
|
|
2390
|
+
id=cntxId)
|
|
2391
|
+
if len(modelXbrl.errors) > prevErrLen:
|
|
2392
|
+
if any(err == "xmlSchema:valueError" for err in modelXbrl.errors[prevErrLen:]):
|
|
2393
|
+
error("{}:invalidDimensionValue".format(valErrPrefix),
|
|
2394
|
+
_("Fact %(factId)s taxonomy-defined dimension value errors noted above."),
|
|
2395
|
+
modelObject=modelXbrl, factId=id)
|
|
2396
|
+
continue
|
|
2397
|
+
numFactCreationXbrlErrors += sum(err != "xmlSchema:valueError" for err in modelXbrl.errors[prevErrLen:])
|
|
2398
|
+
except ValueError as err:
|
|
2399
|
+
error("xbrlce:invalidPeriodRepresentation" if isCSVorXL else "oimce:invalidPeriodRepresentation",
|
|
2400
|
+
_("Invalid period for fact %(factId)s period %(period)s, %(error)s."),
|
|
2401
|
+
modelObject=modelXbrl, factId=id, period=period, error=err)
|
|
2402
|
+
continue
|
|
2403
|
+
cntxTbl[cntxKey] = _cntx
|
|
2404
|
+
if firstCntxUnitFactElt is None:
|
|
2405
|
+
firstCntxUnitFactElt = _cntx
|
|
2406
|
+
unitKey = dimensions.get("unit")
|
|
2407
|
+
if concept.isNumeric:
|
|
2408
|
+
if unitKey is INVALID_REFERENCE_TARGET:
|
|
2409
|
+
factProduced.invalidReferenceTarget = "unit"
|
|
2410
|
+
continue
|
|
2411
|
+
if unitKey == "xbrli:pure":
|
|
2412
|
+
error("oime:illegalPureUnit",
|
|
2413
|
+
_("Unit MUST NOT have single numerator measure xbrli:pure with no denominators."),
|
|
2414
|
+
modelObject=modelXbrl, unit=unitKey)
|
|
2415
|
+
continue
|
|
2416
|
+
if unitKey: # not empty cells
|
|
2417
|
+
factProduced.dimensionsUsed.add("unit")
|
|
2418
|
+
if (unitKey or None) in unitTbl: # either None or EMPTY_CELL match None for default pure unit
|
|
2419
|
+
_unit = unitTbl[unitKey or None]
|
|
2420
|
+
else:
|
|
2421
|
+
_unit = None
|
|
2422
|
+
# validate unit
|
|
2423
|
+
if unitKey and not UnitPattern.match( PrefixedQName.sub(UnitPrefixedQNameSubstitutionChar, unitKey) ):
|
|
2424
|
+
error("oimce:invalidUnitStringRepresentation",
|
|
2425
|
+
_("Unit string representation is lexically invalid, %(unit)s"),
|
|
2426
|
+
modelObject=modelXbrl, unit=unitKey)
|
|
2427
|
+
continue
|
|
2428
|
+
else:
|
|
2429
|
+
if not unitKey:
|
|
2430
|
+
_muls = [XbrlConst.qnXbrliPure]
|
|
2431
|
+
_divs = []
|
|
2432
|
+
unitKey = None # use None for pure unit key (may be either no value or empty cell value)
|
|
2433
|
+
else:
|
|
2434
|
+
_mul, _sep, _div = unitKey.partition('/')
|
|
2435
|
+
if _mul.startswith('('):
|
|
2436
|
+
_mul = _mul[1:-1]
|
|
2437
|
+
_muls = [u for u in _mul.split('*') if u]
|
|
2438
|
+
if _div.startswith('('):
|
|
2439
|
+
_div = _div[1:-1]
|
|
2440
|
+
_divs = [u for u in _div.split('*') if u]
|
|
2441
|
+
if _muls != sorted(_muls) or _divs != sorted(_divs):
|
|
2442
|
+
error("oimce:invalidUnitStringRepresentation",
|
|
2443
|
+
_("Unit string representation measures are not in alphabetical order, %(unit)s"),
|
|
2444
|
+
modelObject=modelXbrl, unit=unitKey)
|
|
2445
|
+
try:
|
|
2446
|
+
mulQns = [qname(u, namespaces, prefixException=OIMException("oimce:unboundPrefix",
|
|
2447
|
+
_("Unit prefix is not declared: %(unit)s"),
|
|
2448
|
+
unit=u))
|
|
2449
|
+
for u in _muls]
|
|
2450
|
+
divQns = [qname(u, namespaces, prefixException=OIMException("oimce:unboundPrefix",
|
|
2451
|
+
_("Unit prefix is not declared: %(unit)s"),
|
|
2452
|
+
unit=u))
|
|
2453
|
+
for u in _divs]
|
|
2454
|
+
unitId = 'u-{:02}'.format(len(unitTbl) + 1)
|
|
2455
|
+
for _measures in mulQns, divQns:
|
|
2456
|
+
for _measure in _measures:
|
|
2457
|
+
XmlUtil.addQnameValue(modelXbrl.modelDocument, _measure)
|
|
2458
|
+
_unit = modelXbrl.createUnit(mulQns, divQns, id=unitId)
|
|
2459
|
+
if firstCntxUnitFactElt is None:
|
|
2460
|
+
firstCntxUnitFactElt = _unit
|
|
2461
|
+
except OIMException as ex:
|
|
2462
|
+
error(ex.code, ex.message, modelObject=modelXbrl, **ex.msgArgs)
|
|
2463
|
+
unitTbl[unitKey] = _unit
|
|
2464
|
+
else:
|
|
2465
|
+
_unit = None
|
|
2466
|
+
if unitKey is not None and not isCSVorXL:
|
|
2467
|
+
error("oime:misplacedUnitDimension",
|
|
2468
|
+
_("The unit core dimension MUST NOT be present on non-numeric facts: %(concept)s, unit %(unit)s."),
|
|
2469
|
+
modelObject=modelXbrl, concept=conceptSQName, unit=unitKey)
|
|
2470
|
+
|
|
2471
|
+
attrs["contextRef"] = _cntx.id
|
|
2472
|
+
|
|
2473
|
+
if fact.get("value") is None:
|
|
2474
|
+
if not concept.isNillable:
|
|
2475
|
+
error("{}:invalidFactValue".format("oime" if isJSON else valErrPrefix),
|
|
2476
|
+
_("Nil value applied to non-nillable concept: %(concept)s."),
|
|
2477
|
+
modelObject=modelXbrl, concept=conceptSQName)
|
|
2478
|
+
continue
|
|
2479
|
+
attrs[XbrlConst.qnXsiNil] = "true"
|
|
2480
|
+
text = None
|
|
2481
|
+
elif concept.isEnumeration2Item:
|
|
2482
|
+
text = fact["value"]
|
|
2483
|
+
if concept.instanceOfType(XbrlConst.qnEnumerationSetItemType2020):
|
|
2484
|
+
if len(text):
|
|
2485
|
+
qnames = text.split(" ")
|
|
2486
|
+
else:
|
|
2487
|
+
qnames = () # empty enumerations set
|
|
2488
|
+
else: # single value may be a QName with whitespaces
|
|
2489
|
+
qnames = (text.strip(),)
|
|
2490
|
+
expandedNames = set()
|
|
2491
|
+
if canonicalValuesFeature and not all(qnames[i] < qnames[i+1] for i in range(len(qnames)-1)):
|
|
2492
|
+
error("xbrlje:nonCanonicalValue",
|
|
2493
|
+
_("Enumeration item must be canonically ordered, %(value)s: %(concept)s."),
|
|
2494
|
+
modelObject=modelXbrl, concept=conceptSQName, value=fact["value"])
|
|
2495
|
+
isFactValid = True
|
|
2496
|
+
for qn in qnames:
|
|
2497
|
+
if not PrefixedQName.match(qn):
|
|
2498
|
+
isFactValid = False
|
|
2499
|
+
else:
|
|
2500
|
+
_qname = qname(qn, namespaces)
|
|
2501
|
+
if not _qname:
|
|
2502
|
+
error("oimce:unboundPrefix",
|
|
2503
|
+
_("Enumeration item QName prefix was not defined in namespaces, %(qname)s: %(concept)s."),
|
|
2504
|
+
modelObject=modelXbrl, concept=conceptSQName, qname=qn)
|
|
2505
|
+
continue
|
|
2506
|
+
else:
|
|
2507
|
+
expandedNames.add(_qname.expandedName)
|
|
2508
|
+
if isFactValid:
|
|
2509
|
+
text = " ".join(sorted(expandedNames))
|
|
2510
|
+
else:
|
|
2511
|
+
error("{}:invalidFactValue".format(valErrPrefix),
|
|
2512
|
+
_("Enumeration item must be %(canonicalOrdered)slist of QNames: %(concept)s."),
|
|
2513
|
+
modelObject=modelXbrl, concept=conceptSQName, canonicalOrdered="a canonical ordered " if canonicalValuesFeature else "")
|
|
2514
|
+
continue
|
|
2515
|
+
else:
|
|
2516
|
+
text = fact["value"]
|
|
2517
|
+
if (canonicalValuesFeature and text is not None and
|
|
2518
|
+
not CanonicalXmlTypePattern.get(concept.baseXsdType, NoCanonicalPattern).match(text)):
|
|
2519
|
+
error("xbrlje:nonCanonicalValue",
|
|
2520
|
+
_("Item must have canonical %(type)s value \"%(value)s\": %(concept)s."),
|
|
2521
|
+
modelObject=modelXbrl, type=concept.baseXsdType, concept=conceptSQName, value=text)
|
|
2522
|
+
|
|
2523
|
+
decimals = fact.get("decimals")
|
|
2524
|
+
if concept.isNumeric:
|
|
2525
|
+
if isCSVorXL and isinstance(text, str): # don't check for suffix if not CSV/XL or None or int
|
|
2526
|
+
_number, _sep, _decimals = text.partition("d")
|
|
2527
|
+
if _sep:
|
|
2528
|
+
if decimalsSuffixPattern.match(text):
|
|
2529
|
+
decimals = _decimals.strip()
|
|
2530
|
+
text = _number.strip()
|
|
2531
|
+
else:
|
|
2532
|
+
error("xbrlce:invalidDecimalsSuffix",
|
|
2533
|
+
_("Fact %(factId)s has invalid decimals \"%(decimals)s\""),
|
|
2534
|
+
modelObject=modelXbrl, factId=id, decimals=_sep+_decimals)
|
|
2535
|
+
continue # skip processing this fact
|
|
2536
|
+
elif decimals is not None:
|
|
2537
|
+
if decimals is INVALID_REFERENCE_TARGET:
|
|
2538
|
+
factProduced.invalidReferenceTarget = "decimals"
|
|
2539
|
+
continue
|
|
2540
|
+
factProduced.dimensionsUsed.add("decimals")
|
|
2541
|
+
if _unit is None:
|
|
2542
|
+
continue # skip creating fact because unit was invalid
|
|
2543
|
+
attrs["unitRef"] = _unit.id
|
|
2544
|
+
if text is not None: # no decimals for nil value
|
|
2545
|
+
attrs["decimals"] = decimals if decimals is not None else "INF"
|
|
2546
|
+
elif decimals is not None:
|
|
2547
|
+
error("oime:misplacedDecimalsProperty",
|
|
2548
|
+
_("The decimals property MUST NOT be present on nil facts: %(concept)s, decimals %(decimals)s"),
|
|
2549
|
+
modelObject=modelXbrl, concept=conceptSQName, decimals=decimals)
|
|
2550
|
+
continue
|
|
2551
|
+
elif decimals is not None and not isCSVorXL:
|
|
2552
|
+
# includes nil facts for JSON (but not CSV)
|
|
2553
|
+
error("oime:misplacedDecimalsProperty",
|
|
2554
|
+
_("The decimals property MUST NOT be present on non-numeric facts: %(concept)s, decimals %(decimals)s"),
|
|
2555
|
+
modelObject=modelXbrl, concept=conceptSQName, decimals=decimals)
|
|
2556
|
+
else:
|
|
2557
|
+
text = None #tuple
|
|
2558
|
+
|
|
2559
|
+
attrs["id"] = id
|
|
2560
|
+
if "id" not in fact: # needed for footnote generation
|
|
2561
|
+
fact["id"] = id
|
|
2562
|
+
|
|
2563
|
+
# is value a QName?
|
|
2564
|
+
if concept.baseXbrliType == "QNameItemType": # renormalize prefix of instance fact
|
|
2565
|
+
text = XmlUtil.addQnameValue(modelXbrl.modelDocument, qname(text.strip(), namespaces))
|
|
2566
|
+
|
|
2567
|
+
prevErrLen = len(modelXbrl.errors) # track any xbrl validation errors
|
|
2568
|
+
factProduced.modelFact = f = modelXbrl.createFact(conceptQn, attributes=attrs, text=text, validate=False)
|
|
2569
|
+
if firstCntxUnitFactElt is None:
|
|
2570
|
+
firstCntxUnitFactElt = f
|
|
2571
|
+
|
|
2572
|
+
XmlValidate.validate(modelXbrl, f)
|
|
2573
|
+
if len(modelXbrl.errors) > prevErrLen:
|
|
2574
|
+
numFactCreationXbrlErrors += sum(err != "xmlSchema:valueError" for err in modelXbrl.errors[prevErrLen:])
|
|
2575
|
+
if any(err == "xmlSchema:valueError" for err in modelXbrl.errors[prevErrLen:]):
|
|
2576
|
+
error("{}:invalidFactValue".format(valErrPrefix),
|
|
2577
|
+
_("Fact %(factId)s value error noted above."),
|
|
2578
|
+
modelObject=modelXbrl, factId=id)
|
|
2579
|
+
|
|
2580
|
+
currentAction = "creating footnotes"
|
|
2581
|
+
footnoteLinks = {} # ELR elements
|
|
2582
|
+
factLocs = {} # index by (linkrole, factId)
|
|
2583
|
+
footnoteLinkNotes = defaultdict(set) # linkrole: noteIds
|
|
2584
|
+
# footnoteNbr = 0
|
|
2585
|
+
locNbr = 0
|
|
2586
|
+
definedInstanceRoles = set()
|
|
2587
|
+
undefinedFootnoteTypes = set()
|
|
2588
|
+
undefinedFootnoteGroups = set()
|
|
2589
|
+
undefinedLinkTargets = set()
|
|
2590
|
+
undefinedLinkSources = set() # csv only
|
|
2591
|
+
footnotesIdTargets = set()
|
|
2592
|
+
for factOrFootnote in footnotes:
|
|
2593
|
+
if isJSON:
|
|
2594
|
+
for ftGroups in factOrFootnote.get("links", {}).values():
|
|
2595
|
+
for ftTgtIds in ftGroups.values():
|
|
2596
|
+
for tgtId in ftTgtIds:
|
|
2597
|
+
if tgtId in xbrlNoteTbl:
|
|
2598
|
+
footnotesIdTargets.add(tgtId)
|
|
2599
|
+
elif isCSVorXL: # footnotes contains footnote objects
|
|
2600
|
+
for ftGroups in factOrFootnote.values():
|
|
2601
|
+
for ftSrcIdTgtIds in ftGroups.values():
|
|
2602
|
+
for ftTgtIds in ftSrcIdTgtIds.values():
|
|
2603
|
+
for tgtId in ftTgtIds:
|
|
2604
|
+
footnotesIdTargets.add(tgtId)
|
|
2605
|
+
for factOrFootnote in footnotes:
|
|
2606
|
+
if isJSON:
|
|
2607
|
+
factFootnotes = []
|
|
2608
|
+
for ftType, ftGroups in factOrFootnote.get("links", {}).items():
|
|
2609
|
+
ftSrcId = factOrFootnote.get("id")
|
|
2610
|
+
if ftSrcId is None:
|
|
2611
|
+
ftSrcId = factOrFootnote.get("dimensions",EMPTY_DICT).get("xbrl:noteId")
|
|
2612
|
+
if ftType not in linkTypes:
|
|
2613
|
+
undefinedFootnoteTypes.add(ftType)
|
|
2614
|
+
else:
|
|
2615
|
+
for ftGroup, ftTgtIds in ftGroups.items():
|
|
2616
|
+
if ftGroup not in linkGroups:
|
|
2617
|
+
undefinedFootnoteGroups.add(ftGroup)
|
|
2618
|
+
else:
|
|
2619
|
+
footnote = {"id": ftSrcId,
|
|
2620
|
+
"footnoteGroup": linkGroups[ftGroup],
|
|
2621
|
+
"footnoteType": linkTypes[ftType]}
|
|
2622
|
+
for tgtId in ftTgtIds:
|
|
2623
|
+
if tgtId in xbrlNoteTbl:
|
|
2624
|
+
footnote.setdefault("noteRefs", []).append(tgtId)
|
|
2625
|
+
noteFactIDsNotReferenced.discard(tgtId)
|
|
2626
|
+
elif tgtId in modelXbrl.modelDocument.idObjects:
|
|
2627
|
+
footnote.setdefault("factRefs", []).append(tgtId)
|
|
2628
|
+
else:
|
|
2629
|
+
undefinedLinkTargets.add(tgtId)
|
|
2630
|
+
factFootnotes.append(footnote)
|
|
2631
|
+
elif isCSVorXL: # footnotes contains footnote objects
|
|
2632
|
+
factFootnotes = []
|
|
2633
|
+
for ftType, ftGroups in factOrFootnote.items():
|
|
2634
|
+
if ftType not in linkTypes:
|
|
2635
|
+
undefinedFootnoteTypes.add(ftType)
|
|
2636
|
+
else:
|
|
2637
|
+
for ftGroup, ftSrcIdTgtIds in ftGroups.items():
|
|
2638
|
+
if ftGroup not in linkGroups:
|
|
2639
|
+
undefinedFootnoteGroups.add(ftGroup)
|
|
2640
|
+
else:
|
|
2641
|
+
for ftSrcId, ftTgtIds in ftSrcIdTgtIds.items():
|
|
2642
|
+
footnote = {"id": ftSrcId,
|
|
2643
|
+
"footnoteGroup": linkGroups[ftGroup],
|
|
2644
|
+
"footnoteType": linkTypes[ftType]}
|
|
2645
|
+
if ftSrcId not in modelXbrl.modelDocument.idObjects:
|
|
2646
|
+
undefinedLinkSources.add(ftSrcId)
|
|
2647
|
+
for tgtId in ftTgtIds:
|
|
2648
|
+
if tgtId in xbrlNoteTbl:
|
|
2649
|
+
footnote.setdefault("noteRefs", []).append(tgtId)
|
|
2650
|
+
noteFactIDsNotReferenced.discard(tgtId)
|
|
2651
|
+
elif tgtId in modelXbrl.modelDocument.idObjects:
|
|
2652
|
+
footnote.setdefault("factRefs", []).append(tgtId)
|
|
2653
|
+
else:
|
|
2654
|
+
undefinedLinkTargets.add(tgtId)
|
|
2655
|
+
factFootnotes.append(footnote)
|
|
2656
|
+
for footnote in factFootnotes:
|
|
2657
|
+
factId = footnote["id"]
|
|
2658
|
+
linkrole = footnote["footnoteGroup"]
|
|
2659
|
+
arcrole = footnote["footnoteType"]
|
|
2660
|
+
skipThisFootnote = False
|
|
2661
|
+
if not factId or not linkrole or not arcrole or not (
|
|
2662
|
+
footnote.get("factRefs") or footnote.get("footnote") is not None or footnote.get("noteRefs") is not None):
|
|
2663
|
+
if not linkrole:
|
|
2664
|
+
warning("xbrlje:unknownLinkGroup",
|
|
2665
|
+
_("FootnoteId has no linkrole %(footnoteId)s."),
|
|
2666
|
+
modelObject=modelXbrl, footnoteId=footnote.get("footnoteId"))
|
|
2667
|
+
if not arcrole:
|
|
2668
|
+
warning("xbrlje:unknownLinkType",
|
|
2669
|
+
_("FootnoteId has no arcrole %(footnoteId)s."),
|
|
2670
|
+
modelObject=modelXbrl, footnoteId=footnote.get("footnoteId"))
|
|
2671
|
+
continue
|
|
2672
|
+
for refType, refValue, roleTypes, lrrRoles in (("role", linkrole, modelXbrl.roleTypes, XbrlConst.lrrRoleHrefs),
|
|
2673
|
+
("arcrole", arcrole, modelXbrl.arcroleTypes, XbrlConst.lrrArcroleHrefs)):
|
|
2674
|
+
if not (XbrlConst.isStandardRole(refValue) or XbrlConst.isStandardArcrole(refValue)):
|
|
2675
|
+
if refValue not in definedInstanceRoles:
|
|
2676
|
+
if refValue in roleTypes or refValue in lrrRoles:
|
|
2677
|
+
definedInstanceRoles.add(refValue)
|
|
2678
|
+
if refValue in roleTypes:
|
|
2679
|
+
hrefElt = roleTypes[refValue][0]
|
|
2680
|
+
href = hrefElt.modelDocument.uri + "#" + hrefElt.id
|
|
2681
|
+
else:
|
|
2682
|
+
href = lrrRoles[refValue]
|
|
2683
|
+
elt = XmlUtil.addChild(modelXbrl.modelDocument.xmlRootElement,
|
|
2684
|
+
qname(XbrlConst.link, refType+"Ref"),
|
|
2685
|
+
attributes=(("{http://www.w3.org/1999/xlink}href", href),
|
|
2686
|
+
("{http://www.w3.org/1999/xlink}type", "simple")),
|
|
2687
|
+
beforeSibling=firstCntxUnitFactElt)
|
|
2688
|
+
href = modelXbrl.modelDocument.discoverHref(elt)
|
|
2689
|
+
if href:
|
|
2690
|
+
_elt, hrefDoc, hrefId = href
|
|
2691
|
+
_defElt = hrefDoc.idObjects.get(hrefId)
|
|
2692
|
+
if _defElt is not None:
|
|
2693
|
+
_uriAttrName = refType + "URI"
|
|
2694
|
+
_uriAttrValue = _defElt.get(_uriAttrName)
|
|
2695
|
+
if _uriAttrValue:
|
|
2696
|
+
elt.set(_uriAttrName, _uriAttrValue)
|
|
2697
|
+
else:
|
|
2698
|
+
error("xbrlxe:nonStandardRoleDefinitionNotInDTS",
|
|
2699
|
+
_("Footnote %(sourceId)s %(roleType)s %(role)s not defined in DTS"),
|
|
2700
|
+
modelObject=modelXbrl, sourceId=factId, roleType=refType, role=refValue)
|
|
2701
|
+
skipThisFootnote = True
|
|
2702
|
+
if skipThisFootnote:
|
|
2703
|
+
continue
|
|
2704
|
+
if linkrole not in footnoteLinks:
|
|
2705
|
+
footnoteLinks[linkrole] = XmlUtil.addChild(modelXbrl.modelDocument.xmlRootElement,
|
|
2706
|
+
XbrlConst.qnLinkFootnoteLink,
|
|
2707
|
+
attributes={"{http://www.w3.org/1999/xlink}type": "extended",
|
|
2708
|
+
"{http://www.w3.org/1999/xlink}role": linkrole})
|
|
2709
|
+
footnoteLink = footnoteLinks[linkrole]
|
|
2710
|
+
factIDs = (factId,)
|
|
2711
|
+
if factId in xbrlNoteTbl: # factId is a note, not a fact
|
|
2712
|
+
fromLabel = "f_{}".format(factId)
|
|
2713
|
+
factLocs[(linkrole, factIDs)] = fromLabel
|
|
2714
|
+
noteFactIDsNotReferenced.discard(factId)
|
|
2715
|
+
elif (linkrole, factIDs) not in factLocs:
|
|
2716
|
+
locNbr += 1
|
|
2717
|
+
locLabel = "l_{:02}".format(locNbr)
|
|
2718
|
+
factLocs[(linkrole, factIDs)] = locLabel
|
|
2719
|
+
XmlUtil.addChild(footnoteLink, XbrlConst.qnLinkLoc,
|
|
2720
|
+
attributes={XLINKTYPE: "locator",
|
|
2721
|
+
XLINKHREF: "#" + factId,
|
|
2722
|
+
XLINKLABEL: locLabel})
|
|
2723
|
+
locFromLabel = factLocs[(linkrole, factIDs)]
|
|
2724
|
+
if "noteRefs" in footnote:
|
|
2725
|
+
# footnoteNbr += 1
|
|
2726
|
+
# footnoteToLabel = "f_{:02}".format(footnoteNbr)
|
|
2727
|
+
for noteId in footnote.get("noteRefs"):
|
|
2728
|
+
footnoteToLabel = "f_{}".format(noteId)
|
|
2729
|
+
noteFactIDsNotReferenced.discard(noteId)
|
|
2730
|
+
if noteId not in footnoteLinkNotes[linkrole]:
|
|
2731
|
+
footnoteLinkNotes[linkrole].add(noteId)
|
|
2732
|
+
xbrlNote = xbrlNoteTbl[noteId]
|
|
2733
|
+
attrs = {XLINKTYPE: "resource",
|
|
2734
|
+
XLINKROLE: XbrlConst.footnote,
|
|
2735
|
+
XLINKLABEL: footnoteToLabel,
|
|
2736
|
+
"id": idDeduped(modelXbrl, noteId),
|
|
2737
|
+
# "oimNoteId": noteId
|
|
2738
|
+
}
|
|
2739
|
+
#if noteId in footnotesIdTargets: # footnote resource is target of another footnote loc
|
|
2740
|
+
# attrs["id"] = noteId
|
|
2741
|
+
try:
|
|
2742
|
+
if "dimensions" in xbrlNote:
|
|
2743
|
+
attrs[XMLLANG] = xbrlNote["dimensions"]["language"]
|
|
2744
|
+
elif "aspects" in xbrlNote:
|
|
2745
|
+
attrs[XMLLANG] = xbrlNote["aspects"]["language"]
|
|
2746
|
+
except KeyError:
|
|
2747
|
+
pass
|
|
2748
|
+
tgtElt = XmlUtil.addChild(footnoteLink, XbrlConst.qnLinkFootnote, attributes=attrs)
|
|
2749
|
+
srcElt = etree.fromstring("<footnote xmlns=\"http://www.w3.org/1999/xhtml\">{}</footnote>"
|
|
2750
|
+
.format(xbrlNote["value"]), parser=modelXbrl.modelDocument.parser)
|
|
2751
|
+
if srcElt.__len__() > 0: # has html children
|
|
2752
|
+
XmlUtil.setXmlns(modelXbrl.modelDocument, "xhtml", "http://www.w3.org/1999/xhtml")
|
|
2753
|
+
XmlUtil.copyIxFootnoteHtml(srcElt, tgtElt, withText=True, isContinChainElt=False)
|
|
2754
|
+
XmlValidate.validate(modelXbrl, tgtElt)
|
|
2755
|
+
footnoteArc = XmlUtil.addChild(footnoteLink,
|
|
2756
|
+
XbrlConst.qnLinkFootnoteArc,
|
|
2757
|
+
attributes={XLINKTYPE: "arc",
|
|
2758
|
+
XLINKARCROLE: arcrole,
|
|
2759
|
+
XLINKFROM: locFromLabel,
|
|
2760
|
+
XLINKTO: footnoteToLabel})
|
|
2761
|
+
if "factRefs" in footnote:
|
|
2762
|
+
factRef = footnote.get("factRefs")
|
|
2763
|
+
fact2IDs = tuple(sorted(factRef))
|
|
2764
|
+
if (linkrole, fact2IDs) not in factLocs:
|
|
2765
|
+
locNbr += 1
|
|
2766
|
+
locLabel = "l_{:02}".format(locNbr)
|
|
2767
|
+
factLocs[(linkrole, fact2IDs)] = locLabel
|
|
2768
|
+
for factId in fact2IDs:
|
|
2769
|
+
XmlUtil.addChild(footnoteLink, XbrlConst.qnLinkLoc,
|
|
2770
|
+
attributes={XLINKTYPE: "locator",
|
|
2771
|
+
XLINKHREF: "#" + factId,
|
|
2772
|
+
XLINKLABEL: locLabel})
|
|
2773
|
+
footnoteToLabel = factLocs[(linkrole, fact2IDs)]
|
|
2774
|
+
footnoteArc = XmlUtil.addChild(footnoteLink,
|
|
2775
|
+
XbrlConst.qnLinkFootnoteArc,
|
|
2776
|
+
attributes={XLINKTYPE: "arc",
|
|
2777
|
+
XLINKARCROLE: arcrole,
|
|
2778
|
+
XLINKFROM: locFromLabel,
|
|
2779
|
+
XLINKTO: footnoteToLabel})
|
|
2780
|
+
if arcrole == XbrlConst.factFootnote:
|
|
2781
|
+
error("oime:illegalStandardFootnoteTarget",
|
|
2782
|
+
_("Standard footnote %(sourceId)s targets must be an xbrl:note, targets %(targetIds)s."),
|
|
2783
|
+
modelObject=modelXbrl, sourceId=factId, targetIds=", ".join(fact2IDs))
|
|
2784
|
+
if noteFactIDsNotReferenced:
|
|
2785
|
+
error("oime:unusedNoteFact",
|
|
2786
|
+
_("Note facts MUST be referenced by at least one link group, IDs: %(noteFactIds)s."),
|
|
2787
|
+
modelObject=modelXbrl, noteFactIds=", ".join(sorted(noteFactIDsNotReferenced)))
|
|
2788
|
+
if footnoteLinks:
|
|
2789
|
+
modelXbrl.modelDocument.linkbaseDiscover(footnoteLinks.values(), inInstance=True)
|
|
2790
|
+
|
|
2791
|
+
if undefinedLinkSources:
|
|
2792
|
+
error("{}:unknownLinkSource".format(errPrefix),
|
|
2793
|
+
_("These link sources are not defined in facts: %(ftTargets)s."),
|
|
2794
|
+
modelObject=modelXbrl, ftTargets=", ".join(sorted(undefinedLinkSources)))
|
|
2795
|
+
if undefinedLinkTargets:
|
|
2796
|
+
error("{}:unknownLinkTarget".format(errPrefix),
|
|
2797
|
+
_("These link targets are not defined in facts: %(ftTargets)s."),
|
|
2798
|
+
modelObject=modelXbrl, ftTargets=", ".join(sorted(undefinedLinkTargets)))
|
|
2799
|
+
if undefinedFootnoteTypes:
|
|
2800
|
+
error("{}:unknownLinkType".format(errPrefix),
|
|
2801
|
+
_("These footnote types are not defined in footnoteTypes: %(ftTypes)s."),
|
|
2802
|
+
modelObject=modelXbrl, ftTypes=", ".join(sorted(undefinedFootnoteTypes)))
|
|
2803
|
+
if undefinedFootnoteGroups:
|
|
2804
|
+
error("{}:unknownLinkGroup".format(errPrefix),
|
|
2805
|
+
_("These footnote groups are not defined in footnoteGroups: %(ftGroups)s."),
|
|
2806
|
+
modelObject=modelXbrl, ftGroups=", ".join(sorted(undefinedFootnoteGroups)))
|
|
2807
|
+
|
|
2808
|
+
currentAction = "checking for duplicates"
|
|
2809
|
+
checkForDuplicates(modelXbrl, allowedDuplicatesFeature, footnotesIdTargets)
|
|
2810
|
+
|
|
2811
|
+
currentAction = "done loading facts and footnotes"
|
|
2812
|
+
|
|
2813
|
+
if numFactCreationXbrlErrors:
|
|
2814
|
+
error("oime:invalidXBRL",
|
|
2815
|
+
_("%(count)s XBRL errors noted above."),
|
|
2816
|
+
modelObject=modelXbrl, count=numFactCreationXbrlErrors)
|
|
2817
|
+
|
|
2818
|
+
|
|
2819
|
+
#cntlr.addToLog("Completed in {0:.2} secs".format(time.time() - startedAt),
|
|
2820
|
+
# messageCode="loadFromExcel:info")
|
|
2821
|
+
except NotOIMException as ex:
|
|
2822
|
+
_return = ex # not an OIM document
|
|
2823
|
+
except Exception as ex:
|
|
2824
|
+
_return = ex
|
|
2825
|
+
if isinstance(ex, OIMException):
|
|
2826
|
+
if ex.code and ex.message:
|
|
2827
|
+
error(ex.code, ex.message, modelObject=modelXbrl, **ex.msgArgs)
|
|
2828
|
+
else:
|
|
2829
|
+
error("arelleOIMloader:error",
|
|
2830
|
+
"Error while %(action)s, error %(error)s\n traceback %(traceback)s",
|
|
2831
|
+
modelObject=modelXbrl, action=currentAction, error=ex,
|
|
2832
|
+
traceback=traceback.format_tb(sys.exc_info()[2]))
|
|
2833
|
+
|
|
2834
|
+
# Reset modified status of model so user is not prompted for changes triggered by this loading operation.
|
|
2835
|
+
_return.isModified = False
|
|
2836
|
+
return _return
|
|
2837
|
+
|
|
2838
|
+
def _isParamRef(value):
|
|
2839
|
+
if not isinstance(value, str):
|
|
2840
|
+
return False
|
|
2841
|
+
if not value.startswith("$"):
|
|
2842
|
+
return False
|
|
2843
|
+
return not value.startswith("$$")
|
|
2844
|
+
|
|
2845
|
+
def _getParamRefName(paramRef):
|
|
2846
|
+
prefixStripped = paramRef.removeprefix("$")
|
|
2847
|
+
periodSpecifierRemoved = prefixStripped.partition("@")[0]
|
|
2848
|
+
return periodSpecifierRemoved
|
|
2849
|
+
|
|
2850
|
+
def isOimLoadable(normalizedUri, filepath):
|
|
2851
|
+
_ext = os.path.splitext(filepath)[1]
|
|
2852
|
+
if _ext in (".csv", ".json", ".xlsx", ".xls"):
|
|
2853
|
+
return True
|
|
2854
|
+
elif UrlUtil.isHttpUrl(normalizedUri) and '?' in _ext: # query parameters and not .json, may be JSON anyway
|
|
2855
|
+
with io.open(filepath, 'rt', encoding='utf-8') as f:
|
|
2856
|
+
_fileStart = f.read(4096)
|
|
2857
|
+
if _fileStart and re.match(r"\s*\{\s*\"documentType\":\s*\"http:\\+/\\+/www.xbrl.org\\+/WGWD\\+/YYYY-MM-DD\\+/xbrl-json\"", _fileStart):
|
|
2858
|
+
return True
|
|
2859
|
+
return False
|
|
2860
|
+
|
|
2861
|
+
def oimLoader(modelXbrl, mappedUri, filepath):
|
|
2862
|
+
cntlr = modelXbrl.modelManager.cntlr
|
|
2863
|
+
cntlr.showStatus(_("Loading OIM file: {0}").format(os.path.basename(filepath)))
|
|
2864
|
+
doc = _loadFromOIM(cntlr, modelXbrl.error, modelXbrl.warning, modelXbrl, filepath, mappedUri)
|
|
2865
|
+
if doc is None:
|
|
2866
|
+
return None # not an OIM file
|
|
2867
|
+
modelXbrl.loadedFromOIM = True
|
|
2868
|
+
modelXbrl.loadedFromOimErrorCount = len(modelXbrl.errors)
|
|
2869
|
+
return doc
|