arelle-release 2.37.46__py3-none-any.whl → 2.38.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arelle/BetaFeatures.py +0 -21
- arelle/Cntlr.py +15 -8
- arelle/CntlrCmdLine.py +121 -56
- arelle/CntlrWinMain.py +143 -70
- arelle/DialogFind.py +1 -1
- arelle/DialogPluginManager.py +6 -4
- arelle/DisclosureSystem.py +7 -0
- arelle/ErrorManager.py +21 -6
- arelle/FileSource.py +11 -4
- arelle/FunctionIxt.py +16 -11
- arelle/HtmlUtil.py +5 -4
- arelle/LeiUtil.py +63 -43
- arelle/ModelDocument.py +20 -15
- arelle/ModelDtsObject.py +8 -0
- arelle/ModelInstanceObject.py +1 -1
- arelle/ModelObject.py +16 -18
- arelle/ModelObjectFactory.py +35 -17
- arelle/ModelXbrl.py +28 -11
- arelle/PluginManager.py +130 -105
- arelle/RuntimeOptions.py +1 -0
- arelle/UrlUtil.py +14 -0
- arelle/Validate.py +17 -12
- arelle/ValidateDuplicateFacts.py +3 -1
- arelle/ValidateFileSource.py +38 -0
- arelle/ValidateFilingText.py +3 -3
- arelle/ValidateXbrl.py +5 -2
- arelle/ValidateXbrlCalcs.py +210 -186
- arelle/ValidateXbrlDTS.py +1 -1
- arelle/ViewFile.py +1 -0
- arelle/ViewFileFactTable.py +2 -2
- arelle/ViewWinDTS.py +4 -1
- arelle/WebCache.py +28 -24
- arelle/XbrlConst.py +22 -0
- arelle/XmlUtil.py +16 -21
- arelle/XmlValidate.py +6 -9
- arelle/_version.py +16 -3
- arelle/api/Session.py +11 -2
- arelle/config/disclosuresystems.xsd +2 -0
- arelle/config/rosettaEntitlements.plist +8 -0
- arelle/conformance/CSVTestcaseLoader.py +1 -1
- arelle/formula/XPathContext.py +3 -3
- arelle/logging/formatters/LogFormatter.py +3 -1
- arelle/packages/report/ReportPackage.py +26 -13
- arelle/packages/report/ReportPackageConst.py +0 -1
- arelle/plugin/inlineXbrlDocumentSet.py +19 -5
- arelle/plugin/validate/DBA/DisclosureSystems.py +19 -1
- arelle/plugin/validate/DBA/PluginValidationDataExtension.py +2 -4
- arelle/plugin/validate/DBA/ValidationPluginExtension.py +2 -1
- arelle/plugin/validate/DBA/resources/config.xml +5 -0
- arelle/plugin/validate/DBA/rules/__init__.py +2 -2
- arelle/plugin/validate/DBA/rules/fr.py +19 -2
- arelle/plugin/validate/DBA/rules/tc.py +2 -0
- arelle/plugin/validate/DBA/rules/th.py +6 -0
- arelle/plugin/validate/DBA/rules/tm.py +18 -5
- arelle/plugin/validate/DBA/rules/tr.py +11 -5
- arelle/plugin/validate/EDINET/Constants.py +193 -9
- arelle/plugin/validate/EDINET/ContextRequirement.py +58 -0
- arelle/plugin/validate/EDINET/ControllerPluginData.py +220 -1
- arelle/plugin/validate/EDINET/CoverItemRequirements.py +42 -0
- arelle/plugin/validate/EDINET/DeiRequirements.py +118 -0
- arelle/plugin/validate/EDINET/FilingFormat.py +275 -0
- arelle/plugin/validate/EDINET/FormType.py +134 -0
- arelle/plugin/validate/EDINET/ManifestInstance.py +72 -5
- arelle/plugin/validate/EDINET/NamespaceConfig.py +50 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +493 -132
- arelle/plugin/validate/EDINET/{InstanceType.py → ReportFolderType.py} +72 -15
- arelle/plugin/validate/EDINET/Statement.py +139 -0
- arelle/plugin/validate/EDINET/TableOfContentsBuilder.py +595 -0
- arelle/plugin/validate/EDINET/UploadContents.py +48 -0
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +20 -2
- arelle/plugin/validate/EDINET/__init__.py +31 -6
- arelle/plugin/validate/EDINET/resources/config.xml +8 -1
- arelle/plugin/validate/EDINET/resources/cover-item-requirements.json +793 -0
- arelle/plugin/validate/EDINET/resources/dei-requirements.csv +27 -0
- arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml +2 -0
- arelle/plugin/validate/EDINET/rules/contexts.py +375 -14
- arelle/plugin/validate/EDINET/rules/edinet.py +1934 -45
- arelle/plugin/validate/EDINET/rules/frta.py +122 -3
- arelle/plugin/validate/EDINET/rules/gfm.py +1907 -11
- arelle/plugin/validate/EDINET/rules/upload.py +989 -141
- arelle/plugin/validate/ESEF/Const.py +3 -1
- arelle/plugin/validate/ESEF/ESEF_2021/DTS.py +5 -0
- arelle/plugin/validate/ESEF/ESEF_2021/Image.py +2 -2
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +23 -20
- arelle/plugin/validate/ESEF/ESEF_Current/DTS.py +47 -14
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +100 -25
- arelle/plugin/validate/ESEF/__init__.py +20 -6
- arelle/plugin/validate/ESEF/resources/authority-validations.json +76 -9
- arelle/plugin/validate/ESEF/resources/config.xml +20 -0
- arelle/plugin/validate/NL/DisclosureSystems.py +22 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +27 -9
- arelle/plugin/validate/NL/ValidationPluginExtension.py +51 -7
- arelle/plugin/validate/NL/resources/config.xml +18 -0
- arelle/plugin/validate/NL/rules/br_kvk.py +17 -61
- arelle/plugin/validate/NL/rules/fg_nl.py +7 -38
- arelle/plugin/validate/NL/rules/fr_kvk.py +7 -42
- arelle/plugin/validate/NL/rules/fr_nl.py +31 -147
- arelle/plugin/validate/NL/rules/nl_kvk.py +142 -28
- arelle/plugin/validate/ROS/PluginValidationDataExtension.py +2 -0
- arelle/plugin/validate/ROS/ValidationPluginExtension.py +4 -1
- arelle/plugin/validate/ROS/rules/ros.py +41 -9
- arelle/plugin/validate/UK/ValidateUK.py +130 -66
- arelle/plugin/validate/UK/__init__.py +89 -103
- arelle/utils/EntryPointDetection.py +79 -13
- arelle/utils/PluginHooks.py +125 -0
- arelle/utils/validate/ESEFImage.py +6 -6
- arelle/utils/validate/Validation.py +18 -0
- arelle/utils/validate/ValidationPlugin.py +76 -11
- arelle/utils/validate/ValidationUtil.py +35 -3
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/METADATA +30 -20
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/RECORD +115 -191
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/licenses/LICENSE.md +0 -3
- arelle/archive/CustomLogger.py +0 -43
- arelle/archive/LoadEFMvalidate.py +0 -32
- arelle/archive/LoadSavePreLbCsv.py +0 -26
- arelle/archive/LoadValidate.cs +0 -31
- arelle/archive/LoadValidate.py +0 -36
- arelle/archive/LoadValidateCmdLine.java +0 -69
- arelle/archive/LoadValidatePostedZip.java +0 -57
- arelle/archive/LoadValidateWebService.java +0 -34
- arelle/archive/SaveTableToExelle.py +0 -140
- arelle/archive/TR3toTR4.py +0 -88
- arelle/archive/plugin/ESEF_2022/__init__.py +0 -47
- arelle/archive/plugin/bigInstance.py +0 -394
- arelle/archive/plugin/cmdWebServerExtension.py +0 -43
- arelle/archive/plugin/crashTest.py +0 -38
- arelle/archive/plugin/functionsXmlCreation.py +0 -106
- arelle/archive/plugin/hello_i18n.pot +0 -26
- arelle/archive/plugin/hello_i18n.py +0 -32
- arelle/archive/plugin/importTestChild1.py +0 -21
- arelle/archive/plugin/importTestChild2.py +0 -22
- arelle/archive/plugin/importTestGrandchild1.py +0 -21
- arelle/archive/plugin/importTestGrandchild2.py +0 -21
- arelle/archive/plugin/importTestImported1.py +0 -23
- arelle/archive/plugin/importTestImported11.py +0 -22
- arelle/archive/plugin/importTestParent.py +0 -48
- arelle/archive/plugin/instanceInfo.py +0 -306
- arelle/archive/plugin/loadFromOIM-2018.py +0 -1282
- arelle/archive/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
- arelle/archive/plugin/objectmaker.py +0 -285
- arelle/archive/plugin/packagedImportTest/__init__.py +0 -47
- arelle/archive/plugin/packagedImportTest/importTestChild1.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestChild2.py +0 -22
- arelle/archive/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestImported1.py +0 -24
- arelle/archive/plugin/packagedImportTest/importTestImported11.py +0 -21
- arelle/archive/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
- arelle/archive/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
- arelle/archive/plugin/sakaCalendar.py +0 -215
- arelle/archive/plugin/saveInstanceInfoset.py +0 -121
- arelle/archive/plugin/sphinx/FormulaGenerator.py +0 -823
- arelle/archive/plugin/sphinx/SphinxContext.py +0 -404
- arelle/archive/plugin/sphinx/SphinxEvaluator.py +0 -783
- arelle/archive/plugin/sphinx/SphinxMethods.py +0 -1287
- arelle/archive/plugin/sphinx/SphinxParser.py +0 -1093
- arelle/archive/plugin/sphinx/SphinxValidator.py +0 -163
- arelle/archive/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
- arelle/archive/plugin/sphinx/__init__.py +0 -285
- arelle/archive/plugin/streamingExtensions.py +0 -335
- arelle/archive/plugin/updateTableLB.py +0 -242
- arelle/archive/plugin/validate/SBRnl/CustomLoader.py +0 -19
- arelle/archive/plugin/validate/SBRnl/DTS.py +0 -305
- arelle/archive/plugin/validate/SBRnl/Dimensions.py +0 -357
- arelle/archive/plugin/validate/SBRnl/Document.py +0 -799
- arelle/archive/plugin/validate/SBRnl/Filing.py +0 -467
- arelle/archive/plugin/validate/SBRnl/__init__.py +0 -75
- arelle/archive/plugin/validate/SBRnl/config.xml +0 -26
- arelle/archive/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
- arelle/archive/plugin/validate/USBestPractices.py +0 -570
- arelle/archive/plugin/validate/USCorpAction.py +0 -557
- arelle/archive/plugin/validate/USSecTagging.py +0 -337
- arelle/archive/plugin/validate/XDC/__init__.py +0 -77
- arelle/archive/plugin/validate/XDC/config.xml +0 -20
- arelle/archive/plugin/validate/XFsyntax/__init__.py +0 -64
- arelle/archive/plugin/validate/XFsyntax/xf.py +0 -2227
- arelle/archive/plugin/validate/calc2.py +0 -536
- arelle/archive/plugin/validateSchemaLxml.py +0 -156
- arelle/archive/plugin/validateTableInfoset.py +0 -52
- arelle/archive/us-gaap-dei-docType-extraction-frm.xml +0 -90
- arelle/archive/us-gaap-dei-ratio-cash-frm.xml +0 -150
- arelle/examples/plugin/formulaSuiteConverter.py +0 -212
- arelle/examples/plugin/functionsCustom.py +0 -59
- arelle/examples/plugin/hello_dolly.py +0 -64
- arelle/examples/plugin/multi.py +0 -58
- arelle/examples/plugin/rssSaveOim.py +0 -96
- arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
- arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
- arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -49
- arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
- arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
- arelle/examples/plugin/validate/XYZ/rules/__init__.py +0 -0
- arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -110
- arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -59
- arelle/model/CommentBase.py +0 -9
- arelle/model/ElementBase.py +0 -11
- arelle/model/PIBase.py +0 -10
- arelle/model/__init__.py +0 -15
- arelle/scripts-macOS/startWebServer.command +0 -3
- arelle/scripts-unix/startWebServer.sh +0 -1
- arelle/scripts-windows/startWebServer.bat +0 -5
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/top_level.txt +0 -0
|
@@ -3,124 +3,441 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
import zipfile
|
|
7
6
|
from collections import defaultdict
|
|
7
|
+
from collections.abc import Callable, Hashable, Iterable
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
+
from decimal import Decimal
|
|
9
10
|
from functools import lru_cache
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
|
|
12
|
-
import
|
|
13
|
+
from lxml.etree import DTD, XML
|
|
14
|
+
from operator import attrgetter
|
|
15
|
+
from typing import cast
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
import os
|
|
18
|
+
|
|
19
|
+
from arelle import XbrlConst
|
|
20
|
+
from arelle.LinkbaseType import LinkbaseType
|
|
21
|
+
from arelle.ModelDocument import Type as ModelDocumentType, ModelDocument, load as modelDocumentLoad
|
|
22
|
+
from arelle.ModelDtsObject import ModelConcept
|
|
23
|
+
from arelle.ModelInstanceObject import ModelFact, ModelUnit, ModelContext, ModelInlineFact
|
|
16
24
|
from arelle.ModelObject import ModelObject
|
|
17
25
|
from arelle.ModelValue import QName, qname
|
|
18
26
|
from arelle.ModelXbrl import ModelXbrl
|
|
19
27
|
from arelle.PrototypeDtsObject import LinkPrototype
|
|
20
28
|
from arelle.ValidateDuplicateFacts import getDeduplicatedFacts, DeduplicationType
|
|
21
29
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
30
|
+
from arelle.XhtmlValidate import htmlEltUriAttrs
|
|
22
31
|
from arelle.XmlValidate import VALID
|
|
23
32
|
from arelle.typing import TypeGetText
|
|
24
33
|
from arelle.utils.PluginData import PluginData
|
|
25
|
-
from .Constants import
|
|
34
|
+
from .Constants import xhtmlDtdExtension, PROHIBITED_HTML_TAGS, PROHIBITED_HTML_ATTRIBUTES
|
|
26
35
|
from .ControllerPluginData import ControllerPluginData
|
|
27
|
-
from .
|
|
36
|
+
from .DeiRequirements import DeiRequirements, DEI_LOCAL_NAMES
|
|
37
|
+
from .FilingFormat import FilingFormat, FILING_FORMATS, DocumentType, Ordinance
|
|
38
|
+
from .FormType import FormType
|
|
28
39
|
from .ManifestInstance import ManifestInstance
|
|
40
|
+
from .NamespaceConfig import NamespaceConfig
|
|
41
|
+
from .ReportFolderType import HTML_EXTENSIONS
|
|
42
|
+
from .Statement import Statement, STATEMENTS, BalanceSheet, StatementInstance, StatementType
|
|
43
|
+
from .UploadContents import UploadContents, UploadPathInfo
|
|
29
44
|
|
|
30
45
|
_: TypeGetText
|
|
31
46
|
|
|
32
47
|
|
|
48
|
+
STANDARD_TAXONOMY_URL_PREFIXES = frozenset((
|
|
49
|
+
'http://disclosure.edinet-fsa.go.jp/taxonomy/',
|
|
50
|
+
'https://disclosure.edinet-fsa.go.jp/taxonomy/',
|
|
51
|
+
'http://www.xbrl.org/20',
|
|
52
|
+
'https://www.xbrl.org/20',
|
|
53
|
+
'http://www.xbrl.org/lrr/',
|
|
54
|
+
'https://www.xbrl.org/lrr/',
|
|
55
|
+
'http://xbrl.org/20',
|
|
56
|
+
'https://xbrl.org/20',
|
|
57
|
+
'http://www.xbrl.org/dtr/',
|
|
58
|
+
'https://www.xbrl.org/dtr/',
|
|
59
|
+
'http://www.w3.org/1999/xlink',
|
|
60
|
+
'https://www.w3.org/1999/xlink'
|
|
61
|
+
))
|
|
62
|
+
|
|
63
|
+
|
|
33
64
|
@dataclass(frozen=True)
|
|
34
|
-
class
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
65
|
+
class UriReference:
|
|
66
|
+
attributeName: str
|
|
67
|
+
attributeValue: str
|
|
68
|
+
document: ModelDocument
|
|
69
|
+
element: ModelObject
|
|
39
70
|
|
|
40
71
|
|
|
41
72
|
@dataclass
|
|
42
73
|
class PluginValidationDataExtension(PluginData):
|
|
74
|
+
namespaces: NamespaceConfig
|
|
75
|
+
|
|
76
|
+
accountingStandardsDeiQn: QName
|
|
43
77
|
assetsIfrsQn: QName
|
|
78
|
+
categoriesOfDirectorsAndOtherOfficersAxisQn: QName
|
|
79
|
+
consolidatedOrNonConsolidatedAxisQn: QName
|
|
80
|
+
corporateGovernanceCompanyWithAuditAndSupervisoryCommitteeTextBlockQn: QName
|
|
81
|
+
corporateGovernanceCompanyWithCorporateAuditorsTextBlockQn: QName
|
|
82
|
+
corporateGovernanceCompanyWithNominatingAndOtherCommitteesTextBlockQn: QName
|
|
44
83
|
documentTypeDeiQn: QName
|
|
84
|
+
executiveOfficersMemberQn: QName
|
|
45
85
|
jpcrpEsrFilingDateCoverPageQn: QName
|
|
46
86
|
jpcrpFilingDateCoverPageQn: QName
|
|
47
87
|
jpspsFilingDateCoverPageQn: QName
|
|
48
|
-
|
|
88
|
+
issuedSharesTotalNumberOfSharesEtcQn: QName
|
|
49
89
|
nonConsolidatedMemberQn: QName
|
|
50
90
|
ratioOfFemaleDirectorsAndOtherOfficersQn: QName
|
|
51
91
|
|
|
52
|
-
|
|
92
|
+
coverItemRequirementsPath: Path
|
|
93
|
+
coverPageTitleQns: tuple[QName, ...]
|
|
94
|
+
deiItems: tuple[QName, ...]
|
|
95
|
+
deiRequirementsPath: Path
|
|
53
96
|
|
|
54
|
-
|
|
97
|
+
_uriReferences: list[UriReference]
|
|
55
98
|
|
|
56
|
-
def __init__(self, name: str):
|
|
99
|
+
def __init__(self, name: str, validateXbrl: ValidateXbrl):
|
|
57
100
|
super().__init__(name)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
self.
|
|
65
|
-
self.
|
|
66
|
-
self.
|
|
67
|
-
self.
|
|
68
|
-
self.
|
|
69
|
-
self.
|
|
70
|
-
self.
|
|
71
|
-
self.
|
|
72
|
-
|
|
73
|
-
self.
|
|
101
|
+
|
|
102
|
+
self.namespaces = NamespaceConfig()
|
|
103
|
+
|
|
104
|
+
# QNames
|
|
105
|
+
self.accountingStandardsDeiQn = qname(self.namespaces.jpdei, 'AccountingStandardsDEI')
|
|
106
|
+
self.assetsIfrsQn = qname(self.namespaces.jpigp, 'AssetsIFRS')
|
|
107
|
+
self.categoriesOfDirectorsAndOtherOfficersAxisQn = qname(self.namespaces.jpcrp, 'CategoriesOfDirectorsAndOtherOfficersAxis')
|
|
108
|
+
self.consolidatedOrNonConsolidatedAxisQn = qname(self.namespaces.jppfs, 'ConsolidatedOrNonConsolidatedAxis')
|
|
109
|
+
self.corporateGovernanceCompanyWithAuditAndSupervisoryCommitteeTextBlockQn = qname(self.namespaces.jpcrp, 'CorporateGovernanceCompanyWithAuditAndSupervisoryCommitteeTextBlock')
|
|
110
|
+
self.corporateGovernanceCompanyWithCorporateAuditorsTextBlockQn = qname(self.namespaces.jpcrp, 'CorporateGovernanceCompanyWithCorporateAuditorsTextBlock')
|
|
111
|
+
self.corporateGovernanceCompanyWithNominatingAndOtherCommitteesTextBlockQn = qname(self.namespaces.jpcrp, 'CorporateGovernanceCompanyWithNominatingAndOtherCommitteesTextBlock')
|
|
112
|
+
self.documentTypeDeiQn = qname(self.namespaces.jpdei, 'DocumentTypeDEI')
|
|
113
|
+
self.executiveOfficersMemberQn = qname(self.namespaces.jpcrp, 'ExecutiveOfficersMember')
|
|
114
|
+
self.issuedSharesTotalNumberOfSharesEtcQn = qname(self.namespaces.jpcrp, 'IssuedSharesTotalNumberOfSharesEtcTextBlock')
|
|
115
|
+
self.jpcrpEsrFilingDateCoverPageQn = qname(self.namespaces.jpcrpEsr, 'FilingDateCoverPage')
|
|
116
|
+
self.jpcrpFilingDateCoverPageQn = qname(self.namespaces.jpcrp, 'FilingDateCoverPage')
|
|
117
|
+
self.jpspsFilingDateCoverPageQn = qname(self.namespaces.jpsps, 'FilingDateCoverPage')
|
|
118
|
+
self.nonConsolidatedMemberQn = qname(self.namespaces.jppfs, "NonConsolidatedMember")
|
|
119
|
+
self.ratioOfFemaleDirectorsAndOtherOfficersQn = qname(self.namespaces.jpcrp, "RatioOfFemaleDirectorsAndOtherOfficers")
|
|
120
|
+
|
|
121
|
+
self.coverItemRequirementsPath = Path(__file__).parent / "resources" / "cover-item-requirements.json"
|
|
122
|
+
self.coverPageTitleQns = (
|
|
123
|
+
qname(self.namespaces.jpsps, "DocumentTitleAnnualSecuritiesReportCoverPage"),
|
|
124
|
+
qname(self.namespaces.jpcrp, "DocumentTitleCoverPage"),
|
|
125
|
+
qname(self.namespaces.jpcrpEsr, "DocumentTitleCoverPage"),
|
|
126
|
+
qname(self.namespaces.jpsps, "DocumentTitleCoverPage"),
|
|
127
|
+
)
|
|
128
|
+
self.deiItems = tuple(
|
|
129
|
+
qname(self.namespaces.jpdei, localName)
|
|
130
|
+
for localName in DEI_LOCAL_NAMES
|
|
131
|
+
)
|
|
132
|
+
self.deiRequirementsPath = Path(__file__).parent / "resources" / "dei-requirements.csv"
|
|
133
|
+
|
|
134
|
+
self._uriReferences = []
|
|
135
|
+
self._initialize(validateXbrl.modelXbrl)
|
|
74
136
|
|
|
75
137
|
# Identity hash for caching.
|
|
76
138
|
def __hash__(self) -> int:
|
|
77
139
|
return id(self)
|
|
78
140
|
|
|
141
|
+
@lru_cache(1)
|
|
142
|
+
def _contextMatchesStatement(self, modelXbrl: ModelXbrl, contextId: str, statement: Statement) -> bool:
|
|
143
|
+
"""
|
|
144
|
+
:return: Whether the context's facts are applicable to the given statement.
|
|
145
|
+
"""
|
|
146
|
+
if 'Interim' in contextId:
|
|
147
|
+
# valid06.zip suggests "interim"" contexts are not considered for balance sheets.
|
|
148
|
+
return False
|
|
149
|
+
context = modelXbrl.contexts[contextId]
|
|
150
|
+
if not all(dimQn == self.consolidatedOrNonConsolidatedAxisQn for dimQn in context.qnameDims):
|
|
151
|
+
return False
|
|
152
|
+
memberValue = context.dimMemberQname(self.consolidatedOrNonConsolidatedAxisQn, includeDefaults=True)
|
|
153
|
+
contextIsConsolidated = memberValue != self.nonConsolidatedMemberQn
|
|
154
|
+
return bool(statement.isConsolidated == contextIsConsolidated)
|
|
155
|
+
|
|
156
|
+
def _initializeDocument(self, uri: str, modelDocument: ModelDocument, modelXbrl: ModelXbrl) -> None:
|
|
157
|
+
docPath = Path(uri)
|
|
158
|
+
basePath = Path(str(modelXbrl.fileSource.basefile))
|
|
159
|
+
if not docPath.is_relative_to(basePath):
|
|
160
|
+
return
|
|
161
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
162
|
+
controllerPluginData.addUsedFilepath(docPath.relative_to(basePath))
|
|
163
|
+
for elt, name, value in self.getUriAttributeValues(modelDocument):
|
|
164
|
+
self._uriReferences.append(UriReference(
|
|
165
|
+
attributeName=name,
|
|
166
|
+
attributeValue=value,
|
|
167
|
+
document=modelDocument,
|
|
168
|
+
element=elt,
|
|
169
|
+
))
|
|
170
|
+
fullPath = (Path(modelDocument.uri).parent / value).resolve()
|
|
171
|
+
if fullPath.is_relative_to(basePath):
|
|
172
|
+
fileSourcePath = fullPath.relative_to(basePath)
|
|
173
|
+
controllerPluginData.addUsedFilepath(fileSourcePath)
|
|
174
|
+
referenceUri = str(fullPath)
|
|
175
|
+
if (
|
|
176
|
+
fullPath.suffix in HTML_EXTENSIONS and
|
|
177
|
+
referenceUri not in modelXbrl.urlDocs and
|
|
178
|
+
modelXbrl.fileSource.exists(referenceUri)
|
|
179
|
+
):
|
|
180
|
+
referenceModelDocument = modelDocumentLoad(
|
|
181
|
+
modelXbrl,
|
|
182
|
+
referenceUri,
|
|
183
|
+
referringElement=elt
|
|
184
|
+
)
|
|
185
|
+
if referenceModelDocument is not None:
|
|
186
|
+
self._initializeDocument(referenceUri, referenceModelDocument, modelXbrl)
|
|
187
|
+
|
|
188
|
+
def _initialize(self, modelXbrl: ModelXbrl) -> None:
|
|
189
|
+
if not isinstance(modelXbrl.fileSource.basefile, str):
|
|
190
|
+
return
|
|
191
|
+
# Additional documents may be loaded, so make a copy to iterate over.
|
|
192
|
+
urlDocs = list(modelXbrl.urlDocs.items())
|
|
193
|
+
for uri, modelDocument in urlDocs:
|
|
194
|
+
self._initializeDocument(uri, modelDocument, modelXbrl)
|
|
195
|
+
|
|
196
|
+
def addToTableOfContents(self, modelXbrl: ModelXbrl) -> None:
|
|
197
|
+
uploadContents = self.getUploadContents(modelXbrl)
|
|
198
|
+
if uploadContents is None:
|
|
199
|
+
return
|
|
200
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
201
|
+
tocBuilder = controllerPluginData.getTableOfContentsBuilder()
|
|
202
|
+
for modelDocument in modelXbrl.urlDocs.values():
|
|
203
|
+
path = Path(modelDocument.uri)
|
|
204
|
+
if modelDocument.type != ModelDocumentType.INLINEXBRL:
|
|
205
|
+
continue
|
|
206
|
+
pathInfo = uploadContents.uploadPathsByFullPath.get(path)
|
|
207
|
+
if pathInfo is not None and not pathInfo.isCoverPage:
|
|
208
|
+
tocBuilder.addDocument(modelDocument)
|
|
209
|
+
|
|
210
|
+
def qname(self, prefix: str, localName: str) -> QName:
|
|
211
|
+
ns = self.namespaces.get(prefix)
|
|
212
|
+
assert ns is not None, f"Unknown namespace prefix: {prefix}"
|
|
213
|
+
return qname(ns, localName)
|
|
214
|
+
|
|
79
215
|
@lru_cache(1)
|
|
80
216
|
def isCorporateForm(self, modelXbrl: ModelXbrl) -> bool:
|
|
81
|
-
|
|
82
|
-
if
|
|
217
|
+
formType = self.getFormType(modelXbrl)
|
|
218
|
+
if formType is None:
|
|
219
|
+
return False
|
|
220
|
+
return formType.isCorporateForm
|
|
221
|
+
|
|
222
|
+
def isCorporateReport(self, modelXbrl: ModelXbrl) -> bool:
|
|
223
|
+
return self.namespaces.jpcrp in modelXbrl.namespaceDocs
|
|
224
|
+
|
|
225
|
+
def isExtensionUri(self, uri: str, modelXbrl: ModelXbrl) -> bool:
|
|
226
|
+
if uri.startswith(modelXbrl.uriDir):
|
|
83
227
|
return True
|
|
84
|
-
return
|
|
228
|
+
return not any(uri.startswith(taxonomyUri) for taxonomyUri in STANDARD_TAXONOMY_URL_PREFIXES)
|
|
229
|
+
|
|
230
|
+
@lru_cache(1)
|
|
231
|
+
def isStockForm(self, modelXbrl: ModelXbrl) -> bool:
|
|
232
|
+
formType = self.getFormType(modelXbrl)
|
|
233
|
+
if formType is None:
|
|
234
|
+
return False
|
|
235
|
+
return formType.isStockReport
|
|
85
236
|
|
|
86
237
|
@lru_cache(1)
|
|
87
|
-
def
|
|
238
|
+
def getExtensionConcepts(self, modelXbrl: ModelXbrl) -> list[ModelConcept]:
|
|
88
239
|
"""
|
|
89
|
-
|
|
240
|
+
Returns a list of extension concepts in the DTS.
|
|
241
|
+
"""
|
|
242
|
+
extensionConcepts = []
|
|
243
|
+
for concepts in modelXbrl.nameConcepts.values():
|
|
244
|
+
for concept in concepts:
|
|
245
|
+
if self.isExtensionUri(concept.document.uri, modelXbrl):
|
|
246
|
+
extensionConcepts.append(concept)
|
|
247
|
+
return extensionConcepts
|
|
90
248
|
|
|
91
|
-
|
|
92
|
-
|
|
249
|
+
@lru_cache(1)
|
|
250
|
+
def getUsedConcepts(self, modelXbrl: ModelXbrl) -> set[ModelConcept]:
|
|
251
|
+
"""
|
|
252
|
+
Returns a set of concepts used on facts and in explicit dimensions
|
|
253
|
+
"""
|
|
254
|
+
usedConcepts = {fact.concept for fact in modelXbrl.facts if fact.concept is not None}
|
|
255
|
+
for context in modelXbrl.contextsInUse:
|
|
256
|
+
for dim in context.scenDimValues.values():
|
|
257
|
+
if dim.isExplicit:
|
|
258
|
+
usedConcepts.update([dim.dimension, dim.member])
|
|
259
|
+
return usedConcepts
|
|
93
260
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
and check if the given model is the first to be loaded.
|
|
98
|
-
:param val: The ValidateXbrl instance with a model to check.
|
|
99
|
-
:return: True if upload validation should be performed, False otherwise.
|
|
261
|
+
def getBalanceSheets(self, modelXbrl: ModelXbrl, statement: Statement) -> list[BalanceSheet]:
|
|
262
|
+
"""
|
|
263
|
+
:return: Balance sheet data for each context/unit pairing the given statement.
|
|
100
264
|
"""
|
|
101
|
-
|
|
102
|
-
if
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
265
|
+
balanceSheets: list[BalanceSheet] = []
|
|
266
|
+
if statement.roleUri not in modelXbrl.roleTypes:
|
|
267
|
+
return balanceSheets
|
|
268
|
+
if statement.statementType not in (
|
|
269
|
+
StatementType.BALANCE_SHEET,
|
|
270
|
+
StatementType.STATEMENT_OF_FINANCIAL_POSITION
|
|
271
|
+
):
|
|
272
|
+
return balanceSheets
|
|
273
|
+
|
|
274
|
+
relSet = modelXbrl.relationshipSet(
|
|
275
|
+
tuple(LinkbaseType.CALCULATION.getArcroles()),
|
|
276
|
+
linkrole=statement.roleUri
|
|
277
|
+
)
|
|
278
|
+
rootConcepts = relSet.rootConcepts
|
|
279
|
+
if len(rootConcepts) == 0:
|
|
280
|
+
return balanceSheets
|
|
281
|
+
|
|
282
|
+
# GFM 1.2.7 and 1.2.10 asserts no duplicate contexts and units, respectively,
|
|
283
|
+
# so context and unit IDs can be used as a key.
|
|
284
|
+
factsByContextIdAndUnitId = self.getFactsByContextAndUnit(
|
|
285
|
+
modelXbrl,
|
|
286
|
+
attrgetter("id"),
|
|
287
|
+
attrgetter("id"),
|
|
288
|
+
tuple(concept.qname for concept in rootConcepts)
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
for (contextId, unitId), facts in factsByContextIdAndUnitId.items():
|
|
292
|
+
if not self._contextMatchesStatement(modelXbrl, contextId, statement):
|
|
293
|
+
continue
|
|
294
|
+
creditSum = Decimal(0)
|
|
295
|
+
debitSum = Decimal(0)
|
|
296
|
+
for fact in facts:
|
|
297
|
+
if isinstance(fact.xValue, float):
|
|
298
|
+
value = Decimal(fact.xValue)
|
|
299
|
+
else:
|
|
300
|
+
value = cast(Decimal, fact.xValue)
|
|
301
|
+
if fact.concept.balance == "debit":
|
|
302
|
+
debitSum += value
|
|
303
|
+
elif fact.concept.balance == "credit":
|
|
304
|
+
creditSum += value
|
|
305
|
+
balanceSheets.append(
|
|
306
|
+
BalanceSheet(
|
|
307
|
+
creditSum=creditSum,
|
|
308
|
+
contextId=str(contextId),
|
|
309
|
+
facts=facts,
|
|
310
|
+
debitSum=debitSum,
|
|
311
|
+
unitId=str(unitId),
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
return balanceSheets
|
|
315
|
+
|
|
316
|
+
@lru_cache(1)
|
|
317
|
+
def getCoverItemRequirements(self, modelXbrl: ModelXbrl) -> list[QName] | None:
|
|
318
|
+
manifestInstance = self.getManifestInstance(modelXbrl)
|
|
319
|
+
if manifestInstance is None:
|
|
320
|
+
return None
|
|
321
|
+
if any(e is not None and e.startswith('EDINET.EC5800E') for e in modelXbrl.errors):
|
|
322
|
+
# Manifest TOC parsing failed, so cannot determine cover items.
|
|
323
|
+
return None
|
|
324
|
+
assert len(manifestInstance.tocItems) == 1, _("Only one TOC item should be associated with this instance.")
|
|
325
|
+
roleUri = manifestInstance.tocItems[0].extrole
|
|
326
|
+
roleUri = roleUri.replace("_std_", "_")
|
|
327
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
328
|
+
coverItemRequirements = controllerPluginData.getCoverItemRequirements(self.coverItemRequirementsPath)
|
|
329
|
+
coverItems = coverItemRequirements.get(roleUri)
|
|
330
|
+
return [
|
|
331
|
+
self.qname(prefix, localName)
|
|
332
|
+
for prefix, localName in
|
|
333
|
+
[name.split(':') for name in coverItems]
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
@lru_cache(1)
|
|
337
|
+
def getCoverItems(self, modelXbrl: ModelXbrl) -> frozenset[QName]:
|
|
338
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
339
|
+
coverItemRequirements = controllerPluginData.getCoverItemRequirements(self.coverItemRequirementsPath)
|
|
340
|
+
coverItems = coverItemRequirements.all()
|
|
341
|
+
return frozenset(
|
|
342
|
+
self.qname(prefix, localName)
|
|
343
|
+
for prefix, localName in
|
|
344
|
+
[name.split(':') for name in coverItems]
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def getDeiRequirements(self, modelXbrl: ModelXbrl) -> DeiRequirements:
|
|
348
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
349
|
+
return controllerPluginData.getDeiRequirements(self.deiRequirementsPath, self.deiItems, FILING_FORMATS)
|
|
350
|
+
|
|
351
|
+
@lru_cache(1)
|
|
352
|
+
def getExtensionSchemas(self, modelXbrl: ModelXbrl) -> dict[str, UploadPathInfo]:
|
|
353
|
+
namespacePathInfos: dict[str, UploadPathInfo] = {}
|
|
354
|
+
uploadContents = self.getUploadContents(modelXbrl)
|
|
355
|
+
if uploadContents is None:
|
|
356
|
+
return namespacePathInfos
|
|
357
|
+
for modelDocument in modelXbrl.urlDocs.values():
|
|
358
|
+
if modelDocument.type != ModelDocumentType.SCHEMA:
|
|
359
|
+
continue # Not a schema
|
|
360
|
+
if modelDocument.targetNamespace is None:
|
|
361
|
+
continue # No target namespace
|
|
362
|
+
if not self.isExtensionUri(modelDocument.uri, modelXbrl):
|
|
363
|
+
continue # Not an extension schema
|
|
364
|
+
path = Path(modelDocument.uri)
|
|
365
|
+
pathInfo = uploadContents.uploadPathsByFullPath.get(path)
|
|
366
|
+
if pathInfo is None or pathInfo.reportFolderType is None:
|
|
367
|
+
continue # Not part of the filing, error will be caught elsewhere
|
|
368
|
+
namespacePathInfos[modelDocument.targetNamespace] = pathInfo
|
|
369
|
+
return namespacePathInfos
|
|
370
|
+
|
|
371
|
+
def getProblematicTextBlocks(self, modelXbrl: ModelXbrl) -> list[ModelInlineFact]:
|
|
372
|
+
problematicTextBlocks: list[ModelInlineFact] = []
|
|
373
|
+
dtd = DTD(os.path.join(modelXbrl.modelManager.cntlr.configDir, xhtmlDtdExtension))
|
|
374
|
+
htmlBodyTemplate = "<body><div>\n{0}\n</div></body>\n"
|
|
375
|
+
for fact in modelXbrl.facts:
|
|
376
|
+
concept = fact.concept
|
|
377
|
+
if isinstance(fact, ModelInlineFact) and not fact.isNil and concept is not None and concept.isTextBlock and not fact.isEscaped:
|
|
378
|
+
xmlBody = htmlBodyTemplate.format(fact.value)
|
|
379
|
+
try:
|
|
380
|
+
textblockXml = XML(xmlBody)
|
|
381
|
+
if not dtd.validate(textblockXml):
|
|
382
|
+
problematicTextBlocks.append(fact)
|
|
383
|
+
except Exception:
|
|
384
|
+
problematicTextBlocks.append(fact)
|
|
385
|
+
return problematicTextBlocks
|
|
386
|
+
|
|
387
|
+
@lru_cache(1)
|
|
388
|
+
def getStatementInstance(self, modelXbrl: ModelXbrl, statement: Statement) -> StatementInstance | None:
|
|
389
|
+
if statement.roleUri not in modelXbrl.roleTypes:
|
|
390
|
+
return None
|
|
391
|
+
return StatementInstance(
|
|
392
|
+
balanceSheets=self.getBalanceSheets(modelXbrl, statement),
|
|
393
|
+
statement=statement,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
@lru_cache(1)
|
|
397
|
+
def getStatementInstances(self, modelXbrl: ModelXbrl) -> list[StatementInstance]:
|
|
398
|
+
return [
|
|
399
|
+
statementInstance
|
|
400
|
+
for statement in STATEMENTS
|
|
401
|
+
if (statementInstance := self.getStatementInstance(modelXbrl, statement)) is not None
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
@property
|
|
405
|
+
def uriReferences(self) -> list[UriReference]:
|
|
406
|
+
return self._uriReferences
|
|
111
407
|
|
|
112
408
|
@lru_cache(1)
|
|
113
409
|
def getDeduplicatedFacts(self, modelXbrl: ModelXbrl) -> list[ModelFact]:
|
|
114
410
|
return getDeduplicatedFacts(modelXbrl, DeduplicationType.CONSISTENT_PAIRS)
|
|
115
411
|
|
|
116
412
|
@lru_cache(1)
|
|
117
|
-
def
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
413
|
+
def getDocumentType(self, modelXbrl: ModelXbrl) -> DocumentType | None:
|
|
414
|
+
"""
|
|
415
|
+
Retrieves document type value from the instance.
|
|
416
|
+
:param modelXbrl: Instance to get document type from.
|
|
417
|
+
:return: document type parsed from filename.
|
|
418
|
+
"""
|
|
419
|
+
filingFormat = self.getFilingFormat(modelXbrl)
|
|
420
|
+
if filingFormat is None:
|
|
421
|
+
return None
|
|
422
|
+
return filingFormat.documentType
|
|
423
|
+
|
|
424
|
+
def getFactsByContextAndUnit(
|
|
425
|
+
self, modelXbrl: ModelXbrl,
|
|
426
|
+
getContextKey: Callable[[ModelContext], Hashable],
|
|
427
|
+
getUnitKey: Callable[[ModelUnit], Hashable],
|
|
428
|
+
qnames: tuple[QName, ...] | None = None,
|
|
429
|
+
) -> dict[tuple[Hashable, Hashable], list[ModelFact]]:
|
|
430
|
+
deduplicatedFacts = self.getDeduplicatedFacts(modelXbrl)
|
|
431
|
+
getFactsByContextAndUnit = defaultdict(list)
|
|
432
|
+
for fact in deduplicatedFacts:
|
|
433
|
+
if qnames is not None and fact.qname not in qnames:
|
|
434
|
+
continue
|
|
435
|
+
if fact.context is None or fact.unit is None:
|
|
436
|
+
continue
|
|
437
|
+
contextKey = getContextKey(fact.context)
|
|
438
|
+
unitKey = getUnitKey(fact.unit)
|
|
439
|
+
getFactsByContextAndUnit[(contextKey, unitKey)].append(fact)
|
|
440
|
+
return dict(getFactsByContextAndUnit)
|
|
124
441
|
|
|
125
442
|
@lru_cache(1)
|
|
126
443
|
def getFootnoteLinkElements(self, modelXbrl: ModelXbrl) -> list[ModelObject | LinkPrototype]:
|
|
@@ -145,90 +462,134 @@ class PluginValidationDataExtension(PluginData):
|
|
|
145
462
|
]
|
|
146
463
|
|
|
147
464
|
@lru_cache(1)
|
|
148
|
-
def
|
|
149
|
-
|
|
150
|
-
|
|
465
|
+
def getFilingFormat(self, modelXbrl: ModelXbrl) -> FilingFormat | None:
|
|
466
|
+
manifestInstance = self.getManifestInstance(modelXbrl)
|
|
467
|
+
if manifestInstance is None:
|
|
468
|
+
return None
|
|
469
|
+
return manifestInstance.filingFormat
|
|
470
|
+
|
|
471
|
+
@lru_cache(1)
|
|
472
|
+
def getFormType(self, modelXbrl: ModelXbrl) -> FormType | None:
|
|
473
|
+
"""
|
|
474
|
+
Retrieves form type value from the instance.
|
|
475
|
+
:param modelXbrl: Instance to get form type from.
|
|
476
|
+
:return: Form type parsed from filename.
|
|
477
|
+
"""
|
|
478
|
+
filingFormat = self.getFilingFormat(modelXbrl)
|
|
479
|
+
if filingFormat is None:
|
|
480
|
+
return None
|
|
481
|
+
return filingFormat.formType
|
|
151
482
|
|
|
152
483
|
@lru_cache(1)
|
|
153
|
-
def
|
|
484
|
+
def getManifestInstance(self, modelXbrl: ModelXbrl) -> ManifestInstance | None:
|
|
154
485
|
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
155
|
-
return controllerPluginData.
|
|
486
|
+
return controllerPluginData.getManifestInstance(modelXbrl)
|
|
156
487
|
|
|
157
|
-
|
|
488
|
+
@lru_cache(1)
|
|
489
|
+
def getOrdinance(self, modelXbrl: ModelXbrl) -> Ordinance | None:
|
|
158
490
|
"""
|
|
159
|
-
|
|
160
|
-
:param modelXbrl:
|
|
161
|
-
:return:
|
|
491
|
+
Retrieves ordinance value from the instance.
|
|
492
|
+
:param modelXbrl: Instance to get ordinance from.
|
|
493
|
+
:return: Ordinance parsed from filename.
|
|
162
494
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
return
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
amendmentPaths = defaultdict(list)
|
|
176
|
-
unknownPaths = []
|
|
177
|
-
directories = []
|
|
178
|
-
forms = defaultdict(list)
|
|
179
|
-
for path in uploadFilepaths:
|
|
180
|
-
parents = list(reversed([p.name for p in path.parents if len(p.name) > 0]))
|
|
181
|
-
if len(parents) == 0:
|
|
495
|
+
filingFormat = self.getFilingFormat(modelXbrl)
|
|
496
|
+
if filingFormat is None:
|
|
497
|
+
return None
|
|
498
|
+
return filingFormat.ordinance
|
|
499
|
+
|
|
500
|
+
@lru_cache(1)
|
|
501
|
+
def getProhibitedAttributeElements(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str]]:
|
|
502
|
+
results: list[tuple[ModelObject, str]] = []
|
|
503
|
+
if modelDocument.type not in (ModelDocumentType.INLINEXBRL, ModelDocumentType.HTML):
|
|
504
|
+
return results
|
|
505
|
+
for elt in modelDocument.xmlRootElement.iter():
|
|
506
|
+
if not isinstance(elt, ModelObject):
|
|
182
507
|
continue
|
|
183
|
-
|
|
184
|
-
if
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
508
|
+
for attributeName in elt.attrib:
|
|
509
|
+
if attributeName in PROHIBITED_HTML_ATTRIBUTES:
|
|
510
|
+
results.append((elt, str(attributeName)))
|
|
511
|
+
return results
|
|
512
|
+
|
|
513
|
+
@lru_cache(1)
|
|
514
|
+
def getProhibitedTagElements(self, modelDocument: ModelDocument) -> list[ModelObject]:
|
|
515
|
+
elts: list[ModelObject] = []
|
|
516
|
+
if modelDocument.type not in (ModelDocumentType.INLINEXBRL, ModelDocumentType.HTML):
|
|
517
|
+
return elts
|
|
518
|
+
for elt in modelDocument.xmlRootElement.iter():
|
|
519
|
+
if not isinstance(elt, ModelObject):
|
|
194
520
|
continue
|
|
195
|
-
|
|
196
|
-
|
|
521
|
+
tag = elt.qname.localName
|
|
522
|
+
if tag in PROHIBITED_HTML_TAGS:
|
|
523
|
+
elts.append(elt)
|
|
524
|
+
return elts
|
|
525
|
+
|
|
526
|
+
def getStandardTaxonomyExtensionLinks(self, linkbaseType: LinkbaseType, modelXbrl: ModelXbrl) -> list[ModelObject]:
|
|
527
|
+
elts: list[ModelObject] = []
|
|
528
|
+
for modelDocument in modelXbrl.urlDocs.values():
|
|
529
|
+
if self.isStandardTaxonomyUrl(modelDocument.uri, modelXbrl) or not modelDocument.type == ModelDocumentType.SCHEMA:
|
|
197
530
|
continue
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
531
|
+
rootElt = modelDocument.xmlRootElement
|
|
532
|
+
for elt in rootElt.iterdescendants(XbrlConst.qnLinkLinkbaseRef.clarkNotation):
|
|
533
|
+
uri = elt.attrib.get(XbrlConst.qnXlinkHref.clarkNotation)
|
|
534
|
+
role = elt.attrib.get(XbrlConst.qnXlinkRole.clarkNotation)
|
|
535
|
+
if not role == linkbaseType.getRefUri() or self.isExtensionUri(uri, modelXbrl):
|
|
536
|
+
continue
|
|
537
|
+
elts.append(elt)
|
|
538
|
+
return elts
|
|
539
|
+
|
|
540
|
+
def getUploadContents(self, modelXbrl: ModelXbrl) -> UploadContents | None:
|
|
541
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
542
|
+
return controllerPluginData.getUploadContents()
|
|
205
543
|
|
|
206
544
|
@lru_cache(1)
|
|
207
|
-
def
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
545
|
+
def getUriAttributeValues(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str, str]]:
|
|
546
|
+
results: list[tuple[ModelObject, str, str]] = []
|
|
547
|
+
modelDocumentType = modelDocument.type
|
|
548
|
+
# Normal document parsing does not assign the HTML type to HTML files.
|
|
549
|
+
# Use ModelDocumentType.identify to check for HTML files.
|
|
550
|
+
if modelDocumentType == ModelDocumentType.UnknownXML:
|
|
551
|
+
modelDocumentType = ModelDocumentType.identify(modelDocument.modelXbrl.fileSource, modelDocument.uri)
|
|
552
|
+
if modelDocumentType not in (ModelDocumentType.INLINEXBRL, ModelDocumentType.HTML):
|
|
553
|
+
return results
|
|
554
|
+
for elt in modelDocument.xmlRootElement.iter():
|
|
555
|
+
if not isinstance(elt, ModelObject):
|
|
556
|
+
continue
|
|
557
|
+
for name in htmlEltUriAttrs.get(elt.localName, ()):
|
|
558
|
+
value = elt.get(name)
|
|
559
|
+
if value is not None:
|
|
560
|
+
results.append((elt, name, value))
|
|
561
|
+
return results
|
|
217
562
|
|
|
218
563
|
def hasValidNonNilFact(self, modelXbrl: ModelXbrl, qname: QName) -> bool:
|
|
219
|
-
|
|
220
|
-
return any(fact.xValid >= VALID and not fact.isNil for fact in requiredFacts)
|
|
221
|
-
|
|
222
|
-
@lru_cache(1)
|
|
223
|
-
def isUpload(self, modelXbrl: ModelXbrl) -> bool:
|
|
224
|
-
if not modelXbrl.fileSource.fs or \
|
|
225
|
-
not isinstance(modelXbrl.fileSource.fs, zipfile.ZipFile):
|
|
226
|
-
modelXbrl.warning(
|
|
227
|
-
codes="EDINET.uploadNotValidated",
|
|
228
|
-
msg=_("The target file is not a zip file, so upload validation could not be performed.")
|
|
229
|
-
)
|
|
230
|
-
return False
|
|
231
|
-
return True
|
|
564
|
+
return any(True for fact in self.iterValidNonNilFacts(modelXbrl, qname))
|
|
232
565
|
|
|
233
566
|
def isStandardTaxonomyUrl(self, uri: str, modelXbrl: ModelXbrl) -> bool:
|
|
234
567
|
return modelXbrl.modelManager.disclosureSystem.hrefValidForDisclosureSystem(uri)
|
|
568
|
+
|
|
569
|
+
def iterCoverPages(self, modelXbrl: ModelXbrl) -> Iterable[ModelDocument]:
|
|
570
|
+
uploadContents = self.getUploadContents(modelXbrl)
|
|
571
|
+
if uploadContents is None:
|
|
572
|
+
return
|
|
573
|
+
for url, doc in modelXbrl.urlDocs.items():
|
|
574
|
+
path = Path(url)
|
|
575
|
+
pathInfo = uploadContents.uploadPathsByFullPath.get(path)
|
|
576
|
+
if pathInfo is None or not pathInfo.isCoverPage:
|
|
577
|
+
continue
|
|
578
|
+
yield doc
|
|
579
|
+
|
|
580
|
+
def iterFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
|
|
581
|
+
yield from modelXbrl.factsByQname.get(qname, set())
|
|
582
|
+
|
|
583
|
+
def iterValidFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
|
|
584
|
+
for fact in self.iterFacts(modelXbrl, qname):
|
|
585
|
+
if fact.xValid >= VALID:
|
|
586
|
+
yield fact
|
|
587
|
+
|
|
588
|
+
def iterValidNonNilFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
|
|
589
|
+
for fact in self.iterValidFacts(modelXbrl, qname):
|
|
590
|
+
if not fact.isNil:
|
|
591
|
+
yield fact
|
|
592
|
+
|
|
593
|
+
def addUsedFilepath(self, modelXbrl: ModelXbrl, path: Path) -> None:
|
|
594
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
595
|
+
controllerPluginData.addUsedFilepath(path)
|