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.
Files changed (893) hide show
  1. arelle/Aspect.py +6 -3
  2. arelle/BetaFeatures.py +3 -0
  3. arelle/Cntlr.py +117 -413
  4. arelle/CntlrCmdLine.py +364 -227
  5. arelle/CntlrQuickBooks.py +4 -2
  6. arelle/CntlrWebMain.py +179 -119
  7. arelle/CntlrWinMain.py +342 -124
  8. arelle/DialogAbout.py +1 -1
  9. arelle/DialogArcroleGroup.py +1 -1
  10. arelle/DialogFind.py +1 -1
  11. arelle/DialogFormulaParameters.py +2 -2
  12. arelle/DialogLanguage.py +45 -36
  13. arelle/DialogNewFactItem.py +1 -1
  14. arelle/DialogOpenArchive.py +30 -34
  15. arelle/DialogPackageManager.py +11 -8
  16. arelle/DialogPluginManager.py +43 -89
  17. arelle/DialogRssWatch.py +2 -2
  18. arelle/DialogURL.py +1 -1
  19. arelle/DialogUserPassword.py +1 -1
  20. arelle/DisclosureSystem.py +69 -1
  21. arelle/ErrorManager.py +321 -0
  22. arelle/FileSource.py +201 -99
  23. arelle/FunctionFn.py +44 -5
  24. arelle/FunctionIxt.py +16 -13
  25. arelle/FunctionUtil.py +1 -1
  26. arelle/FunctionXfi.py +68 -24
  27. arelle/FunctionXs.py +1 -1
  28. arelle/HtmlUtil.py +5 -4
  29. arelle/LeiUtil.py +63 -43
  30. arelle/LinkbaseType.py +94 -0
  31. arelle/LocalViewer.py +8 -2
  32. arelle/Locale.py +239 -79
  33. arelle/ModelDocument.py +180 -48
  34. arelle/ModelDtsObject.py +32 -27
  35. arelle/ModelFormulaObject.py +14 -6
  36. arelle/ModelInstanceObject.py +43 -14
  37. arelle/ModelManager.py +6 -1
  38. arelle/ModelObject.py +12 -8
  39. arelle/ModelObjectFactory.py +18 -2
  40. arelle/ModelRelationshipSet.py +46 -3
  41. arelle/ModelRenderingObject.py +899 -951
  42. arelle/ModelRssItem.py +2 -4
  43. arelle/ModelTestcaseObject.py +41 -4
  44. arelle/ModelValue.py +42 -14
  45. arelle/ModelVersReport.py +1 -1
  46. arelle/ModelXbrl.py +133 -273
  47. arelle/PackageManager.py +202 -176
  48. arelle/PluginManager.py +551 -239
  49. arelle/PluginUtils.py +54 -0
  50. arelle/PrototypeInstanceObject.py +53 -12
  51. arelle/PythonUtil.py +166 -25
  52. arelle/RuntimeOptions.py +32 -18
  53. arelle/SocketUtils.py +3 -0
  54. arelle/SystemInfo.py +1 -1
  55. arelle/TkTableWrapper.py +33 -5
  56. arelle/UITkTable.py +27 -21
  57. arelle/Updater.py +7 -3
  58. arelle/UrlUtil.py +6 -2
  59. arelle/Validate.py +596 -402
  60. arelle/ValidateDuplicateFacts.py +584 -0
  61. arelle/ValidateFileSource.py +38 -0
  62. arelle/ValidateFilingText.py +46 -29
  63. arelle/ValidateInfoset.py +104 -32
  64. arelle/ValidateUtr.py +31 -4
  65. arelle/ValidateXbrl.py +144 -120
  66. arelle/ValidateXbrlCalcs.py +51 -66
  67. arelle/ValidateXbrlDTS.py +98 -29
  68. arelle/Version.py +2 -7
  69. arelle/ViewFile.py +2 -0
  70. arelle/ViewFileDTS.py +7 -2
  71. arelle/ViewFileFactList.py +1 -1
  72. arelle/ViewFileFactTable.py +4 -4
  73. arelle/ViewFileRelationshipSet.py +36 -19
  74. arelle/ViewFileRenderedGrid.py +164 -753
  75. arelle/ViewFileRenderedLayout.py +174 -0
  76. arelle/ViewFileRenderedStructure.py +108 -0
  77. arelle/ViewWinDTS.py +4 -1
  78. arelle/ViewWinFactList.py +1 -1
  79. arelle/ViewWinFactTable.py +1 -1
  80. arelle/ViewWinRelationshipSet.py +14 -8
  81. arelle/ViewWinRenderedGrid.py +397 -287
  82. arelle/ViewWinRssFeed.py +4 -0
  83. arelle/ViewWinTree.py +15 -9
  84. arelle/ViewWinXml.py +1 -1
  85. arelle/WebCache.py +510 -272
  86. arelle/XbrlConst.py +202 -196
  87. arelle/XbrlUtil.py +2 -1
  88. arelle/XhtmlValidate.py +9 -23
  89. arelle/XmlUtil.py +24 -19
  90. arelle/XmlValidate.py +95 -67
  91. arelle/XmlValidateParticles.py +4 -4
  92. arelle/_version.py +33 -3
  93. arelle/api/Session.py +183 -0
  94. arelle/config/creationSoftwareNames.json +24 -10
  95. arelle/config/disclosuresystems.xml +1 -1
  96. arelle/config/disclosuresystems.xsd +3 -0
  97. arelle/config/sbr-text-formatting.xsd +737 -0
  98. arelle/conformance/CSVTestcaseLoader.py +102 -0
  99. arelle/formula/FactAspectsCache.py +10 -4
  100. arelle/formula/ValidateFormula.py +82 -194
  101. arelle/formula/XPathContext.py +42 -28
  102. arelle/formula/XPathParser.py +15 -9
  103. arelle/locale/ar_EG/LC_MESSAGES/ar_EG.po +3 -4
  104. arelle/locale/messages.pot +1 -2
  105. arelle/locale/ru/LC_MESSAGES/ru.po +2 -2
  106. arelle/logging/formatters/LogFormatter.py +47 -0
  107. arelle/logging/handlers/LogHandlerWithXml.py +94 -0
  108. arelle/logging/handlers/LogToBufferHandler.py +20 -0
  109. arelle/logging/handlers/LogToPrintHandler.py +41 -0
  110. arelle/logging/handlers/LogToXmlHandler.py +244 -0
  111. arelle/logging/handlers/StructuredMessageLogHandler.py +74 -0
  112. arelle/model/CommentBase.py +3 -0
  113. arelle/model/ElementBase.py +3 -0
  114. arelle/model/PIBase.py +3 -0
  115. arelle/model/__init__.py +3 -0
  116. arelle/oim/Load.py +2869 -0
  117. arelle/oim/Validate.py +155 -0
  118. arelle/oim/xml/Save.py +21 -0
  119. arelle/packages/PackageConst.py +7 -0
  120. arelle/packages/PackageType.py +13 -0
  121. arelle/packages/PackageUtils.py +22 -0
  122. arelle/packages/PackageValidation.py +200 -0
  123. arelle/packages/report/DetectReportPackage.py +13 -0
  124. arelle/packages/report/ReportPackage.py +265 -0
  125. arelle/packages/report/ReportPackageConst.py +79 -0
  126. arelle/packages/report/ReportPackageValidator.py +207 -0
  127. arelle/plugin/EdgarRendererAllReports.py +12 -12
  128. arelle/plugin/OimTaxonomy/ModelValueMore.py +15 -0
  129. arelle/plugin/OimTaxonomy/ValidateDTS.py +484 -0
  130. arelle/plugin/OimTaxonomy/ViewXbrlTxmyObj.py +239 -0
  131. arelle/plugin/OimTaxonomy/XbrlAbstract.py +16 -0
  132. arelle/plugin/OimTaxonomy/XbrlConcept.py +68 -0
  133. arelle/plugin/OimTaxonomy/XbrlConst.py +261 -0
  134. arelle/plugin/OimTaxonomy/XbrlCube.py +91 -0
  135. arelle/plugin/OimTaxonomy/XbrlDimension.py +38 -0
  136. arelle/plugin/OimTaxonomy/XbrlDts.py +152 -0
  137. arelle/plugin/OimTaxonomy/XbrlEntity.py +16 -0
  138. arelle/plugin/OimTaxonomy/XbrlGroup.py +22 -0
  139. arelle/plugin/OimTaxonomy/XbrlImportedTaxonomy.py +22 -0
  140. arelle/plugin/OimTaxonomy/XbrlLabel.py +31 -0
  141. arelle/plugin/OimTaxonomy/XbrlNetwork.py +100 -0
  142. arelle/plugin/OimTaxonomy/XbrlProperty.py +28 -0
  143. arelle/plugin/OimTaxonomy/XbrlReference.py +33 -0
  144. arelle/plugin/OimTaxonomy/XbrlReport.py +24 -0
  145. arelle/plugin/OimTaxonomy/XbrlTableTemplate.py +35 -0
  146. arelle/plugin/OimTaxonomy/XbrlTaxonomy.py +93 -0
  147. arelle/plugin/OimTaxonomy/XbrlTaxonomyObject.py +154 -0
  148. arelle/plugin/OimTaxonomy/XbrlTransform.py +17 -0
  149. arelle/plugin/OimTaxonomy/XbrlTypes.py +23 -0
  150. arelle/plugin/OimTaxonomy/XbrlUnit.py +17 -0
  151. arelle/plugin/OimTaxonomy/__init__.py +1037 -0
  152. arelle/plugin/OimTaxonomy/resources/iso4217.json +4479 -0
  153. arelle/plugin/OimTaxonomy/resources/oim-taxonomy-schema.json +935 -0
  154. arelle/plugin/OimTaxonomy/resources/ref.json +333 -0
  155. arelle/plugin/OimTaxonomy/resources/transform-types.json +2481 -0
  156. arelle/plugin/OimTaxonomy/resources/types.json +727 -0
  157. arelle/plugin/OimTaxonomy/resources/utr.json +3046 -0
  158. arelle/plugin/OimTaxonomy/resources/xbrlSpec.json +1082 -0
  159. arelle/plugin/formulaLoader.py +3 -3
  160. arelle/plugin/formulaSaver.py +4 -4
  161. arelle/plugin/inlineXbrlDocumentSet.py +331 -102
  162. arelle/plugin/internet/proxyNTLM/HTTPNtlmAuthHandler.py +1 -1
  163. arelle/plugin/loadFromExcel.py +13 -10
  164. arelle/plugin/loadFromOIM.py +16 -3096
  165. arelle/plugin/logging/saveMessages.py +1 -1
  166. arelle/plugin/saveHtmlEBAtables.py +293 -213
  167. arelle/plugin/saveLoadableOIM.py +652 -361
  168. arelle/plugin/saveOIMTaxonomy.py +311 -0
  169. arelle/plugin/streamingExtensions.py +1 -1
  170. arelle/plugin/systemInfo.py +46 -0
  171. arelle/plugin/transforms/tester.py +4 -4
  172. arelle/plugin/validate/CIPC/Const.py +18 -0
  173. arelle/plugin/validate/CIPC/__init__.py +34 -13
  174. arelle/plugin/validate/DBA/DisclosureSystems.py +12 -0
  175. arelle/plugin/validate/DBA/PluginValidationDataExtension.py +165 -0
  176. arelle/plugin/validate/DBA/ValidationPluginExtension.py +545 -0
  177. arelle/plugin/validate/DBA/__init__.py +49 -0
  178. arelle/plugin/validate/DBA/resources/config.xml +21 -0
  179. arelle/plugin/validate/DBA/rules/__init__.py +323 -0
  180. arelle/plugin/validate/DBA/rules/fr.py +1377 -0
  181. arelle/plugin/validate/DBA/rules/tc.py +45 -0
  182. arelle/plugin/validate/DBA/rules/th.py +172 -0
  183. arelle/plugin/validate/DBA/rules/tm.py +351 -0
  184. arelle/plugin/validate/DBA/rules/tr.py +373 -0
  185. arelle/plugin/validate/EBA/__init__.py +11 -12
  186. arelle/plugin/validate/EDINET/Constants.py +208 -0
  187. arelle/plugin/validate/EDINET/ContextRequirement.py +58 -0
  188. arelle/plugin/validate/EDINET/ControllerPluginData.py +298 -0
  189. arelle/plugin/validate/EDINET/CoverItemRequirements.py +42 -0
  190. arelle/plugin/validate/EDINET/DeiRequirements.py +118 -0
  191. arelle/plugin/validate/EDINET/DisclosureSystems.py +1 -0
  192. arelle/plugin/validate/EDINET/FilingFormat.py +275 -0
  193. arelle/plugin/validate/EDINET/FormType.py +134 -0
  194. arelle/plugin/validate/EDINET/ManifestInstance.py +236 -0
  195. arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +640 -0
  196. arelle/plugin/validate/EDINET/ReportFolderType.py +162 -0
  197. arelle/plugin/validate/EDINET/Statement.py +139 -0
  198. arelle/plugin/validate/EDINET/TableOfContentsBuilder.py +493 -0
  199. arelle/plugin/validate/EDINET/UploadContents.py +48 -0
  200. arelle/plugin/validate/EDINET/ValidationPluginExtension.py +64 -0
  201. arelle/plugin/validate/EDINET/__init__.py +109 -0
  202. arelle/plugin/validate/EDINET/resources/config.xml +22 -0
  203. arelle/plugin/validate/EDINET/resources/cover-item-requirements.json +793 -0
  204. arelle/plugin/validate/EDINET/resources/dei-requirements.csv +27 -0
  205. arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml +62 -0
  206. arelle/plugin/validate/EDINET/rules/contexts.py +547 -0
  207. arelle/plugin/validate/EDINET/rules/edinet.py +1991 -0
  208. arelle/plugin/validate/EDINET/rules/frta.py +301 -0
  209. arelle/plugin/validate/EDINET/rules/gfm.py +2394 -0
  210. arelle/plugin/validate/EDINET/rules/manifests.py +88 -0
  211. arelle/plugin/validate/EDINET/rules/upload.py +1370 -0
  212. arelle/plugin/validate/ESEF/Const.py +16 -75
  213. arelle/plugin/validate/ESEF/Dimensions.py +2 -2
  214. arelle/plugin/validate/ESEF/ESEF_2021/DTS.py +6 -1
  215. arelle/plugin/validate/ESEF/ESEF_2021/Image.py +7 -4
  216. arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +80 -57
  217. arelle/plugin/validate/ESEF/ESEF_Current/DTS.py +60 -22
  218. arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +272 -133
  219. arelle/plugin/validate/ESEF/Util.py +5 -19
  220. arelle/plugin/validate/ESEF/__init__.py +61 -46
  221. arelle/plugin/validate/ESEF/resources/authority-validations.json +120 -10
  222. arelle/plugin/validate/ESEF/resources/config.xml +44 -3
  223. arelle/plugin/validate/FERC/__init__.py +31 -18
  224. arelle/plugin/validate/FERC/config.xml +18 -18
  225. arelle/plugin/validate/NL/DisclosureSystems.py +19 -3
  226. arelle/plugin/validate/NL/PluginValidationDataExtension.py +710 -7
  227. arelle/plugin/validate/NL/ValidationPluginExtension.py +117 -12
  228. arelle/plugin/validate/NL/__init__.py +21 -10
  229. arelle/plugin/validate/NL/resources/config.xml +27 -6
  230. arelle/plugin/validate/NL/rules/br_kvk.py +81 -21
  231. arelle/plugin/validate/NL/rules/fg_nl.py +292 -0
  232. arelle/plugin/validate/NL/rules/fr_kvk.py +60 -7
  233. arelle/plugin/validate/NL/rules/fr_nl.py +705 -24
  234. arelle/plugin/validate/NL/rules/nl_kvk.py +2182 -0
  235. arelle/plugin/validate/ROS/DisclosureSystems.py +1 -0
  236. arelle/plugin/validate/ROS/PluginValidationDataExtension.py +109 -0
  237. arelle/plugin/validate/ROS/ValidationPluginExtension.py +27 -0
  238. arelle/plugin/validate/ROS/__init__.py +28 -341
  239. arelle/plugin/validate/ROS/resources/config.xml +20 -0
  240. arelle/plugin/validate/ROS/rules/__init__.py +56 -0
  241. arelle/plugin/validate/ROS/rules/ros.py +393 -0
  242. arelle/plugin/validate/UK/ValidateUK.py +1372 -0
  243. arelle/plugin/validate/{HMRC → UK}/__init__.py +195 -189
  244. arelle/plugin/validate/{HMRC → UK}/config.xml +1 -1
  245. arelle/plugin/xbrlDB/SqlDb.py +1 -1
  246. arelle/plugin/xbrlDB/XbrlOpenSqlDB.py +5 -5
  247. arelle/plugin/xbrlDB/XbrlPublicPostgresDB.py +1 -1
  248. arelle/plugin/xbrlDB/XbrlSemanticJsonDB.py +1 -1
  249. arelle/plugin/xbrlDB/XbrlSemanticRdfDB.py +1 -1
  250. arelle/plugin/xbrlDB/__init__.py +4 -1
  251. arelle/{RenderingEvaluator.py → rendering/RenderingEvaluator.py} +120 -99
  252. arelle/rendering/RenderingLayout.py +476 -0
  253. arelle/rendering/RenderingResolution.py +814 -0
  254. arelle/resources/cache/http/www.eurofiling.info/eu/fr/xbrl/ext/filing-indicators.xsd +20 -0
  255. arelle/resources/cache/http/www.w3.org/2001/03/xml.xsd +117 -0
  256. arelle/resources/cache/http/www.w3.org/2001/XMLSchema.xsd +2534 -0
  257. arelle/resources/cache/http/www.w3.org/2001/xml.xsd +287 -0
  258. arelle/resources/cache/http/www.xbrl.org/2003/xbrl-instance-2003-12-31.xsd +779 -0
  259. arelle/resources/cache/http/www.xbrl.org/2003/xbrl-linkbase-2003-12-31.xsd +486 -0
  260. arelle/resources/cache/http/www.xbrl.org/2003/xl-2003-12-31.xsd +251 -0
  261. arelle/resources/cache/http/www.xbrl.org/2003/xlink-2003-12-31.xsd +121 -0
  262. arelle/resources/cache/http/www.xbrl.org/2004/ref-2004-08-10.xsd +129 -0
  263. arelle/resources/cache/http/www.xbrl.org/2005/xbrldt-2005.xsd +53 -0
  264. arelle/resources/cache/http/www.xbrl.org/2006/ref-2006-02-27.xsd +120 -0
  265. arelle/resources/cache/http/www.xbrl.org/2006/xbrldi-2006.xsd +41 -0
  266. arelle/resources/cache/http/www.xbrl.org/2008/boolean-filter.xsd +51 -0
  267. arelle/resources/cache/http/www.xbrl.org/2008/concept-filter.xsd +127 -0
  268. arelle/resources/cache/http/www.xbrl.org/2008/conformance.xsd +116 -0
  269. arelle/resources/cache/http/www.xbrl.org/2008/conformanceFunction.xsd +51 -0
  270. arelle/resources/cache/http/www.xbrl.org/2008/consistency-assertion.xsd +66 -0
  271. arelle/resources/cache/http/www.xbrl.org/2008/dimension-filter.xsd +86 -0
  272. arelle/resources/cache/http/www.xbrl.org/2008/entity-filter.xsd +92 -0
  273. arelle/resources/cache/http/www.xbrl.org/2008/existence-assertion.xsd +44 -0
  274. arelle/resources/cache/http/www.xbrl.org/2008/formula.xsd +261 -0
  275. arelle/resources/cache/http/www.xbrl.org/2008/function.xsd +90 -0
  276. arelle/resources/cache/http/www.xbrl.org/2008/general-filter.xsd +38 -0
  277. arelle/resources/cache/http/www.xbrl.org/2008/generic-label.xsd +80 -0
  278. arelle/resources/cache/http/www.xbrl.org/2008/generic-link.xsd +81 -0
  279. arelle/resources/cache/http/www.xbrl.org/2008/generic-reference.xsd +63 -0
  280. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xbrl-instance-2003-12-31-ixmod.xsd +788 -0
  281. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xbrl-linkbase-2003-12-31-ixmod.xsd +488 -0
  282. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xl-2003-12-31.xsd +248 -0
  283. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xbrl/xlink-2003-12-31.xsd +117 -0
  284. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xframes-1.xsd +166 -0
  285. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-applet-1.xsd +66 -0
  286. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-attribs-1.xsd +72 -0
  287. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-base-1.xsd +36 -0
  288. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic-form-1.xsd +195 -0
  289. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic-table-1.xsd +169 -0
  290. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10-model-1.xsd +376 -0
  291. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10-module-redefines-1.xsd +61 -0
  292. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10-modules-1.xsd +259 -0
  293. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-basic10.xsd +98 -0
  294. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-bdo-1.xsd +78 -0
  295. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-blkphras-1.xsd +161 -0
  296. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-blkpres-1.xsd +37 -0
  297. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-blkstruct-1.xsd +49 -0
  298. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-charent-1.xsd +38 -0
  299. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-copyright-1.xsd +29 -0
  300. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-csismap-1.xsd +96 -0
  301. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-datatypes-1.xsd +128 -0
  302. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-edit-1.xsd +39 -0
  303. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-events-1.xsd +130 -0
  304. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-form-1.xsd +326 -0
  305. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-frames-1.xsd +113 -0
  306. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-framework-1.xsd +66 -0
  307. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-hypertext-1.xsd +47 -0
  308. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-iframe-1.xsd +68 -0
  309. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-image-1.xsd +45 -0
  310. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlphras-1.xsd +163 -0
  311. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlpres-1.xsd +39 -0
  312. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlstruct-1.xsd +50 -0
  313. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-inlstyle-1.xsd +27 -0
  314. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-legacy-1.xsd +97 -0
  315. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-link-1.xsd +45 -0
  316. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-list-1.xsd +99 -0
  317. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-meta-1.xsd +42 -0
  318. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-misc-1.xsd +441 -0
  319. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-nameident-1.xsd +63 -0
  320. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-notations-1.xsd +69 -0
  321. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-object-1.xsd +76 -0
  322. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-param-1.xsd +51 -0
  323. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-pres-1.xsd +51 -0
  324. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-ruby-1.xsd +171 -0
  325. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-ruby-basic-1.xsd +89 -0
  326. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-script-1.xsd +70 -0
  327. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-ssismap-1.xsd +43 -0
  328. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-struct-1.xsd +112 -0
  329. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-style-1.xsd +52 -0
  330. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-table-1.xsd +272 -0
  331. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-target-1.xsd +53 -0
  332. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml-text-1.xsd +67 -0
  333. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11-model-1.xsd +677 -0
  334. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11-module-redefines-1.xsd +335 -0
  335. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11-modules-1.xsd +528 -0
  336. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml11.xsd +104 -0
  337. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xhtml2.xsd +21 -0
  338. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-1.xsd +73 -0
  339. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-2.xsd +74 -0
  340. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-attribs-1.xsd +73 -0
  341. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-attribs-2.xsd +75 -0
  342. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-copyright-1.xsd +34 -0
  343. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-events-copyright-2.xsd +34 -0
  344. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml/xml-handlers-2.xsd +98 -0
  345. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0-definitions.xsd +225 -0
  346. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0-model.xsd +681 -0
  347. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0-modules.xsd +535 -0
  348. arelle/resources/cache/http/www.xbrl.org/2008/inlinexbrl/xhtml-inlinexbrl-1_0.xsd +40 -0
  349. arelle/resources/cache/http/www.xbrl.org/2008/match-filter.xsd +94 -0
  350. arelle/resources/cache/http/www.xbrl.org/2008/period-filter.xsd +86 -0
  351. arelle/resources/cache/http/www.xbrl.org/2008/registry.xsd +145 -0
  352. arelle/resources/cache/http/www.xbrl.org/2008/relative-filter.xsd +38 -0
  353. arelle/resources/cache/http/www.xbrl.org/2008/segment-scenario-filter.xsd +41 -0
  354. arelle/resources/cache/http/www.xbrl.org/2008/tuple-filter.xsd +83 -0
  355. arelle/resources/cache/http/www.xbrl.org/2008/unit-filter.xsd +58 -0
  356. arelle/resources/cache/http/www.xbrl.org/2008/validation.xsd +78 -0
  357. arelle/resources/cache/http/www.xbrl.org/2008/value-assertion.xsd +43 -0
  358. arelle/resources/cache/http/www.xbrl.org/2008/value-filter.xsd +43 -0
  359. arelle/resources/cache/http/www.xbrl.org/2008/variable.xsd +240 -0
  360. arelle/resources/cache/http/www.xbrl.org/2010/aspect-cover-filter.xsd +67 -0
  361. arelle/resources/cache/http/www.xbrl.org/2010/concept-relation-filter.xsd +108 -0
  362. arelle/resources/cache/http/www.xbrl.org/2010/custom-function-implementation.xsd +71 -0
  363. arelle/resources/cache/http/www.xbrl.org/2010/generic-message.xsd +68 -0
  364. arelle/resources/cache/http/www.xbrl.org/2010/validation-message.xsd +50 -0
  365. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xbrl-instance-2003-12-31-ixmod.xsd +788 -0
  366. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xbrl-linkbase-2003-12-31-ixmod.xsd +488 -0
  367. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xl-2003-12-31.xsd +248 -0
  368. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xbrl/xlink-2003-12-31.xsd +117 -0
  369. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xframes-1.xsd +166 -0
  370. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-applet-1.xsd +66 -0
  371. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-attribs-1.xsd +72 -0
  372. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-base-1.xsd +36 -0
  373. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic-form-1.xsd +195 -0
  374. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic-table-1.xsd +169 -0
  375. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10-model-1.xsd +376 -0
  376. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10-module-redefines-1.xsd +61 -0
  377. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10-modules-1.xsd +259 -0
  378. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-basic10.xsd +98 -0
  379. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-bdo-1.xsd +78 -0
  380. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-blkphras-1.xsd +161 -0
  381. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-blkpres-1.xsd +37 -0
  382. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-blkstruct-1.xsd +49 -0
  383. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-charent-1.xsd +38 -0
  384. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-copyright-1.xsd +29 -0
  385. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-csismap-1.xsd +96 -0
  386. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-datatypes-1.xsd +128 -0
  387. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-edit-1.xsd +39 -0
  388. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-events-1.xsd +130 -0
  389. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-form-1.xsd +326 -0
  390. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-frames-1.xsd +113 -0
  391. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-framework-1.xsd +66 -0
  392. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-hypertext-1.xsd +47 -0
  393. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-iframe-1.xsd +68 -0
  394. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-image-1.xsd +45 -0
  395. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlphras-1.xsd +163 -0
  396. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlpres-1.xsd +39 -0
  397. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlstruct-1.xsd +50 -0
  398. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-inlstyle-1.xsd +27 -0
  399. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-legacy-1.xsd +97 -0
  400. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-link-1.xsd +45 -0
  401. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-list-1.xsd +99 -0
  402. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-meta-1.xsd +42 -0
  403. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-misc-1.xsd +441 -0
  404. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-nameident-1.xsd +63 -0
  405. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-notations-1.xsd +69 -0
  406. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-object-1.xsd +76 -0
  407. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-param-1.xsd +51 -0
  408. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-pres-1.xsd +51 -0
  409. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-ruby-1.xsd +171 -0
  410. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-ruby-basic-1.xsd +89 -0
  411. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-script-1.xsd +70 -0
  412. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-ssismap-1.xsd +43 -0
  413. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-struct-1.xsd +112 -0
  414. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-style-1.xsd +52 -0
  415. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-table-1.xsd +272 -0
  416. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-target-1.xsd +53 -0
  417. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml-text-1.xsd +67 -0
  418. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11-model-1.xsd +677 -0
  419. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11-module-redefines-1.xsd +335 -0
  420. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11-modules-1.xsd +528 -0
  421. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml11.xsd +104 -0
  422. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xhtml2.xsd +21 -0
  423. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-1.xsd +73 -0
  424. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-2.xsd +74 -0
  425. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-attribs-1.xsd +73 -0
  426. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-attribs-2.xsd +75 -0
  427. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-copyright-1.xsd +34 -0
  428. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-events-copyright-2.xsd +34 -0
  429. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml/xml-handlers-2.xsd +98 -0
  430. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1-definitions.xsd +252 -0
  431. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1-model.xsd +681 -0
  432. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1-modules.xsd +535 -0
  433. arelle/resources/cache/http/www.xbrl.org/2013/inlineXBRL/xhtml-inlinexbrl-1_1.xsd +40 -0
  434. arelle/resources/cache/http/www.xbrl.org/2013/match-filter.xsd +94 -0
  435. arelle/resources/cache/http/www.xbrl.org/2013/preferred-label.xsd +12 -0
  436. arelle/resources/cache/http/www.xbrl.org/2013/versioning-base.xsd +142 -0
  437. arelle/resources/cache/http/www.xbrl.org/2013/versioning-concept-details.xsd +143 -0
  438. arelle/resources/cache/http/www.xbrl.org/2013/versioning-concept-use.xsd +64 -0
  439. arelle/resources/cache/http/www.xbrl.org/2013/versioning-dimensions.xsd +162 -0
  440. arelle/resources/cache/http/www.xbrl.org/2014/extensible-enumerations.xsd +27 -0
  441. arelle/resources/cache/http/www.xbrl.org/2014/table.xsd +356 -0
  442. arelle/resources/cache/http/www.xbrl.org/2014/tablemodel.xsd +156 -0
  443. arelle/resources/cache/http/www.xbrl.org/2016/assertion-severity.xsd +29 -0
  444. arelle/resources/cache/http/www.xbrl.org/2016/severities.xml +21 -0
  445. arelle/resources/cache/http/www.xbrl.org/2016/taxonomy-package-catalog.xsd +38 -0
  446. arelle/resources/cache/http/www.xbrl.org/2016/taxonomy-package.xsd +154 -0
  447. arelle/resources/cache/http/www.xbrl.org/2022/assertion-severity.xsd +43 -0
  448. arelle/resources/cache/http/www.xbrl.org/2022/severities.xml +21 -0
  449. arelle/resources/cache/http/www.xbrl.org/dtr/type/nonNumeric-2009-12-16.xsd +76 -0
  450. arelle/resources/cache/http/www.xbrl.org/dtr/type/numeric-2009-12-16.xsd +78 -0
  451. arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/accounting-arcrole-2023-01-04.xsd +39 -0
  452. arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/deprecated-2009-12-16.xsd +29 -0
  453. arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/esma-arcrole-2018-11-21.xsd +14 -0
  454. arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/factExplanatory-2009-12-16.xsd +13 -0
  455. arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/jpfr-arcrole-2007-11-07.xsd +27 -0
  456. arelle/resources/cache/http/www.xbrl.org/lrr/arcrole/parent-child-2013-09-19.xsd +12 -0
  457. arelle/resources/cache/http/www.xbrl.org/lrr/role/deprecated-2009-12-16.xsd +17 -0
  458. arelle/resources/cache/http/www.xbrl.org/lrr/role/jpfr-role-2007-11-07.xsd +47 -0
  459. arelle/resources/cache/http/www.xbrl.org/lrr/role/negated-2008-03-31.xsd +30 -0
  460. arelle/resources/cache/http/www.xbrl.org/lrr/role/negated-2009-12-16.xsd +33 -0
  461. arelle/resources/cache/http/www.xbrl.org/lrr/role/negative-2009-12-16.xsd +25 -0
  462. arelle/resources/cache/http/www.xbrl.org/lrr/role/net-2009-12-16.xsd +13 -0
  463. arelle/resources/cache/http/www.xbrl.org/lrr/role/positive-2009-12-16.xsd +25 -0
  464. arelle/resources/cache/http/www.xbrl.org/lrr/role/property-2022-09-28.xsd +15 -0
  465. arelle/resources/cache/http/www.xbrl.org/lrr/role/reference-2009-12-16.xsd +21 -0
  466. arelle/resources/cache/http/www.xbrl.org/lrr/role/restated-2006-02-21.xsd +19 -0
  467. arelle/resources/cache/http/www.xbrl.org/utr/2012-01-30/utr.xml +4543 -0
  468. arelle/resources/cache/http/www.xbrl.org/utr/2012-10-31/utr.xml +4510 -0
  469. arelle/resources/cache/http/www.xbrl.org/utr/2012-11-30/utr.xml +4567 -0
  470. arelle/resources/cache/http/www.xbrl.org/utr/2013-02-28/utr.xml +5180 -0
  471. arelle/resources/cache/http/www.xbrl.org/utr/2013-05-17/utr.xml +5349 -0
  472. arelle/resources/cache/http/www.xbrl.org/utr/2016-08-10/utr.xml +5363 -0
  473. arelle/resources/cache/http/www.xbrl.org/utr/2017-07-12/utr.xml +6164 -0
  474. arelle/resources/cache/http/www.xbrl.org/utr/2021-02-16/utr.xml +6370 -0
  475. arelle/resources/cache/http/www.xbrl.org/utr/2021-12-08/utr.xml +6493 -0
  476. arelle/resources/cache/http/www.xbrl.org/utr/2022-02-16/utr.xml +6551 -0
  477. arelle/resources/cache/http/www.xbrl.org/utr/2022-07-20/utr.xml +6638 -0
  478. arelle/resources/cache/http/www.xbrl.org/utr/2023-12-20/utr.xml +6680 -0
  479. arelle/resources/cache/http/www.xbrl.org/utr/2024-01-31/utr.xml +6680 -0
  480. arelle/resources/cache/http/www.xbrl.org/utr/2024-10-22/utr.xml +6693 -0
  481. arelle/resources/cache/http/www.xbrl.org/utr/utr.xml +6693 -0
  482. arelle/resources/cache/https/www.xbrl.org/2005/conformance.xsd +87 -0
  483. arelle/resources/cache/https/www.xbrl.org/2020/extensible-enumerations-2.0.xsd +51 -0
  484. arelle/resources/cache/https/www.xbrl.org/2023/calculation-1.1.xsd +27 -0
  485. arelle/resources/cache/https/www.xbrl.org/dtr/type/2020-01-21/types.xsd +812 -0
  486. arelle/resources/cache/https/www.xbrl.org/dtr/type/2022-03-31/types.xsd +847 -0
  487. arelle/resources/cache/https/www.xbrl.org/dtr/type/2024-01-31/types.xsd +897 -0
  488. arelle/resources/cache/https/www.xbrl.org/taxonomy/int/filing-indicators/REC/2021-02-03/filing-indicators-def.xml +68 -0
  489. arelle/resources/cache/https/www.xbrl.org/taxonomy/int/filing-indicators/REC/2021-02-03/filing-indicators-label.xml +84 -0
  490. arelle/resources/cache/https/www.xbrl.org/taxonomy/int/filing-indicators/REC/2021-02-03/filing-indicators.xsd +67 -0
  491. arelle/resources/libs/Tktable2.11/linux-x86_64/README.txt +149 -0
  492. arelle/resources/libs/Tktable2.11/linux-x86_64/html/tkTable.html +2039 -0
  493. arelle/resources/libs/Tktable2.11/linux-x86_64/libTktable2.11.so +0 -0
  494. arelle/resources/libs/Tktable2.11/linux-x86_64/license.txt +51 -0
  495. arelle/resources/libs/Tktable2.11/linux-x86_64/pkgIndex.tcl +3 -0
  496. arelle/resources/libs/Tktable2.11/linux-x86_64/tkTable.tcl +825 -0
  497. arelle/resources/libs/Tktable2.11/linux-x86_64/tktable.py +674 -0
  498. arelle/resources/libs/Tktable2.11/macos-arm64/README.txt +149 -0
  499. arelle/resources/libs/Tktable2.11/macos-arm64/html/tkTable.html +2039 -0
  500. arelle/resources/libs/Tktable2.11/macos-arm64/libTktable2.11.dylib +0 -0
  501. arelle/resources/libs/Tktable2.11/macos-arm64/license.txt +51 -0
  502. arelle/resources/libs/Tktable2.11/macos-arm64/pkgIndex.tcl +2 -0
  503. arelle/resources/libs/Tktable2.11/macos-arm64/tkTable.tcl +825 -0
  504. arelle/resources/libs/Tktable2.11/macos-arm64/tktable.py +674 -0
  505. arelle/resources/libs/Tktable2.11/macos-x86_64/README.txt +149 -0
  506. arelle/resources/libs/Tktable2.11/macos-x86_64/html/tkTable.html +2039 -0
  507. arelle/resources/libs/Tktable2.11/macos-x86_64/libTktable2.11.dylib +0 -0
  508. arelle/resources/libs/Tktable2.11/macos-x86_64/license.txt +51 -0
  509. arelle/resources/libs/Tktable2.11/macos-x86_64/pkgIndex.tcl +2 -0
  510. arelle/resources/libs/Tktable2.11/macos-x86_64/tkTable.tcl +825 -0
  511. arelle/resources/libs/Tktable2.11/macos-x86_64/tktable.py +674 -0
  512. arelle/resources/libs/Tktable2.11/win-x86_64/Tktable.dll +0 -0
  513. arelle/resources/libs/Tktable2.11/win-x86_64/pkgIndex.tcl +2 -0
  514. arelle/resources/libs/Tktable2.11/win-x86_64/tkTable.tcl +825 -0
  515. arelle/typing.py +9 -4
  516. arelle/utils/Contexts.py +38 -0
  517. arelle/utils/EntryPointDetection.py +139 -0
  518. arelle/utils/Equivalence.py +22 -0
  519. arelle/utils/{validate/PluginValidationData.py → PluginData.py} +5 -2
  520. arelle/utils/PluginHooks.py +256 -5
  521. arelle/utils/Units.py +36 -0
  522. arelle/utils/validate/Decorator.py +5 -3
  523. arelle/utils/validate/DetectScriptsInXhtml.py +99 -0
  524. arelle/utils/validate/ESEFImage.py +283 -0
  525. arelle/utils/validate/Validation.py +9 -1
  526. arelle/utils/validate/ValidationPlugin.py +74 -11
  527. arelle/utils/validate/ValidationUtil.py +55 -0
  528. arelle/webserver/bottle.py +5 -4424
  529. {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info}/METADATA +51 -42
  530. arelle_release-2.37.71.dist-info/RECORD +697 -0
  531. {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info}/WHEEL +1 -1
  532. {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info/licenses}/LICENSE.md +1 -4
  533. {arelle_release-2.17.1.dist-info → arelle_release-2.37.71.dist-info}/top_level.txt +0 -1
  534. arelle/DialogOpenTaxonomyPackage.py +0 -1
  535. arelle/RenderingResolver.py +0 -624
  536. arelle/examples/.pydevproject +0 -5
  537. arelle/examples/CustomLogger.py +0 -43
  538. arelle/examples/LoadEFMvalidate.py +0 -32
  539. arelle/examples/LoadSavePreLbCsv.py +0 -26
  540. arelle/examples/LoadValidate.cs +0 -31
  541. arelle/examples/LoadValidate.py +0 -36
  542. arelle/examples/LoadValidateCmdLine.java +0 -69
  543. arelle/examples/LoadValidatePostedZip.java +0 -57
  544. arelle/examples/LoadValidateWebService.java +0 -34
  545. arelle/examples/SaveTableToExelle.py +0 -140
  546. arelle/examples/TR3toTR4.py +0 -88
  547. arelle/examples/plugin/bigInstance.py +0 -394
  548. arelle/examples/plugin/cmdWebServerExtension.py +0 -42
  549. arelle/examples/plugin/crashTest.py +0 -38
  550. arelle/examples/plugin/formulaSuiteConverter.py +0 -212
  551. arelle/examples/plugin/functionsCustom.py +0 -59
  552. arelle/examples/plugin/hello_dolly.py +0 -64
  553. arelle/examples/plugin/hello_i18n.pot +0 -26
  554. arelle/examples/plugin/hello_i18n.py +0 -32
  555. arelle/examples/plugin/importTestChild1.py +0 -21
  556. arelle/examples/plugin/importTestChild2.py +0 -22
  557. arelle/examples/plugin/importTestGrandchild1.py +0 -21
  558. arelle/examples/plugin/importTestGrandchild2.py +0 -21
  559. arelle/examples/plugin/importTestImported1.py +0 -23
  560. arelle/examples/plugin/importTestImported11.py +0 -22
  561. arelle/examples/plugin/importTestParent.py +0 -48
  562. arelle/examples/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
  563. arelle/examples/plugin/packagedImportTest/__init__.py +0 -47
  564. arelle/examples/plugin/packagedImportTest/importTestChild1.py +0 -21
  565. arelle/examples/plugin/packagedImportTest/importTestChild2.py +0 -22
  566. arelle/examples/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
  567. arelle/examples/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
  568. arelle/examples/plugin/packagedImportTest/importTestImported1.py +0 -24
  569. arelle/examples/plugin/packagedImportTest/importTestImported11.py +0 -21
  570. arelle/examples/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
  571. arelle/examples/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
  572. arelle/examples/plugin/sakaCalendar.py +0 -215
  573. arelle/examples/plugin/saveInstanceInfoset.py +0 -121
  574. arelle/examples/plugin/streamingExtensions.py +0 -335
  575. arelle/examples/plugin/testcaseCalc11ValidateSetup.py +0 -32
  576. arelle/examples/plugin/testcaseIxExpectedHtmlFixup.py +0 -45
  577. arelle/examples/plugin/updateTableLB.py +0 -242
  578. arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
  579. arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
  580. arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -49
  581. arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
  582. arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
  583. arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -109
  584. arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -58
  585. arelle/examples/plugin/validateSchemaLxml.py +0 -156
  586. arelle/examples/plugin/validateTableInfoset.py +0 -52
  587. arelle/examples/us-gaap-dei-docType-extraction-frm.xml +0 -90
  588. arelle/examples/us-gaap-dei-ratio-cash-frm.xml +0 -150
  589. arelle/plugin/functionsXmlCreation.py +0 -106
  590. arelle/plugin/instanceInfo.py +0 -306
  591. arelle/plugin/loadFromOIM-2018.py +0 -1282
  592. arelle/plugin/objectmaker.py +0 -285
  593. arelle/plugin/sphinx/FormulaGenerator.py +0 -823
  594. arelle/plugin/sphinx/SphinxContext.py +0 -404
  595. arelle/plugin/sphinx/SphinxEvaluator.py +0 -783
  596. arelle/plugin/sphinx/SphinxMethods.py +0 -1287
  597. arelle/plugin/sphinx/SphinxParser.py +0 -1093
  598. arelle/plugin/sphinx/SphinxValidator.py +0 -163
  599. arelle/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
  600. arelle/plugin/sphinx/__init__.py +0 -285
  601. arelle/plugin/transforms/SEC/__init__.py +0 -308
  602. arelle/plugin/transforms/SEC/conf/README.md +0 -39
  603. arelle/plugin/transforms/SEC/conf/extractTestcase.sh +0 -2
  604. arelle/plugin/transforms/SEC/conf/extractTestcase.xsl +0 -109
  605. arelle/plugin/transforms/SEC/conf/runIxtSecTests.sh +0 -16
  606. arelle/plugin/transforms/SEC/conf/saxon9.jar +0 -0
  607. arelle/plugin/transforms/SEC/conf/testcase.xml +0 -7117
  608. arelle/plugin/transforms/SEC/conf/tests.xml +0 -848
  609. arelle/plugin/transforms/SEC/text2num.py +0 -110
  610. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-boolballotbox.xml +0 -66
  611. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-countrynameen.xml +0 -65
  612. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-datequarterend.xml +0 -66
  613. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durday.xml +0 -61
  614. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durhour.xml +0 -69
  615. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durmonth.xml +0 -71
  616. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durweek.xml +0 -70
  617. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-durwordsen.xml +0 -56
  618. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-duryear.xml +0 -64
  619. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-edgarprovcountryen.xml +0 -65
  620. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-entityfilercategoryen.xml +0 -63
  621. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-exchnameen.xml +0 -86
  622. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-numwordsen.xml +0 -55
  623. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-stateprovnameen.xml +0 -64
  624. arelle/plugin/transforms/SEC/transformationRegistry/registry/ixt-sec-yesnoballotbox.xml +0 -66
  625. arelle/plugin/transforms/SEC/transformationRegistry/registry/transform-registry.xml +0 -314
  626. arelle/plugin/transforms/SEC/transformationRegistry/schema/inlinexbrl-sec-transformation.xsd +0 -418
  627. arelle/plugin/validate/EFM/Consts.py +0 -362
  628. arelle/plugin/validate/EFM/DTS.py +0 -569
  629. arelle/plugin/validate/EFM/Dimensions.py +0 -264
  630. arelle/plugin/validate/EFM/Document.py +0 -206
  631. arelle/plugin/validate/EFM/Filing.py +0 -4383
  632. arelle/plugin/validate/EFM/IsolateSeparateIXDSes.py +0 -19
  633. arelle/plugin/validate/EFM/MessageNumericId.py +0 -142
  634. arelle/plugin/validate/EFM/PreCalAlignment.py +0 -324
  635. arelle/plugin/validate/EFM/Util.py +0 -688
  636. arelle/plugin/validate/EFM/__init__.py +0 -843
  637. arelle/plugin/validate/EFM/config.xml +0 -333
  638. arelle/plugin/validate/EFM/resources/README.md +0 -57
  639. arelle/plugin/validate/EFM/resources/axiswarnings.json +0 -27
  640. arelle/plugin/validate/EFM/resources/cef-deprecated-concepts.json +0 -8
  641. arelle/plugin/validate/EFM/resources/country-deprecated-concepts.json +0 -19
  642. arelle/plugin/validate/EFM/resources/currency-deprecated-concepts.json +0 -7
  643. arelle/plugin/validate/EFM/resources/dei-deprecated-concepts.json +0 -8
  644. arelle/plugin/validate/EFM/resources/dei-validations.json +0 -2884
  645. arelle/plugin/validate/EFM/resources/dqc-us-rules.json +0 -1389
  646. arelle/plugin/validate/EFM/resources/ecd-deprecated-concepts.json +0 -1
  647. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-16-4.xml +0 -115
  648. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-0-4.xml +0 -300
  649. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-1.xml +0 -304
  650. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-2.xml +0 -290
  651. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-17-3-1.xml +0 -294
  652. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-18.1.xml +0 -549
  653. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-18.2.xml +0 -506
  654. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-18.3.xml +0 -496
  655. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-19-1.xml +0 -662
  656. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-19-2.xml +0 -3761
  657. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-19-3.xml +0 -3577
  658. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-20-1.xml +0 -4020
  659. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-20-2.xml +0 -3320
  660. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-20-3.xml +0 -2998
  661. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2012.xml +0 -957
  662. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2013.xml +0 -982
  663. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2014.xml +0 -1001
  664. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2015.xml +0 -1076
  665. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-2016.xml +0 -120
  666. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-1.xml +0 -3329
  667. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-2.xml +0 -1230
  668. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-3.xml +0 -1113
  669. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-21-4.xml +0 -1321
  670. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-1-preview.xml +0 -1721
  671. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-1.xml +0 -1841
  672. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-2-2.xml +0 -1450
  673. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-2.xml +0 -1429
  674. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-22-4.xml +0 -1527
  675. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-1-1.xml +0 -2049
  676. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-1.xml +0 -2065
  677. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-2.xml +0 -1674
  678. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-23-3.xml +0 -1715
  679. arelle/plugin/validate/EFM/resources/edgartaxonomies/edgartaxonomies-all-years.xml +0 -10671
  680. arelle/plugin/validate/EFM/resources/edgartaxonomies/erxl.xsd +0 -69
  681. arelle/plugin/validate/EFM/resources/edgartaxonomies/extendedtaxonomies-all-years.xml +0 -18
  682. arelle/plugin/validate/EFM/resources/edgartaxonomies/ifrs-taxonomies.xml +0 -3126
  683. arelle/plugin/validate/EFM/resources/edgartaxonomies/ifrstaxonomies-all-years.xml +0 -22
  684. arelle/plugin/validate/EFM/resources/ex26-validations.json +0 -255
  685. arelle/plugin/validate/EFM/resources/exch-deprecated-concepts.json +0 -163
  686. arelle/plugin/validate/EFM/resources/ifrs-full-deprecated-concepts.json +0 -204
  687. arelle/plugin/validate/EFM/resources/invest-deprecated-concepts.json +0 -99
  688. arelle/plugin/validate/EFM/resources/ixbrl-transform-registries.json +0 -16
  689. arelle/plugin/validate/EFM/resources/naics-deprecated-concepts.json +0 -295
  690. arelle/plugin/validate/EFM/resources/oef-deprecated-concepts.json +0 -59
  691. arelle/plugin/validate/EFM/resources/other-standard-taxonomies.json +0 -12
  692. arelle/plugin/validate/EFM/resources/rr-deprecated-concepts.json +0 -14
  693. arelle/plugin/validate/EFM/resources/rxp-deprecated-concepts.json +0 -1
  694. arelle/plugin/validate/EFM/resources/shr-deprecated-concepts.json +0 -1
  695. arelle/plugin/validate/EFM/resources/sic-deprecated-concepts.json +0 -1
  696. arelle/plugin/validate/EFM/resources/signwarnings.json +0 -112
  697. arelle/plugin/validate/EFM/resources/srt-deprecated-concepts.json +0 -1
  698. arelle/plugin/validate/EFM/resources/stpr-deprecated-concepts.json +0 -1
  699. arelle/plugin/validate/EFM/resources/taxonomy-compatibility.json +0 -79
  700. arelle/plugin/validate/EFM/resources/us-gaap-deprecated-concepts.json +0 -2886
  701. arelle/plugin/validate/EFM/resources/us-gaap-rels-2020.json +0 -29068
  702. arelle/plugin/validate/EFM/resources/us-gaap-rels-2021.json +0 -29598
  703. arelle/plugin/validate/EFM/resources/us-gaap-rels-2022.json +0 -29141
  704. arelle/plugin/validate/EFM/resources/us-gaap-rels-2023.json +0 -29400
  705. arelle/plugin/validate/EFM/resources/vip-deprecated-concepts.json +0 -1
  706. arelle/plugin/validate/EFM/tools/CheckTxmyRefs.py +0 -180
  707. arelle/plugin/validate/EFM-htm/Const.py +0 -205
  708. arelle/plugin/validate/EFM-htm/__init__.py +0 -217
  709. arelle/plugin/validate/EFM-htm/config.xml +0 -17
  710. arelle/plugin/validate/EFM-htm/resources/efm-htm.dtd +0 -664
  711. arelle/plugin/validate/ESEF/ESEF_Current/Image.py +0 -199
  712. arelle/plugin/validate/ESEF_2022/__init__.py +0 -47
  713. arelle/plugin/validate/GFM/__init__.py +0 -59
  714. arelle/plugin/validate/GFM/config.xml +0 -82
  715. arelle/plugin/validate/SBRnl/CustomLoader.py +0 -19
  716. arelle/plugin/validate/SBRnl/DTS.py +0 -305
  717. arelle/plugin/validate/SBRnl/Dimensions.py +0 -357
  718. arelle/plugin/validate/SBRnl/Document.py +0 -799
  719. arelle/plugin/validate/SBRnl/Filing.py +0 -467
  720. arelle/plugin/validate/SBRnl/__init__.py +0 -75
  721. arelle/plugin/validate/SBRnl/config.xml +0 -26
  722. arelle/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
  723. arelle/plugin/validate/USBestPractices.py +0 -570
  724. arelle/plugin/validate/USCorpAction.py +0 -557
  725. arelle/plugin/validate/USSecTagging.py +0 -337
  726. arelle/plugin/validate/XDC/__init__.py +0 -77
  727. arelle/plugin/validate/XDC/config.xml +0 -20
  728. arelle/plugin/validate/XFsyntax/__init__.py +0 -64
  729. arelle/plugin/validate/XFsyntax/xf.py +0 -2227
  730. arelle/plugin/validate/__init__.py +0 -20
  731. arelle/plugin/validate/calc2.py +0 -536
  732. arelle/plugin/validateSBRnl.py +0 -530
  733. arelle/scripts-macOS/startWebServer.command +0 -3
  734. arelle/scripts-unix/startWebServer.sh +0 -1
  735. arelle/scripts-windows/startWebServer.bat +0 -5
  736. arelle_release-2.17.1.dist-info/RECORD +0 -676
  737. tests/__init__.py +0 -0
  738. tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/ArelleGUITest.csproj +0 -30
  739. tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/Tests.cs +0 -381
  740. tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/Usings.cs +0 -1
  741. tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest.sln +0 -31
  742. tests/integration_tests/ui_tests/resources/workiva.zip +0 -0
  743. tests/integration_tests/validation/README.md +0 -50
  744. tests/integration_tests/validation/conformance_suite_config.py +0 -59
  745. tests/integration_tests/validation/conformance_suite_configs.py +0 -63
  746. tests/integration_tests/validation/conformance_suite_configurations/efm_current.py +0 -261
  747. tests/integration_tests/validation/conformance_suite_configurations/esef_ixbrl_2021.py +0 -85
  748. tests/integration_tests/validation/conformance_suite_configurations/esef_ixbrl_2022.py +0 -90
  749. tests/integration_tests/validation/conformance_suite_configurations/esef_xhtml_2021.py +0 -15
  750. tests/integration_tests/validation/conformance_suite_configurations/esef_xhtml_2022.py +0 -15
  751. tests/integration_tests/validation/conformance_suite_configurations/nl_nt16.py +0 -13
  752. tests/integration_tests/validation/conformance_suite_configurations/nl_nt17.py +0 -13
  753. tests/integration_tests/validation/conformance_suite_configurations/nl_nt18.py +0 -13
  754. tests/integration_tests/validation/conformance_suite_configurations/xbrl_2_1.py +0 -34
  755. tests/integration_tests/validation/conformance_suite_configurations/xbrl_calculations_1_1.py +0 -40
  756. tests/integration_tests/validation/conformance_suite_configurations/xbrl_dimensions_1_0.py +0 -28
  757. tests/integration_tests/validation/conformance_suite_configurations/xbrl_extensible_enumerations_1_0.py +0 -10
  758. tests/integration_tests/validation/conformance_suite_configurations/xbrl_extensible_enumerations_2_0.py +0 -10
  759. tests/integration_tests/validation/conformance_suite_configurations/xbrl_formula_1_0.py +0 -9
  760. tests/integration_tests/validation/conformance_suite_configurations/xbrl_formula_1_0_assertion_severity_2_0.py +0 -9
  761. tests/integration_tests/validation/conformance_suite_configurations/xbrl_formula_1_0_function_registry.py +0 -14
  762. tests/integration_tests/validation/conformance_suite_configurations/xbrl_ixbrl_1_1.py +0 -22
  763. tests/integration_tests/validation/conformance_suite_configurations/xbrl_link_role_registry_1_0.py +0 -11
  764. tests/integration_tests/validation/conformance_suite_configurations/xbrl_oim_1_0.py +0 -19
  765. tests/integration_tests/validation/conformance_suite_configurations/xbrl_table_linkbase_1_0.py +0 -296
  766. tests/integration_tests/validation/conformance_suite_configurations/xbrl_taxonomy_packages_1_0.py +0 -13
  767. tests/integration_tests/validation/conformance_suite_configurations/xbrl_transformation_registry_3.py +0 -13
  768. tests/integration_tests/validation/conformance_suite_configurations/xbrl_transformation_registry_4.py +0 -13
  769. tests/integration_tests/validation/conformance_suite_configurations/xbrl_transformation_registry_5.py +0 -13
  770. tests/integration_tests/validation/conformance_suite_configurations/xbrl_utr_malformed_1_0.py +0 -31
  771. tests/integration_tests/validation/conformance_suite_configurations/xbrl_utr_registry_1_0.py +0 -22
  772. tests/integration_tests/validation/conformance_suite_configurations/xbrl_utr_structure_1_0.py +0 -18
  773. tests/integration_tests/validation/conftest.py +0 -24
  774. tests/integration_tests/validation/discover_tests.py +0 -22
  775. tests/integration_tests/validation/download_conformance_suites.py +0 -47
  776. tests/integration_tests/validation/run_conformance_suites.py +0 -175
  777. tests/integration_tests/validation/test_conformance_suites.py +0 -14
  778. tests/integration_tests/validation/validation_util.py +0 -271
  779. tests/resources/conformance_suites/nl_nt16/br_kvk/2-04-invalid-period.xbrl +0 -63
  780. tests/resources/conformance_suites/nl_nt16/br_kvk/2-04-testcase.xml +0 -22
  781. tests/resources/conformance_suites/nl_nt16/br_kvk/3-01-missing.xbrl +0 -53
  782. tests/resources/conformance_suites/nl_nt16/br_kvk/3-01-multiple.xbrl +0 -60
  783. tests/resources/conformance_suites/nl_nt16/br_kvk/3-01-testcase.xml +0 -33
  784. tests/resources/conformance_suites/nl_nt16/br_kvk/4-07-invalid.xbrl +0 -57
  785. tests/resources/conformance_suites/nl_nt16/br_kvk/4-07-testcase.xml +0 -22
  786. tests/resources/conformance_suites/nl_nt16/br_kvk/4-10-invalid.xbrl +0 -58
  787. tests/resources/conformance_suites/nl_nt16/br_kvk/4-10-testcase.xml +0 -22
  788. tests/resources/conformance_suites/nl_nt16/br_kvk/4-12-invalid.xbrl +0 -63
  789. tests/resources/conformance_suites/nl_nt16/br_kvk/4-12-testcase.xml +0 -22
  790. tests/resources/conformance_suites/nl_nt16/br_kvk/4-16-missing.xbrl +0 -61
  791. tests/resources/conformance_suites/nl_nt16/br_kvk/4-16-testcase.xml +0 -22
  792. tests/resources/conformance_suites/nl_nt16/br_kvk/4-20-invalid-date.xbrl +0 -89
  793. tests/resources/conformance_suites/nl_nt16/br_kvk/4-20-testcase.xml +0 -22
  794. tests/resources/conformance_suites/nl_nt16/fr_kvk/1-01-invalid-file-extension.xml +0 -56
  795. tests/resources/conformance_suites/nl_nt16/fr_kvk/1-01-testcase.xml +0 -22
  796. tests/resources/conformance_suites/nl_nt16/fr_kvk/2-01-invalid-lang.xbrl +0 -56
  797. tests/resources/conformance_suites/nl_nt16/fr_kvk/2-01-missing-lang.xbrl +0 -55
  798. tests/resources/conformance_suites/nl_nt16/fr_kvk/2-01-testcase.xml +0 -33
  799. tests/resources/conformance_suites/nl_nt16/fr_kvk/2-03-entrypoint.xsd +0 -6
  800. tests/resources/conformance_suites/nl_nt16/fr_kvk/2-03-invalid-entrypoint.xbrl +0 -63
  801. tests/resources/conformance_suites/nl_nt16/fr_kvk/2-03-testcase.xml +0 -22
  802. tests/resources/conformance_suites/nl_nt16/fr_kvk/5-01-invalid-decimals.xbrl +0 -49
  803. tests/resources/conformance_suites/nl_nt16/fr_kvk/5-01-testcase.xml +0 -22
  804. tests/resources/conformance_suites/nl_nt16/fr_kvk/5-02-invalid-decimals.xbrl +0 -49
  805. tests/resources/conformance_suites/nl_nt16/fr_kvk/5-02-testcase.xml +0 -22
  806. tests/resources/conformance_suites/nl_nt16/fr_nl/1-01-invalid-file.xbrl +0 -2
  807. tests/resources/conformance_suites/nl_nt16/fr_nl/1-01-invalid-zip.zip +0 -0
  808. tests/resources/conformance_suites/nl_nt16/fr_nl/1-01-testcase.xml +0 -33
  809. tests/resources/conformance_suites/nl_nt16/fr_nl/1-03-invalid-doctype.xbrl +0 -4
  810. tests/resources/conformance_suites/nl_nt16/fr_nl/1-03-testcase.xml +0 -22
  811. tests/resources/conformance_suites/nl_nt16/fr_nl/1-05-invalid-encoding.xbrl +0 -0
  812. tests/resources/conformance_suites/nl_nt16/fr_nl/1-05-testcase.xml +0 -22
  813. tests/resources/conformance_suites/nl_nt16/fr_nl/1-06-testcase.xml +0 -22
  814. tests/resources/conformance_suites/nl_nt16/fr_nl/1-06.invalid.xbrl +0 -2
  815. tests/resources/conformance_suites/nl_nt16/fr_nl/2-06-invalid-file.xbrl +0 -8
  816. tests/resources/conformance_suites/nl_nt16/fr_nl/2-06-invalid-zip.zip +0 -0
  817. tests/resources/conformance_suites/nl_nt16/fr_nl/2-06-testcase.xml +0 -33
  818. tests/resources/conformance_suites/nl_nt16/index.xml +0 -20
  819. tests/resources/conformance_suites/nl_nt17/br_kvk/2-04-invalid-period.xbrl +0 -63
  820. tests/resources/conformance_suites/nl_nt17/br_kvk/2-04-testcase.xml +0 -22
  821. tests/resources/conformance_suites/nl_nt17/br_kvk/3-01-missing.xbrl +0 -53
  822. tests/resources/conformance_suites/nl_nt17/br_kvk/3-01-multiple.xbrl +0 -60
  823. tests/resources/conformance_suites/nl_nt17/br_kvk/3-01-testcase.xml +0 -33
  824. tests/resources/conformance_suites/nl_nt17/br_kvk/4-07-invalid.xbrl +0 -57
  825. tests/resources/conformance_suites/nl_nt17/br_kvk/4-07-testcase.xml +0 -22
  826. tests/resources/conformance_suites/nl_nt17/br_kvk/4-10-invalid.xbrl +0 -58
  827. tests/resources/conformance_suites/nl_nt17/br_kvk/4-10-testcase.xml +0 -22
  828. tests/resources/conformance_suites/nl_nt17/br_kvk/4-12-invalid.xbrl +0 -63
  829. tests/resources/conformance_suites/nl_nt17/br_kvk/4-12-testcase.xml +0 -22
  830. tests/resources/conformance_suites/nl_nt17/br_kvk/4-16-missing.xbrl +0 -61
  831. tests/resources/conformance_suites/nl_nt17/br_kvk/4-16-testcase.xml +0 -22
  832. tests/resources/conformance_suites/nl_nt17/br_kvk/4-20-invalid-date.xbrl +0 -89
  833. tests/resources/conformance_suites/nl_nt17/br_kvk/4-20-testcase.xml +0 -22
  834. tests/resources/conformance_suites/nl_nt17/fr_kvk/1-01-invalid-file-extension.xml +0 -56
  835. tests/resources/conformance_suites/nl_nt17/fr_kvk/1-01-testcase.xml +0 -22
  836. tests/resources/conformance_suites/nl_nt17/fr_kvk/2-01-invalid-lang.xbrl +0 -56
  837. tests/resources/conformance_suites/nl_nt17/fr_kvk/2-01-missing-lang.xbrl +0 -55
  838. tests/resources/conformance_suites/nl_nt17/fr_kvk/2-01-testcase.xml +0 -33
  839. tests/resources/conformance_suites/nl_nt17/fr_kvk/2-03-entrypoint.xsd +0 -6
  840. tests/resources/conformance_suites/nl_nt17/fr_kvk/2-03-invalid-entrypoint.xbrl +0 -63
  841. tests/resources/conformance_suites/nl_nt17/fr_kvk/2-03-testcase.xml +0 -22
  842. tests/resources/conformance_suites/nl_nt17/fr_kvk/5-01-invalid-decimals.xbrl +0 -49
  843. tests/resources/conformance_suites/nl_nt17/fr_kvk/5-01-testcase.xml +0 -22
  844. tests/resources/conformance_suites/nl_nt17/fr_kvk/5-02-invalid-decimals.xbrl +0 -49
  845. tests/resources/conformance_suites/nl_nt17/fr_kvk/5-02-testcase.xml +0 -22
  846. tests/resources/conformance_suites/nl_nt17/fr_nl/1-01-invalid-file.xbrl +0 -2
  847. tests/resources/conformance_suites/nl_nt17/fr_nl/1-01-invalid-zip.zip +0 -0
  848. tests/resources/conformance_suites/nl_nt17/fr_nl/1-01-testcase.xml +0 -33
  849. tests/resources/conformance_suites/nl_nt17/fr_nl/1-03-invalid-doctype.xbrl +0 -4
  850. tests/resources/conformance_suites/nl_nt17/fr_nl/1-03-testcase.xml +0 -22
  851. tests/resources/conformance_suites/nl_nt17/fr_nl/1-05-invalid-encoding.xbrl +0 -0
  852. tests/resources/conformance_suites/nl_nt17/fr_nl/1-05-testcases.xml +0 -22
  853. tests/resources/conformance_suites/nl_nt17/fr_nl/1-06-testcase.xml +0 -22
  854. tests/resources/conformance_suites/nl_nt17/fr_nl/1-06.invalid.xbrl +0 -2
  855. tests/resources/conformance_suites/nl_nt17/fr_nl/2-06-invalid-file.xbrl +0 -8
  856. tests/resources/conformance_suites/nl_nt17/fr_nl/2-06-invalid-zip.zip +0 -0
  857. tests/resources/conformance_suites/nl_nt17/fr_nl/2-06-testcase.xml +0 -33
  858. tests/resources/conformance_suites/nl_nt17/index.xml +0 -20
  859. tests/resources/conformance_suites/nl_nt18/fr_nl/1-03-invalid-doctype.xbrl +0 -4
  860. tests/resources/conformance_suites/nl_nt18/fr_nl/1-03-testcase.xml +0 -22
  861. tests/resources/conformance_suites/nl_nt18/fr_nl/1-05-invalid-encoding.xbrl +0 -0
  862. tests/resources/conformance_suites/nl_nt18/fr_nl/1-05-testcases.xml +0 -22
  863. tests/resources/conformance_suites/nl_nt18/fr_nl/1-06-testcase.xml +0 -22
  864. tests/resources/conformance_suites/nl_nt18/fr_nl/1-06.invalid.xbrl +0 -2
  865. tests/resources/conformance_suites/nl_nt18/fr_nl/1.01-invalid-file.xbrl +0 -2
  866. tests/resources/conformance_suites/nl_nt18/fr_nl/1.01-invalid-zip.zip +0 -0
  867. tests/resources/conformance_suites/nl_nt18/fr_nl/1.01-testcase.xml +0 -33
  868. tests/resources/conformance_suites/nl_nt18/fr_nl/2-06-invalid-file.xbrl +0 -8
  869. tests/resources/conformance_suites/nl_nt18/fr_nl/2-06-invalid-zip.zip +0 -0
  870. tests/resources/conformance_suites/nl_nt18/fr_nl/2-06-testcase.xml +0 -33
  871. tests/resources/conformance_suites/nl_nt18/index.xml +0 -8
  872. tests/unit_tests/arelle/conftest.py +0 -16
  873. tests/unit_tests/arelle/formula/test_fact_aspects_cache.py +0 -170
  874. tests/unit_tests/arelle/plugin/test_loadfromoim.py +0 -40
  875. tests/unit_tests/arelle/plugin/test_plugin_imports.py +0 -27
  876. tests/unit_tests/arelle/test_betafeatures.py +0 -81
  877. tests/unit_tests/arelle/test_cntlr.py +0 -28
  878. tests/unit_tests/arelle/test_import.py +0 -40
  879. tests/unit_tests/arelle/test_locale.py +0 -73
  880. tests/unit_tests/arelle/test_modelmanager.py +0 -15
  881. tests/unit_tests/arelle/test_packagemanager.py +0 -65
  882. tests/unit_tests/arelle/test_pluginmanager.py +0 -176
  883. tests/unit_tests/arelle/test_qname.py +0 -140
  884. tests/unit_tests/arelle/test_runtimeoptions.py +0 -56
  885. tests/unit_tests/arelle/test_system_info.py +0 -26
  886. tests/unit_tests/arelle/test_updater.py +0 -507
  887. tests/unit_tests/arelle/test_urlutil.py +0 -49
  888. tests/unit_tests/arelle/test_version.py +0 -46
  889. tests/unit_tests/arelle/utils/validate/test_decorator.py +0 -59
  890. /arelle/{examples/plugin/validate/XYZ → plugin/validate/EDINET}/rules/__init__.py +0 -0
  891. /arelle/plugin/validate/{HMRC → UK}/consistencyChecksByName.json +0 -0
  892. /arelle/plugin/validate/{HMRC → UK}/hmrc-taxonomies.xml +0 -0
  893. {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