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