arelle-release 2.37.46__py3-none-any.whl → 2.37.48__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.
Potentially problematic release.
This version of arelle-release might be problematic. Click here for more details.
- arelle/CntlrCmdLine.py +10 -1
- arelle/ErrorManager.py +14 -5
- arelle/ModelObjectFactory.py +18 -2
- arelle/Validate.py +4 -0
- arelle/_version.py +2 -2
- arelle/plugin/validate/DBA/ValidationPluginExtension.py +2 -1
- arelle/plugin/validate/EDINET/ControllerPluginData.py +84 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +0 -114
- arelle/plugin/validate/EDINET/UploadContents.py +17 -0
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +8 -2
- arelle/plugin/validate/EDINET/__init__.py +5 -0
- arelle/plugin/validate/EDINET/rules/upload.py +66 -75
- arelle/plugin/validate/NL/ValidationPluginExtension.py +3 -1
- arelle/plugin/validate/ROS/ValidationPluginExtension.py +3 -1
- arelle/utils/PluginHooks.py +32 -0
- arelle/utils/validate/ValidationPlugin.py +54 -8
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/METADATA +1 -1
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/RECORD +22 -106
- 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/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.37.48.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.37.48.dist-info}/top_level.txt +0 -0
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
'''
|
|
2
|
-
This module is an example to convert Html Tables into Xlsx (Excel) tables
|
|
3
|
-
Preconfigured here to use SEC Edgar Rendering R files as input
|
|
4
|
-
|
|
5
|
-
See COPYRIGHT.md for copyright information.
|
|
6
|
-
'''
|
|
7
|
-
import os, sys
|
|
8
|
-
import regex as re
|
|
9
|
-
from lxml import etree, html
|
|
10
|
-
from openpyxl.workbook import Workbook
|
|
11
|
-
from openpyxl.worksheet import ColumnDimension
|
|
12
|
-
from openpyxl.cell import get_column_letter
|
|
13
|
-
from openpyxl.style import Alignment
|
|
14
|
-
|
|
15
|
-
class Report():
|
|
16
|
-
def __init__(self, longName, shortName, htmlFileName):
|
|
17
|
-
self.longName = longName
|
|
18
|
-
self.shortName = shortName
|
|
19
|
-
self.htmlFileName = htmlFileName
|
|
20
|
-
def __repr__(self):
|
|
21
|
-
return ("report(longName='{}', shortName='{}', htmlFileName='{}')"
|
|
22
|
-
.format(self.longName, self.shortName, self.htmlFileName))
|
|
23
|
-
|
|
24
|
-
def intCol(elt, attrName, default=None):
|
|
25
|
-
try:
|
|
26
|
-
return int(elt.get(attrName, default))
|
|
27
|
-
except (TypeError, ValueError):
|
|
28
|
-
return default
|
|
29
|
-
|
|
30
|
-
numberPattern = re.compile(r"\s*([$]\s*)?[(]?\s*[+-]?[0-9,]+([.][0-9]*)?[)-]?\s*$")
|
|
31
|
-
displayNonePattern = re.compile(r"\s*display:\s*none;")
|
|
32
|
-
|
|
33
|
-
def saveTableToExelle(rFilesDir):
|
|
34
|
-
|
|
35
|
-
# get reports from FilingSummary
|
|
36
|
-
reports = []
|
|
37
|
-
try:
|
|
38
|
-
fsdoc = etree.parse(os.path.join(rFilesDir, "FilingSummary.xml"))
|
|
39
|
-
for rElt in fsdoc.iter(tag="Report"):
|
|
40
|
-
reports.append(Report(rElt.findtext("LongName"),
|
|
41
|
-
rElt.findtext("ShortName"),
|
|
42
|
-
rElt.findtext("HtmlFileName")))
|
|
43
|
-
except (EnvironmentError,
|
|
44
|
-
etree.LxmlError) as err:
|
|
45
|
-
print("FilingSummary.xml: directory {0} error: {1}".format(rFilesDir, err))
|
|
46
|
-
|
|
47
|
-
wb = Workbook(encoding='utf-8')
|
|
48
|
-
# remove predefined sheets
|
|
49
|
-
for sheetName in wb.get_sheet_names():
|
|
50
|
-
ws = wb.get_sheet_by_name(sheetName)
|
|
51
|
-
if ws is not None:
|
|
52
|
-
wb.remove_sheet(ws)
|
|
53
|
-
|
|
54
|
-
sheetNames = set() # prevent duplicates
|
|
55
|
-
|
|
56
|
-
for reportNum, report in enumerate(reports):
|
|
57
|
-
sheetName = report.shortName[:31] # max length 31 for excel title
|
|
58
|
-
if sheetName in sheetNames:
|
|
59
|
-
sheetName = sheetName[:31-len(str(reportNum))] + str(reportNum)
|
|
60
|
-
sheetNames.add(sheetName)
|
|
61
|
-
ws = wb.create_sheet(title=sheetName)
|
|
62
|
-
|
|
63
|
-
try:
|
|
64
|
-
# doesn't detect utf-8 encoding the normal way, pass it a string
|
|
65
|
-
#htmlSource = ''
|
|
66
|
-
#with open(os.path.join(rFilesDir, report.htmlFileName), 'rt', encoding='utf-8') as fh:
|
|
67
|
-
# htmlSource = fh.read()
|
|
68
|
-
#rdoc = html.document_fromstring(htmlSource)
|
|
69
|
-
rdoc = html.parse(os.path.join(rFilesDir, report.htmlFileName))
|
|
70
|
-
row = -1
|
|
71
|
-
mergedAreas = {} # colNumber: (colspan,lastrow)
|
|
72
|
-
for tableElt in rdoc.iter(tag="table"):
|
|
73
|
-
# skip pop up tables
|
|
74
|
-
if tableElt.get("class") == "authRefData":
|
|
75
|
-
continue
|
|
76
|
-
if tableElt.getparent().tag == "div":
|
|
77
|
-
style = tableElt.getparent().get("style")
|
|
78
|
-
if style and displayNonePattern.match(style):
|
|
79
|
-
continue
|
|
80
|
-
colWidths = {}
|
|
81
|
-
for rowNum, trElt in enumerate(tableElt.iter(tag="tr")):
|
|
82
|
-
# remove passed mergedAreas
|
|
83
|
-
for mergeCol in [col
|
|
84
|
-
for col, mergedArea in mergedAreas.items()
|
|
85
|
-
if mergedArea[1] > rowNum]:
|
|
86
|
-
del mergedAreas[mergeCol]
|
|
87
|
-
col = 0
|
|
88
|
-
for coltag in ("th", "td"):
|
|
89
|
-
for cellElt in trElt.iter(tag=coltag):
|
|
90
|
-
if col == 0:
|
|
91
|
-
row += 1 # new row
|
|
92
|
-
if col in mergedAreas:
|
|
93
|
-
col += mergedAreas[col][0] - 1
|
|
94
|
-
text = cellElt.text_content()
|
|
95
|
-
colspan = intCol(cellElt, "colspan", 1)
|
|
96
|
-
rowspan = intCol(cellElt, "rowspan", 1)
|
|
97
|
-
#if col not in colWidths:
|
|
98
|
-
# colWidths[col] = 10.0 # some kind of default width
|
|
99
|
-
for elt in cellElt.iter():
|
|
100
|
-
style = elt.get("style")
|
|
101
|
-
if style and "width:" in style:
|
|
102
|
-
try:
|
|
103
|
-
kw, sep, width = style.partition("width:")
|
|
104
|
-
if "px" in width:
|
|
105
|
-
width, sep, kw = width.partition("px")
|
|
106
|
-
width = float(width) * 0.67777777
|
|
107
|
-
else:
|
|
108
|
-
width = float(width)
|
|
109
|
-
colWidths[col] = width
|
|
110
|
-
except ValueError:
|
|
111
|
-
pass
|
|
112
|
-
if rowspan > 1:
|
|
113
|
-
mergedAreas[col] = (colspan, row + rowspan - 1)
|
|
114
|
-
cell = ws.cell(row=row,column=col)
|
|
115
|
-
if text:
|
|
116
|
-
cell.value = text
|
|
117
|
-
if numberPattern.match(text):
|
|
118
|
-
cell.style.alignment.horizontal = Alignment.HORIZONTAL_RIGHT
|
|
119
|
-
else:
|
|
120
|
-
cell.style.alignment.wrap_text = True
|
|
121
|
-
if colspan > 1 or rowspan > 1:
|
|
122
|
-
ws.merge_cells(start_row=row, end_row=row+rowspan-1, start_column=col, end_column=col+colspan-1)
|
|
123
|
-
cell.style.alignment.vertical = Alignment.VERTICAL_TOP
|
|
124
|
-
if coltag == "th":
|
|
125
|
-
cell.style.alignment.horizontal = Alignment.HORIZONTAL_CENTER
|
|
126
|
-
cell.style.font.bold = True
|
|
127
|
-
cell.style.font.size = 9 # some kind of default size
|
|
128
|
-
col += colspan
|
|
129
|
-
for col, width in colWidths.items():
|
|
130
|
-
ws.column_dimensions[get_column_letter(col+1)].width = width
|
|
131
|
-
except (EnvironmentError,
|
|
132
|
-
etree.LxmlError) as err:
|
|
133
|
-
print("{0}: directory {1} error: {2}".format(report.htmlFileName, rFilesDir, err))
|
|
134
|
-
|
|
135
|
-
wb.save(os.path.join(rFilesDir, "exelleOut.xlsx"))
|
|
136
|
-
|
|
137
|
-
if __name__ == "__main__":
|
|
138
|
-
|
|
139
|
-
# test directory
|
|
140
|
-
saveTableToExelle(r"C:\Users\Herm Fischer\Documents\mvsl\projects\SEC\14.1\R-files\wpoRfiles")
|
arelle/archive/TR3toTR4.py
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
'''
|
|
2
|
-
See COPYRIGHT.md for copyright information.
|
|
3
|
-
License: https://www.apache.org/licenses/LICENSE-2.0
|
|
4
|
-
|
|
5
|
-
usage: python3 {tr2 or tr3 input-file} {tr4 output-file}
|
|
6
|
-
'''
|
|
7
|
-
|
|
8
|
-
import io, os, sys, time
|
|
9
|
-
from lxml import etree
|
|
10
|
-
|
|
11
|
-
if len(sys.argv) < 3:
|
|
12
|
-
print("Please enter input and output filename arguments")
|
|
13
|
-
exit(1)
|
|
14
|
-
INFILE = sys.argv[1]
|
|
15
|
-
OUTFILE = sys.argv[2]
|
|
16
|
-
|
|
17
|
-
if not INFILE or not os.path.isfile(INFILE) or not OUTFILE:
|
|
18
|
-
print("Please enter existing input filename and output filename")
|
|
19
|
-
exit(1)
|
|
20
|
-
|
|
21
|
-
xhtmlNs = "http://www.w3.org/1999/xhtml"
|
|
22
|
-
ixNS = "http://www.xbrl.org/2013/inlineXBRL"
|
|
23
|
-
ixEltsWithFormat = ["{{{}}}{}".format(ixNS, localName) for localName in ("denominator", "numerator", "nonFraction", "nonNumeric")]
|
|
24
|
-
xhtmlRootElts = ["{{{}}}{}".format(xhtmlNs, localName) for localName in ("html", "xhtml")]
|
|
25
|
-
TR2NS = "http://www.xbrl.org/inlineXBRL/transformation/2011-07-31"
|
|
26
|
-
TR3NS = "http://www.xbrl.org/inlineXBRL/transformation/2015-02-26"
|
|
27
|
-
TR4NS = "http://www.xbrl.org/inlineXBRL/transformation/2020-02-12"
|
|
28
|
-
TR23to4 = {
|
|
29
|
-
"booleanfalse": "fixed-false",
|
|
30
|
-
"booleantrue": "fixed-true",
|
|
31
|
-
"calindaymonthyear": "date-ind-day-monthname-year-hi",
|
|
32
|
-
"datedaymonth": "date-day-month",
|
|
33
|
-
"datedaymonthdk": "date-day-monthname-da",
|
|
34
|
-
"datedaymonthen": "date-day-monthname-en",
|
|
35
|
-
"datedaymonthyear": "date-day-month-year",
|
|
36
|
-
"datedaymonthyeardk": "date-day-monthname-year-da",
|
|
37
|
-
"datedaymonthyearen": "date-day-monthname-year-en",
|
|
38
|
-
"datedaymonthyearin": "date-day-monthname-year-hi", # does not handle this: Use date-day-month-year when using Devanagari numerals for the month, otherwise use date-day-monthname-year-hi.
|
|
39
|
-
"dateerayearmonthdayjp": "date-jpn-era-year-month-day",
|
|
40
|
-
"dateerayearmonthjp": "date-jpn-era-year-month",
|
|
41
|
-
"datemonthday": "date-month-day",
|
|
42
|
-
"datemonthdayen": "date-monthname-day-en",
|
|
43
|
-
"datemonthdayyear": "date-month-day-year",
|
|
44
|
-
"datemonthdayyearen": "date-monthname-day-year-en",
|
|
45
|
-
"datemonthyear": "date-month-year",
|
|
46
|
-
"datemonthyeardk": "date-monthname-year-da",
|
|
47
|
-
"datemonthyearen": "date-monthname-year-en",
|
|
48
|
-
"datemonthyearin": "date-monthname-year-hi",
|
|
49
|
-
"dateyearmonthday": "date-year-month-day",
|
|
50
|
-
"dateyearmonthdaycjk": "date-year-month-day",
|
|
51
|
-
"dateyearmonthcjk": "date-year-month",
|
|
52
|
-
"dateyearmonthen": "date-year-monthname-en",
|
|
53
|
-
"nocontent": "fixed-empty",
|
|
54
|
-
"numcommadecimal": "num-comma-decimal",
|
|
55
|
-
"numdotdecimal": "num-dot-decimal",
|
|
56
|
-
"numdotdecimalin": "num-dot-decimal",
|
|
57
|
-
"numunitdecimal": "num-unit-decimal",
|
|
58
|
-
"numunitdecimalin": "num-unit-decimal",
|
|
59
|
-
"zerodash": "fixed-zero"
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
print("Parsing TR3 file {}".format(INFILE))
|
|
63
|
-
startedAt = time.time()
|
|
64
|
-
parser = etree.XMLParser(recover=True, huge_tree=True)
|
|
65
|
-
with io.open(INFILE, "rb") as fh:
|
|
66
|
-
doc = etree.parse(fh,parser=parser,base_url=INFILE)
|
|
67
|
-
|
|
68
|
-
# replace format TR3 with TR4
|
|
69
|
-
nsToChange = set()
|
|
70
|
-
for elt in doc.iter(*ixEltsWithFormat):
|
|
71
|
-
prefix, _sep, localName = elt.get("format", "").rpartition(":")
|
|
72
|
-
if localName:
|
|
73
|
-
ns = elt.nsmap.get(prefix)
|
|
74
|
-
if ns in (TR2NS, TR3NS):
|
|
75
|
-
if localName in TR23to4:
|
|
76
|
-
elt.set("format", "ixt:" + TR23to4[localName])
|
|
77
|
-
nsToChange.add(ns)
|
|
78
|
-
else:
|
|
79
|
-
print("{} line {}: nable to convert transform {}".format(elt.tag, elt.sourceline, elt.get("format")))
|
|
80
|
-
|
|
81
|
-
# replace TR2 and TR3 namespaces in any xmlns'ed element
|
|
82
|
-
outXhtml = etree.tostring(doc, encoding=doc.docinfo.encoding, xml_declaration=True)
|
|
83
|
-
for ns in nsToChange:
|
|
84
|
-
outXhtml = outXhtml.replace(ns.encode(), TR4NS.encode())
|
|
85
|
-
|
|
86
|
-
with io.open(OUTFILE, "wb", ) as fh:
|
|
87
|
-
fh.write(outXhtml)
|
|
88
|
-
print("Converted in {:.3f} secs, TR4 file {}".format(time.time()-startedAt, INFILE))
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
See COPYRIGHT.md for copyright information.
|
|
3
|
-
"""
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
from typing import Any
|
|
7
|
-
|
|
8
|
-
from arelle.ModelDocument import LoadingException, ModelDocument
|
|
9
|
-
from arelle.ModelXbrl import ModelXbrl
|
|
10
|
-
from arelle.Version import authorLabel, copyrightLabel
|
|
11
|
-
from arelle.typing import TypeGetText
|
|
12
|
-
from arelle.utils.PluginHooks import PluginHooks
|
|
13
|
-
|
|
14
|
-
_: TypeGetText
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class DeprecatedESEF2022Plugin(PluginHooks):
|
|
18
|
-
"""
|
|
19
|
-
The 'validate/ESEF_2022' plugin has been merged into the `validate/ESEF` plugin using disclosure systems to select the year.
|
|
20
|
-
This implementation only exists to raise an error and inform users to migrate their configuration.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
@staticmethod
|
|
24
|
-
def modelDocumentPullLoader(
|
|
25
|
-
modelXbrl: ModelXbrl,
|
|
26
|
-
normalizedUri: str,
|
|
27
|
-
filepath: str,
|
|
28
|
-
isEntry: bool,
|
|
29
|
-
namespace: str | None,
|
|
30
|
-
*args: Any,
|
|
31
|
-
**kwargs: Any,
|
|
32
|
-
) -> ModelDocument | LoadingException | None:
|
|
33
|
-
message = _("The 'validate/ESEF_2022' plugin has been combined with the 'validate/ESEF' plugin. Please use the 'validate/ESEF' plugin with a 2022 disclosure system.")
|
|
34
|
-
modelXbrl.error("plugin:deprecated", message)
|
|
35
|
-
return LoadingException(message)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
__pluginInfo__ = {
|
|
39
|
-
"name": "Validate ESMA ESEF-2022",
|
|
40
|
-
"version": "1.2023.00",
|
|
41
|
-
"description": "Deprecated: use the 'validate/ESEF' plugin with a 2022 disclosure system instead.",
|
|
42
|
-
"license": "Apache-2",
|
|
43
|
-
"author": authorLabel,
|
|
44
|
-
"copyright": copyrightLabel,
|
|
45
|
-
"import": ("inlineXbrlDocumentSet",),
|
|
46
|
-
"ModelDocument.PullLoader": DeprecatedESEF2022Plugin.modelDocumentPullLoader,
|
|
47
|
-
}
|
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
'''
|
|
2
|
-
BigInstance is an example of a plug-in to both GUI menu and command line/web service
|
|
3
|
-
that provides an alternative approach to big instance documents without building a DOM, to save
|
|
4
|
-
memory footprint. SAX is used to parse the big instance. ModelObjects are specialized by features
|
|
5
|
-
for efficiency and to avoid dependency on an underlying DOM.
|
|
6
|
-
|
|
7
|
-
See COPYRIGHT.md for copyright information.
|
|
8
|
-
'''
|
|
9
|
-
|
|
10
|
-
import xml.sax, io, sys
|
|
11
|
-
from collections import defaultdict
|
|
12
|
-
from arelle import XbrlConst, XmlUtil, XmlValidate
|
|
13
|
-
from arelle.ModelDocument import ModelDocument, Type
|
|
14
|
-
from arelle.ModelObject import ModelObject
|
|
15
|
-
from arelle.ModelValue import QName
|
|
16
|
-
from arelle.ModelInstanceObject import ModelContext, ModelFact, ModelUnit
|
|
17
|
-
from arelle.Version import authorLabel, copyrightLabel
|
|
18
|
-
|
|
19
|
-
class NotInstanceDocumentException(Exception):
|
|
20
|
-
def __init__(self):
|
|
21
|
-
pass
|
|
22
|
-
|
|
23
|
-
sharedEmptyDict = {}
|
|
24
|
-
sharedEmptyList = []
|
|
25
|
-
|
|
26
|
-
qnIDattr = QName(None, None, "id")
|
|
27
|
-
qnContextRefAttr = QName(None, None, "contextRef")
|
|
28
|
-
qnUnitRefAttr = QName(None, None, "unitRef")
|
|
29
|
-
qnPrecisionAttr = QName(None, None, "precision")
|
|
30
|
-
qnDecimalsAttr = QName(None, None, "decimals")
|
|
31
|
-
|
|
32
|
-
def initModelObject(obj, saxhandler, qname, attrs):
|
|
33
|
-
obj._elementQname = qname
|
|
34
|
-
obj._namespaceURI = qname.namespaceURI
|
|
35
|
-
obj._localName = qname.localName
|
|
36
|
-
obj.modelDocument = saxhandler.modelDocument
|
|
37
|
-
obj._sourceline = saxhandler.saxParser.getLineNumber()
|
|
38
|
-
obj._parent = saxhandler.qnameStack[0] if saxhandler.qnameStack else None
|
|
39
|
-
obj._attrs = dict((('{{{0}}}{1}'.format(*name) if name[0] else name[1]), value)
|
|
40
|
-
for name, value in attrs.items())
|
|
41
|
-
obj._elementText = ''
|
|
42
|
-
|
|
43
|
-
class BigInstModelObject(ModelObject):
|
|
44
|
-
def __init__(self, saxhandler, qname, attrs):
|
|
45
|
-
initModelObject(self, saxhandler, qname, attrs)
|
|
46
|
-
super(BigInstModelObject, self).init(saxhandler.modelDocument)
|
|
47
|
-
|
|
48
|
-
def getparent(self):
|
|
49
|
-
return self._parent
|
|
50
|
-
|
|
51
|
-
def items(self):
|
|
52
|
-
return self._attrs.items()
|
|
53
|
-
|
|
54
|
-
def get(self, clarkName):
|
|
55
|
-
return self._attrs.get(clarkName)
|
|
56
|
-
|
|
57
|
-
@property
|
|
58
|
-
def sourceline(self):
|
|
59
|
-
return self._sourceline
|
|
60
|
-
|
|
61
|
-
@property
|
|
62
|
-
def elementText(self):
|
|
63
|
-
return self._elementText
|
|
64
|
-
|
|
65
|
-
class BigInstContext(ModelContext):
|
|
66
|
-
def __init__(self, saxhandler, qname, attrs):
|
|
67
|
-
initModelObject(self, saxhandler, qname, attrs)
|
|
68
|
-
super(BigInstContext, self).init(saxhandler.modelDocument)
|
|
69
|
-
self._isStartEndPeriod = self._isInstantPeriod = self._isForeverPeriod = False
|
|
70
|
-
|
|
71
|
-
def getparent(self):
|
|
72
|
-
return self._parent
|
|
73
|
-
|
|
74
|
-
def items(self):
|
|
75
|
-
return self._attrs.items()
|
|
76
|
-
|
|
77
|
-
def get(self, clarkName):
|
|
78
|
-
return self._attrs.get(clarkName)
|
|
79
|
-
|
|
80
|
-
@property
|
|
81
|
-
def sourceline(self):
|
|
82
|
-
return self._sourceline
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def elementText(self):
|
|
86
|
-
return self._elementText
|
|
87
|
-
|
|
88
|
-
class BigInstUnit(ModelUnit):
|
|
89
|
-
def __init__(self, saxhandler, qname, attrs):
|
|
90
|
-
initModelObject(self, saxhandler, qname, attrs)
|
|
91
|
-
super(BigInstUnit, self).init(saxhandler.modelDocument)
|
|
92
|
-
self._measures = [[],[]]
|
|
93
|
-
|
|
94
|
-
def getparent(self):
|
|
95
|
-
return self._parent
|
|
96
|
-
|
|
97
|
-
def items(self):
|
|
98
|
-
return self._attrs.items()
|
|
99
|
-
|
|
100
|
-
def get(self, clarkName):
|
|
101
|
-
return self._attrs.get(clarkName)
|
|
102
|
-
|
|
103
|
-
@property
|
|
104
|
-
def sourceline(self):
|
|
105
|
-
return self._sourceline
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def elementText(self):
|
|
109
|
-
return self._elementText
|
|
110
|
-
|
|
111
|
-
class BigInstFact(ModelFact):
|
|
112
|
-
__slots__ = ("_parent", "_concept", "_attrs", "_sourceline", "_elementText",
|
|
113
|
-
"_context", "_conceptContextUnitLangHash",
|
|
114
|
-
"_isItem", "_isTuple", "_isNumeric", "_isFraction",
|
|
115
|
-
"_id", "_decimals", "_precision",
|
|
116
|
-
"modelDocument", "objectIndex", "modelTupleFacts",
|
|
117
|
-
"_parentBigInstObj", "_prevObj", "_nextObj",
|
|
118
|
-
"xValid", "xValue", "sValue", "xAttributes")
|
|
119
|
-
|
|
120
|
-
# reimplement ancestorQnames, parentQname
|
|
121
|
-
|
|
122
|
-
def __init__(self, saxhandler, qname, attrs):
|
|
123
|
-
self._concept = saxhandler.modelXbrl.qnameConcepts.get(qname) # use the qname object of the DTS, not parser
|
|
124
|
-
self.modelDocument = saxhandler.modelDocument
|
|
125
|
-
self._sourceline = saxhandler.saxParser.getLineNumber()
|
|
126
|
-
self._parent = saxhandler.qnameStack[0] if saxhandler.qnameStack else None
|
|
127
|
-
self._context = self._unit = self._decimals = self._precision = self._id = None
|
|
128
|
-
self.xValid = 0 # unvalidated
|
|
129
|
-
self._attrs = sharedEmptyDict # try with common shared emptyDict if no separate attributes
|
|
130
|
-
self._parentBigInstObj = self._prevObj = self._nextObj = 1234
|
|
131
|
-
for names, value in attrs.items():
|
|
132
|
-
attrNameURI, attrLocalName= names
|
|
133
|
-
if not attrNameURI:
|
|
134
|
-
if attrLocalName == "id":
|
|
135
|
-
self._id = value
|
|
136
|
-
elif attrLocalName == "decimals":
|
|
137
|
-
self._decimals = value
|
|
138
|
-
elif attrLocalName == "precision":
|
|
139
|
-
self._precision = value
|
|
140
|
-
elif attrLocalName == "contextRef":
|
|
141
|
-
self._context = saxhandler.modelXbrl.contexts.get(value, 0)
|
|
142
|
-
if self._context == 0: # provide dummmy non-none so attribute is present for validation
|
|
143
|
-
saxhandler.contextRefedFacts[value].append(self)
|
|
144
|
-
elif attrLocalName == "unitRef":
|
|
145
|
-
self._unit = saxhandler.modelXbrl.units.get(value, 0)
|
|
146
|
-
if self._unit == 0:
|
|
147
|
-
saxhandler.unitRefedFacts[value].append(self)
|
|
148
|
-
else:
|
|
149
|
-
if not self._attrs: self.attrs = {} # stop using common shared emptyDict
|
|
150
|
-
self._attrs[attrLocalName] = value
|
|
151
|
-
else:
|
|
152
|
-
if not self._attrs: self.attrs = {} # stop using common shared emptyDict
|
|
153
|
-
self._attrs['{{{0}}}{1}'.format(attrNameURI, attrLocalName)] = value
|
|
154
|
-
self._elementText = ''
|
|
155
|
-
self.modelTupleFacts = sharedEmptyList
|
|
156
|
-
super(ModelFact, self).init(saxhandler.modelDocument)
|
|
157
|
-
|
|
158
|
-
def getparent(self):
|
|
159
|
-
return self._parent
|
|
160
|
-
|
|
161
|
-
def items(self):
|
|
162
|
-
return self._attrs.items()
|
|
163
|
-
|
|
164
|
-
def get(self, clarkName):
|
|
165
|
-
return self._attrs.get(clarkName)
|
|
166
|
-
|
|
167
|
-
@property
|
|
168
|
-
def sourceline(self):
|
|
169
|
-
return self._sourceline
|
|
170
|
-
|
|
171
|
-
@property
|
|
172
|
-
def concept(self):
|
|
173
|
-
return self._concept
|
|
174
|
-
|
|
175
|
-
@property
|
|
176
|
-
def qname(self):
|
|
177
|
-
return self._concept.qname
|
|
178
|
-
|
|
179
|
-
@property
|
|
180
|
-
def elementQname(self):
|
|
181
|
-
return self._concept.qname
|
|
182
|
-
|
|
183
|
-
@property
|
|
184
|
-
def namespaceURI(self):
|
|
185
|
-
return self._concept.qname.namespaceURI
|
|
186
|
-
|
|
187
|
-
@property
|
|
188
|
-
def localName(self):
|
|
189
|
-
return self._concept.qname.localName
|
|
190
|
-
|
|
191
|
-
@property
|
|
192
|
-
def elementText(self):
|
|
193
|
-
return self._elementText
|
|
194
|
-
|
|
195
|
-
@property
|
|
196
|
-
def contextID(self):
|
|
197
|
-
if self._context is not None:
|
|
198
|
-
return self._context.id
|
|
199
|
-
return None
|
|
200
|
-
|
|
201
|
-
@property
|
|
202
|
-
def slottedAttributesNames(self):
|
|
203
|
-
names = set()
|
|
204
|
-
if self._id: names.add(qnIDattr)
|
|
205
|
-
if self._context is not None: names.add(qnContextRefAttr)
|
|
206
|
-
if self._unit is not None: names.add(qnUnitRefAttr)
|
|
207
|
-
if self._decimals is not None: names.add(qnDecimalsAttr)
|
|
208
|
-
if self._precision is not None: names.add(qnPrecisionAttr)
|
|
209
|
-
return names
|
|
210
|
-
|
|
211
|
-
@property
|
|
212
|
-
def unitID(self):
|
|
213
|
-
if self._unit is not None:
|
|
214
|
-
return self._unit.id
|
|
215
|
-
return None
|
|
216
|
-
|
|
217
|
-
class saxHandler(xml.sax.ContentHandler):
|
|
218
|
-
def __init__(self, saxParser, modelXbrl, mappedUri, filepath):
|
|
219
|
-
self.saxParser = saxParser
|
|
220
|
-
self.modelXbrl = modelXbrl
|
|
221
|
-
self.mappedUri = mappedUri
|
|
222
|
-
self.filepath = filepath
|
|
223
|
-
self.nsmap = {}
|
|
224
|
-
self.prefixmap = {}
|
|
225
|
-
self.qnameStack = []
|
|
226
|
-
self.currentNamespaceURI = None
|
|
227
|
-
self.modelDocument = None
|
|
228
|
-
self.contextRefedFacts = defaultdict(list)
|
|
229
|
-
self.unitRefedFacts = defaultdict(list)
|
|
230
|
-
|
|
231
|
-
def qname(self, prefixedName):
|
|
232
|
-
prefix, sep, localName = prefixedName.rpartition(":")
|
|
233
|
-
return QName(prefix, self.nsmap.get(prefix,None), localName)
|
|
234
|
-
|
|
235
|
-
def startPrefixMapping(self, prefix, uri):
|
|
236
|
-
self.nsmap[prefix] = uri
|
|
237
|
-
self.prefixmap[uri] = prefix
|
|
238
|
-
|
|
239
|
-
def endPrefixMapping(self, prefix):
|
|
240
|
-
if prefix in self.nsmap:
|
|
241
|
-
self.prefixmap.pop(self.nsmap[prefix], None)
|
|
242
|
-
self.nsmap.pop(prefix, None)
|
|
243
|
-
|
|
244
|
-
def startElementNS(self, name, qname, attrs):
|
|
245
|
-
namespaceURI, localName = name
|
|
246
|
-
prefix = self.prefixmap.get(namespaceURI, None)
|
|
247
|
-
thisQname = QName(prefix, namespaceURI, localName)
|
|
248
|
-
if not self.qnameStack:
|
|
249
|
-
if thisQname != XbrlConst.qnXbrliXbrl: # not an instance document
|
|
250
|
-
self.modelXbrl = None # dereference
|
|
251
|
-
self.saxParser = None
|
|
252
|
-
raise NotInstanceDocumentException()
|
|
253
|
-
if self.modelDocument is None:
|
|
254
|
-
self.modelDocument = ModelDocument(self.modelXbrl, Type.INSTANCE, self.mappedUri, self.filepath, None)
|
|
255
|
-
parentQname = None
|
|
256
|
-
elif self.qnameStack:
|
|
257
|
-
parentElement = self.qnameStack[0]
|
|
258
|
-
parentQname = parentElement.elementQname
|
|
259
|
-
if namespaceURI in (XbrlConst.xbrli, XbrlConst.link):
|
|
260
|
-
if parentQname == XbrlConst.qnXbrliContext:
|
|
261
|
-
if localName == "identifier":
|
|
262
|
-
parentElement._entityIdentifier = (attrs.get(None,"scheme"), "")
|
|
263
|
-
elif localName == "forever":
|
|
264
|
-
parentElement._isForeverPeriod = True
|
|
265
|
-
else:
|
|
266
|
-
if localName == "context":
|
|
267
|
-
thisModelObject = BigInstContext(self, thisQname, attrs)
|
|
268
|
-
self.modelXbrl.contexts[thisModelObject.id] = thisModelObject
|
|
269
|
-
elif localName == "unit":
|
|
270
|
-
thisModelObject = BigInstUnit(self, thisQname, attrs)
|
|
271
|
-
self.modelXbrl.units[thisModelObject.id] = thisModelObject
|
|
272
|
-
else:
|
|
273
|
-
thisModelObject = BigInstModelObject(self, thisQname, attrs)
|
|
274
|
-
if localName in ("schemaRef", "linkbaseRef"):
|
|
275
|
-
self.modelDocument.discoverHref(thisModelObject)
|
|
276
|
-
else:
|
|
277
|
-
self.qnameStack.insert(0, thisModelObject)
|
|
278
|
-
elif parentQname:
|
|
279
|
-
if parentQname == XbrlConst.qnXbrliContext:
|
|
280
|
-
if namespaceURI == XbrlConst.xbrldi:
|
|
281
|
-
if localName == "explicitMember":
|
|
282
|
-
self.dimensionPrefixedName = attrs.get(None,"dimension")
|
|
283
|
-
else: # might be a fact
|
|
284
|
-
thisModelObject = BigInstFact(self, thisQname, attrs)
|
|
285
|
-
if len(self.qnameStack) > 1:
|
|
286
|
-
tuple = self.qnameStack[0]
|
|
287
|
-
if not tuple.modelTupleFacts:
|
|
288
|
-
tuple.modelTupleFacts = [] # allocate unshared list
|
|
289
|
-
tuple.modelTupleFacts.append(thisModelObject)
|
|
290
|
-
else:
|
|
291
|
-
self.modelXbrl.facts.append(thisModelObject)
|
|
292
|
-
self.qnameStack.insert(0, thisModelObject) # build content
|
|
293
|
-
self.currentNamespaceURI = namespaceURI
|
|
294
|
-
self.currentLocalName = localName
|
|
295
|
-
|
|
296
|
-
def endElementNS(self, name, qname):
|
|
297
|
-
thisQname = QName(None, *name)
|
|
298
|
-
if self.qnameStack and self.qnameStack[0].elementQname == thisQname:
|
|
299
|
-
elt = self.qnameStack.pop(0)
|
|
300
|
-
if elt.namespaceURI == XbrlConst.xbrli:
|
|
301
|
-
if elt.localName == "unit":
|
|
302
|
-
elt._measures = (sorted(elt._measures[0]), sorted(elt._measures[1]))
|
|
303
|
-
if elt.id in self.unitRefedFacts:
|
|
304
|
-
for fact in self.unitRefedFacts[elt.id]:
|
|
305
|
-
fact._unit = elt
|
|
306
|
-
del self.unitRefedFacts[elt.id]
|
|
307
|
-
elif elt.localName == "context":
|
|
308
|
-
if elt.id in self.contextRefedFacts:
|
|
309
|
-
for fact in self.contextRefedFacts[elt.id]:
|
|
310
|
-
fact._context = elt
|
|
311
|
-
del self.contextRefedFacts[elt.id]
|
|
312
|
-
self.currentNamespaceURI = None
|
|
313
|
-
self.currentLocalName = None
|
|
314
|
-
XmlValidate.validate(self.modelXbrl, elt, recurse=False)
|
|
315
|
-
pass
|
|
316
|
-
|
|
317
|
-
def characters(self, content):
|
|
318
|
-
if self.currentNamespaceURI:
|
|
319
|
-
elt = self.qnameStack[0]
|
|
320
|
-
if self.currentNamespaceURI == XbrlConst.xbrli:
|
|
321
|
-
s = content.strip()
|
|
322
|
-
if s:
|
|
323
|
-
if self.currentLocalName == "identifier":
|
|
324
|
-
elt._entityIdentifier = (elt._entityIdentifier[0], elt._entityIdentifier[1] + content)
|
|
325
|
-
elif self.currentLocalName == "startDate":
|
|
326
|
-
elt._startDatetime = XmlUtil.datetimeValue(s)
|
|
327
|
-
elt._isStartEndPeriod = True
|
|
328
|
-
elif self.currentLocalName == "endDate":
|
|
329
|
-
elt._endDatetime = XmlUtil.datetimeValue(s, addOneDay=True)
|
|
330
|
-
elt._isStartEndPeriod = True
|
|
331
|
-
elif self.currentLocalName == "instant":
|
|
332
|
-
elt._endDatetime = elt._instantDatetime = XmlUtil.datetimeValue(s, addOneDay=True)
|
|
333
|
-
elt._isInstantPeriod = True
|
|
334
|
-
elif self.currentLocalName == "measure":
|
|
335
|
-
m = self.qname(content)
|
|
336
|
-
parentEltLocalName = self.qnameStack[1].localName
|
|
337
|
-
if parentEltLocalName == "unit":
|
|
338
|
-
self.qnameStack[1]._measures[0].append(m)
|
|
339
|
-
elif parentEltLocalName == "unitNumerator" and self.qnameStack[2].localName == "unit":
|
|
340
|
-
self.qnameStack[2]._measures[0].append(m)
|
|
341
|
-
elif parentEltLocalName == "unitDenominator" and self.qnameStack[2].localName == "unit":
|
|
342
|
-
self.qnameStack[2]._measures[1].append(m)
|
|
343
|
-
elif self.currentNamespaceURI == XbrlConst.xbrldi:
|
|
344
|
-
s = content.strip()
|
|
345
|
-
if s:
|
|
346
|
-
if self.currentLocalName == "explicitMember" and self.dimensionPrefixedName:
|
|
347
|
-
dimQname = self.qname(self.currentLocalName)
|
|
348
|
-
memQname = self.qname(s)
|
|
349
|
-
dimConcept = self.modelXbrl.qnameConcepts.get(dimQname)
|
|
350
|
-
memConcept = self.modelXbrl.qnameConcepts.get(memQname)
|
|
351
|
-
elif elt is not None:
|
|
352
|
-
elt._elementText += content
|
|
353
|
-
|
|
354
|
-
def skippedEntity(self, name):
|
|
355
|
-
print ("skipped entity={0}".format(name))
|
|
356
|
-
|
|
357
|
-
class BigInstDocument:
|
|
358
|
-
def __init__(self, file, xbrlParser, saxParser, saxHandler):
|
|
359
|
-
self.file = file
|
|
360
|
-
self.xbrlParser = xbrlParser
|
|
361
|
-
self.saxParser = saxParser
|
|
362
|
-
self.saxHandler = saxHandler
|
|
363
|
-
|
|
364
|
-
def getroot(self):
|
|
365
|
-
return self.saxHandler.qnameStack[-1]
|
|
366
|
-
|
|
367
|
-
def bigInstLoader(modelXbrl, file, mappedUri, filepath):
|
|
368
|
-
saxParser = xml.sax.make_parser()
|
|
369
|
-
saxParser.setFeature("http://xml.org/sax/features/namespaces", True)
|
|
370
|
-
saxParser.setFeature("http://xml.org/sax/features/external-general-entities", True)
|
|
371
|
-
saxhandler = saxHandler(saxParser, modelXbrl, mappedUri, filepath)
|
|
372
|
-
saxParser.setContentHandler(saxhandler)
|
|
373
|
-
try:
|
|
374
|
-
saxParser.parse(file)
|
|
375
|
-
return saxhandler.modelDocument
|
|
376
|
-
except NotInstanceDocumentException:
|
|
377
|
-
file.seek(0,io.SEEK_SET) # allow reparsing
|
|
378
|
-
return None
|
|
379
|
-
|
|
380
|
-
'''
|
|
381
|
-
Do not use _( ) in pluginInfo itself (it is applied later, after loading
|
|
382
|
-
'''
|
|
383
|
-
|
|
384
|
-
__pluginInfo__ = {
|
|
385
|
-
'name': 'Big Instance Loader',
|
|
386
|
-
'version': '0.9',
|
|
387
|
-
'description': "This plug-in loads big XBRL instances without building a DOM in memory. "
|
|
388
|
-
"SAX parses XBRL directly into an object model without a DOM. ",
|
|
389
|
-
'license': 'Apache-2',
|
|
390
|
-
'author': authorLabel,
|
|
391
|
-
'copyright': copyrightLabel,
|
|
392
|
-
# classes of mount points (required)
|
|
393
|
-
'ModelDocument.CustomLoader': bigInstLoader,
|
|
394
|
-
}
|