arelle-release 2.36.27__py3-none-any.whl → 2.36.28__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/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.36.27'
21
- __version_tuple__ = version_tuple = (2, 36, 27)
20
+ __version__ = version = '2.36.28'
21
+ __version_tuple__ = version_tuple = (2, 36, 28)
@@ -1,105 +1,222 @@
1
- '''
1
+ """
2
2
  Save HTML EBA Tables is an example of a plug-in to both GUI menu and command line/web service
3
3
  that will save a directory containing HTML Tablesets with an EBA index page.
4
4
 
5
5
  See COPYRIGHT.md for copyright information.
6
- '''
7
- from arelle.Version import authorLabel, copyrightLabel
6
+ """
8
7
 
9
- def generateHtmlEbaTablesetFiles(dts, indexFile, lang="en"):
10
- try:
11
- import os, io
12
- from arelle import Version, XbrlConst, XmlUtil
13
- from arelle.ViewFileRenderedGrid import viewRenderedGrid
14
- from arelle.ModelRenderingObject import DefnMdlTable
8
+ import os
9
+ import threading
10
+ from operator import itemgetter
11
+ from optparse import OptionParser
12
+ from tkinter import Menu
13
+ from typing import Any
15
14
 
16
- numTableFiles = 0
15
+ from lxml import etree
16
+
17
+ from arelle import Version, XbrlConst, XmlUtil
18
+ from arelle.CntlrCmdLine import CntlrCmdLine
19
+ from arelle.CntlrWinMain import CntlrWinMain
20
+ from arelle.ModelDocument import Type
21
+ from arelle.ModelObjectFactory import parser
22
+ from arelle.ModelRenderingObject import DefnMdlTable
23
+ from arelle.ModelXbrl import ModelXbrl
24
+ from arelle.rendering import RenderingEvaluator
25
+ from arelle.RuntimeOptions import RuntimeOptions
26
+ from arelle.typing import TypeGetText
27
+ from arelle.utils.PluginHooks import PluginHooks
28
+ from arelle.ViewFileRenderedGrid import viewRenderedGrid
29
+
30
+ _: TypeGetText
31
+
32
+ MENU_HTML = """<!DOCTYPE html>
33
+ <html>
34
+ <head>
35
+ <style>
36
+ body {
37
+ margin: 0;
38
+ padding: 10px;
39
+ font-family: Arial, sans-serif;
40
+ color: #243e5e;
41
+ }
42
+ .nav-list-menu-ul {
43
+ list-style-type: none;
44
+ padding: 0;
45
+ margin: 0;
46
+ }
47
+ .nav-list-menu-li {
48
+ margin: 5px 0;
49
+ padding: 5px;
50
+ border-bottom: 1px solid #eee;
51
+ }
52
+ .nav-list-menu-link {
53
+ text-decoration: none;
54
+ cursor: pointer;
55
+ display: block;
56
+ background: none;
57
+ border: none;
58
+ padding: 0;
59
+ }
60
+ .nav-list-menu-link:hover {
61
+ text-decoration: underline;
62
+ }
63
+ </style>
64
+ </head>
65
+ <body>
66
+ <ul class="nav-list-menu-ul"/>
67
+ </body>
68
+ </html>
69
+ """
17
70
 
18
- file = io.StringIO('''
19
- <html xmlns="http://www.w3.org/1999/xhtml">
20
- <head id="Left">
21
- <link type="text/css" rel="stylesheet" href="http://arelle.org/files/EBA/style20121210/eba.css" />
71
+ CENTER_LANDING_HTML = """<!DOCTYPE html>
72
+ <html>
73
+ <head>
74
+ <style>
75
+ body {
76
+ margin: 0;
77
+ padding: 20px;
78
+ font-family: Arial, sans-serif;
79
+ }
80
+ #page-title {
81
+ margin-bottom: 20px;
82
+ }
83
+ #page-title h1 {
84
+ color: #243e5e;
85
+ margin-top: 0;
86
+ }
87
+ #content-center {
88
+ margin-top: 20px;
89
+ line-height: 1.5;
90
+ }
91
+ </style>
22
92
  </head>
23
- <body class="LTR IE7 ENGB">
24
- <ul class="CMSListMenuUL" id="Vertical2"/>
93
+ <body>
94
+ <div id="page-title">
95
+ <h1>Taxonomy Tables Viewer</h1>
96
+ </div>
97
+ <div id="content-center">
98
+ <p>Please select tables to view by clicking in the left column.</p>
99
+ </div>
100
+ </body>
101
+ </html>
102
+ """
103
+
104
+ TABLE_CSS_EXTRAS = """
105
+ table {background:#fff}
106
+ """
107
+
108
+
109
+ def indexFileHTML(indexBaseName: str) -> str:
110
+ return f'''<!DOCTYPE html>
111
+ <html>
112
+ <head>
113
+ <meta charset="utf-8">
114
+ <meta name="viewport" content="width=device-width, initial-scale=1">
115
+ <title>EBA - Tablesets</title>
116
+ <style>
117
+ html, body {{
118
+ margin: 0;
119
+ padding: 0;
120
+ height: 100%;
121
+ width: 100%;
122
+ font-family: Arial, sans-serif;
123
+ display: flex;
124
+ flex-direction: column;
125
+ }}
126
+ #header {{
127
+ background: rgb(36, 62, 94);
128
+ color: rgb(255, 255, 255);
129
+ height: 40px;
130
+ }}
131
+ #header h1 {{
132
+ font-size: 1.5em;
133
+ margin: 0.25em;
134
+ }}
135
+ #main-container {{
136
+ display: flex;
137
+ flex: 1;
138
+ height: calc(100vh - 40px);
139
+ }}
140
+ #menu-container {{
141
+ width: 360px;
142
+ border-right: 2px solid #243e5e;
143
+ overflow-y: auto;
144
+ box-sizing: border-box;
145
+ }}
146
+ #content-container {{
147
+ flex: 1;
148
+ overflow: auto;
149
+ box-sizing: border-box;
150
+ }}
151
+ iframe {{
152
+ border: none;
153
+ width: 100%;
154
+ height: 100%;
155
+ }}
156
+ </style>
157
+ <script>
158
+ function loadContent(url) {{
159
+ document.getElementById('content-frame').src = url;
160
+ }}
161
+ </script>
162
+ </head>
163
+ <body>
164
+ <div id="header"><h1>EBA - Tablesets</h1></div>
165
+ <div id="main-container">
166
+ <div id="menu-container">
167
+ <iframe src="{indexBaseName}FormsFrame.html" width="100%" height="100%" frameborder="0" id="menu-frame"></iframe>
168
+ </div>
169
+ <div id="content-container">
170
+ <iframe src="{indexBaseName}CenterLanding.html" width="100%" height="100%" frameborder="0" id="content-frame"></iframe>
171
+ </div>
172
+ </div>
25
173
  </body>
26
174
  </html>
27
175
  '''
28
- )
29
- from arelle.ModelObjectFactory import parser
30
- parser, parserLookupName, parserLookupClass = parser(dts,None)
31
- from lxml import etree
32
- indexDocument = etree.parse(file,parser=parser,base_url=indexFile)
33
- file.close()
34
- #xmlDocument.getroot().init(self) ## is this needed ??
35
- for listElt in indexDocument.iter(tag="{http://www.w3.org/1999/xhtml}ul"):
36
- break
37
-
38
- class nonTkBooleanVar():
39
- def __init__(self, value=True):
40
- self.value = value
41
- def set(self, value):
42
- self.value = value
43
- def get(self):
44
- return self.value
45
-
46
- class View():
47
- def __init__(self, tableOrELR, ignoreDimValidity, xAxisChildrenFirst, yAxisChildrenFirst):
48
- self.tblELR = tableOrELR
49
- # context menu boolean vars (non-tkinter boolean
50
- self.ignoreDimValidity = nonTkBooleanVar(value=ignoreDimValidity)
51
- self.xAxisChildrenFirst = nonTkBooleanVar(value=xAxisChildrenFirst)
52
- self.yAxisChildrenFirst = nonTkBooleanVar(value=yAxisChildrenFirst)
176
+
177
+
178
+ def generateHtmlEbaTablesetFiles(dts: ModelXbrl, indexFile: str, lang: str = "en") -> None:
179
+ try:
180
+ numTableFiles = 0
181
+ _parser = parser(dts, None)[0]
182
+ menuFrameDocument = etree.fromstring(MENU_HTML, parser=_parser, base_url=indexFile)
183
+ listElt = menuFrameDocument.find(".//ul")
184
+ assert listElt is not None, "No list element in index document"
53
185
 
54
186
  indexBase = indexFile.rpartition(".")[0]
55
187
  groupTableRels = dts.modelXbrl.relationshipSet(XbrlConst.euGroupTable)
56
188
  modelTables = []
57
- tblCssExtras='''
58
- body {background-image:url('http://arelle.org/files/EBA/style20121210/lhsbackground.jpg')}
59
- table {background:#fff}
60
- '''
61
- # order number is missing
62
- def viewTable(modelTable):
189
+
190
+ def viewTable(modelTable: DefnMdlTable) -> None:
63
191
  if modelTable is None:
64
192
  return
65
- if isinstance(modelTable, (DefnMdlTable)):
66
- # status
67
- dts.modelManager.cntlr.addToLog("viewing: " + modelTable.id)
68
- # for table file name, use table ELR
69
- tblFile = os.path.join(os.path.dirname(indexFile), modelTable.id + ".html")
70
- tableName = modelTable.id
193
+ tableId = modelTable.id or ""
194
+ if isinstance(modelTable, DefnMdlTable):
195
+ dts.modelManager.cntlr.addToLog("viewing: " + tableId)
196
+ tblFile = os.path.join(os.path.dirname(indexFile), tableId + ".html")
197
+ tableName = tableId
71
198
  if tableName.startswith("eba_t"):
72
199
  tableName = tableName.removeprefix("eba_t")
73
- if tableName.startswith("srb_t"):
200
+ elif tableName.startswith("srb_t"):
74
201
  tableName = tableName.removeprefix("srb_t")
75
- viewRenderedGrid(dts,
76
- tblFile,
77
- lang=lang,
78
- cssExtras=tblCssExtras,
79
- table=tableName)
80
-
81
- # generaate menu entry
82
- elt = etree.SubElement(listElt, "{http://www.w3.org/1999/xhtml}li")
83
- elt.set("class", "CMSListMenuLI")
84
- elt.set("id", modelTable.id)
85
- elt = etree.SubElement(elt, "{http://www.w3.org/1999/xhtml}a")
202
+ viewRenderedGrid(dts, tblFile, lang=lang, cssExtras=TABLE_CSS_EXTRAS, table=tableName) # type: ignore[no-untyped-call]
203
+
204
+ elt = etree.SubElement(listElt, "li")
205
+ elt.set("class", "nav-list-menu-li")
206
+ elt.set("id", tableId)
207
+ elt = etree.SubElement(elt, "button")
86
208
  elt.text = modelTable.genLabel(lang=lang, strip=True)
87
- elt.set("class", "CMSListMenuLink")
88
- elt.set("href", "javascript:void(0)")
89
- elt.set("onClick", "javascript:parent.body.location.href='{0}';".format(modelTable.id + ".html"))
209
+ elt.set("class", "nav-list-menu-link")
210
+ elt.set("onClick", f"javascript:parent.loadContent('{tableId}.html');")
90
211
  elt.text = modelTable.genLabel(lang=lang, strip=True)
91
-
92
- else: # just a header
93
- # generaate menu entry
94
- elt = etree.SubElement(listElt, "{http://www.w3.org/1999/xhtml}li")
95
- elt.set("class", "CMSListMenuLink")
96
- elt.set("id", modelTable.id)
212
+ else:
213
+ elt = etree.SubElement(listElt, "li")
214
+ elt.set("id", tableId)
97
215
  elt.text = modelTable.label(lang=lang, strip=True)
98
216
 
99
217
  for rel in groupTableRels.fromModelObject(modelTable):
100
218
  viewTable(rel.toModelObject)
101
219
 
102
-
103
220
  for rootConcept in groupTableRels.rootConcepts:
104
221
  sourceline = 0
105
222
  for rel in dts.modelXbrl.relationshipSet(XbrlConst.euGroupTable).fromModelObject(rootConcept):
@@ -107,171 +224,129 @@ table {background:#fff}
107
224
  break
108
225
  modelTables.append((rootConcept, sourceline))
109
226
 
110
- for modelTable, order in sorted(modelTables, key=lambda x: x[1]):
227
+ for modelTable, _order in sorted(modelTables, key=itemgetter(1)):
111
228
  viewTable(modelTable)
112
229
 
113
- with open(indexBase + "FormsFrame.html", "wt", encoding="utf-8") as fh:
114
- XmlUtil.writexml(fh, indexDocument, encoding="utf-8")
230
+ with open(indexBase + "FormsFrame.html", "w", encoding="utf-8") as fh:
231
+ XmlUtil.writexml(fh, menuFrameDocument, encoding="utf-8")
115
232
 
116
- with open(indexFile, "wt", encoding="utf-8") as fh:
117
- fh.write(
118
- '''
119
- <html xmlns="http://www.w3.org/1999/xhtml">
120
- <head id="Head1">
121
- <title>European Banking Authority - EBA - FINREP Taxonomy</title>
122
- <meta name="generator" content="Arelle(r) {0}" />
123
- <meta name="provider" content="Aguilonius(r)" />
124
- <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
125
- <meta http-equiv="pragma" content="no-cache" />
126
- <meta http-equiv="content-style-type" content="text/css" />
127
- <meta http-equiv="content-script-type" content="text/javascript" />
128
- <link type="text/css" rel="stylesheet" href="http://arelle.org/files/EBA/style20121210/eba.css" />
129
- </head>
130
- <frameset border="0" frameborder="0" rows="90,*">
131
- <frame name="head" src="{1}" scrolling="no" marginwidth="0" marginheight="10"/>
132
- <frameset bordercolor="#0000cc" border="10" frameborder="no" framespacing="0" cols="360, *">
133
- <frame src="{2}" name="menu" bordercolor="#0000cc"/>
134
- <frame src="{3}" name="body" bordercolor="#0000cc"/>
135
- </frameset>
136
- </frameset>
137
- '''.format(Version.version,
138
- os.path.basename(indexBase) + "TopFrame.html",
139
- os.path.basename(indexBase) + "FormsFrame.html",
140
- os.path.basename(indexBase) + "CenterLanding.html",
141
- ))
142
-
143
- with open(indexBase + "TopFrame.html", "wt", encoding="utf-8") as fh:
144
- fh.write(
145
- '''
146
- <html xmlns="http://www.w3.org/1999/xhtml">
147
- <head id="Top">
148
- <link type="text/css" rel="stylesheet" href="http://arelle.org/files/EBA/style20121210/eba.css" />
149
- </head>
150
- <body class="LTR IE7 ENGB">
151
- <div id="topsection">
152
- <div id="topsectionLeft" style="cursor:pointer;" onclick="location.href='http://www.eba.europa.eu/home.aspx';"></div>
153
- <div id="topsectionRight"></div>
154
- <div id="topnavigation">
155
- <ul id="menuElem">
156
- <li><a href="http://www.eba.europa.eu/topnav/Contacts.aspx">Contacts</a></li>
157
- <li><a href="http://www.eba.europa.eu/topnav/Links.aspx">Links</a></li>
158
- <li><a href="http://www.eba.europa.eu/topnav/Sitemap.aspx">Sitemap</a></li>
159
- <li><a href="http://www.eba.europa.eu/topnav/Legal-Notice.aspx">Legal Notice</a></li>
160
- </ul>
161
- </div>
162
- </body>
163
- </html>
164
- ''')
165
-
166
- with open(indexBase + "CenterLanding.html", "wt", encoding="utf-8") as fh:
167
- fh.write(
168
- '''
169
- <html xmlns="http://www.w3.org/1999/xhtml">
170
- <head id="Center">
171
- <link type="text/css" rel="stylesheet" href="http://http://arelle.org/files/EBA/style20121210/eba.css" />
172
- </head>
173
- <body class="LTR IE7 ENGB">
174
- <div id="plc_lt_zoneContent_usercontrol_userControlElem_ContentPanel">
175
- <div id="plc_lt_zoneContent_usercontrol_userControlElem_PanelTitle">
176
- <div id="pagetitle" style="float:left;width:500px;">
177
- <h1>Taxonomy Tables Viewer</h1>
178
- </div>
179
- </div>
180
- </div>
181
- <div style="clear:both;"></div>
182
- <div id="contentcenter">
183
- <p style="text-align: justify; margin-top: 0pt; margin-bottom: 0pt">Please select tables to view by clicking in the left column.</p>
184
- </div>
185
- </body>
186
- </html>
187
- ''')
233
+ with open(indexFile, "w", encoding="utf-8") as fh:
234
+ fh.write(indexFileHTML(os.path.basename(indexBase)))
188
235
 
189
- # to merge gif's and style sheets, use a zipfile sibling of the python plug-in file.
190
- #import zipfile
191
- #zf = zipfile.ZipFile(__file__.rpartition('.')[0] + "Files.zip", mode="r")
192
- #zf.extractall(path=os.path.dirname(indexBase))
193
- #zf.close()
236
+ with open(indexBase + "CenterLanding.html", "w", encoding="utf-8") as fh:
237
+ fh.write(CENTER_LANDING_HTML)
194
238
 
195
- dts.info("info:saveEBAtables",
196
- _("Tables index file of %(entryFile)s has %(numberTableFiles)s table files with index file %(indexFile)s."),
197
- modelObject=dts,
198
- entryFile=dts.uri, numberTableFiles=numTableFiles, indexFile=indexFile)
239
+ dts.info(
240
+ "info:saveEBAtables",
241
+ _("Tables index file of %(entryFile)s has %(numberTableFiles)s table files with index file %(indexFile)s."),
242
+ modelObject=dts,
243
+ entryFile=dts.uri,
244
+ numberTableFiles=numTableFiles,
245
+ indexFile=indexFile,
246
+ )
199
247
 
200
248
  dts.modelManager.showStatus(_("Saved EBA HTML Table Files"), 5000)
201
249
  except Exception as ex:
202
- dts.error("exception",
203
- _("HTML EBA Tableset files generation exception: %(error)s"), error=ex,
250
+ dts.error(
251
+ "exception",
252
+ _("HTML EBA Tableset files generation exception: %(error)s"),
253
+ error=ex,
204
254
  modelXbrl=dts,
205
- exc_info=True)
255
+ exc_info=True,
256
+ )
206
257
 
207
- def saveHtmlEbaTablesMenuEntender(cntlr, menu, *args, **kwargs):
208
- # Extend menu with an item for the save infoset plugin
209
- menu.add_command(label="Save HTML EBA Tables",
210
- underline=0,
211
- command=lambda: saveHtmlEbaTablesMenuCommand(cntlr) )
212
258
 
213
- def saveHtmlEbaTablesMenuCommand(cntlr):
214
- # save Infoset menu item has been invoked
215
- from arelle.ModelDocument import Type
259
+ def saveHtmlEbaTablesMenuCommand(cntlr: CntlrWinMain) -> None:
216
260
  if cntlr.modelManager is None or cntlr.modelManager.modelXbrl is None:
217
- cntlr.addToLog("No DTS loaded.")
261
+ cntlr.addToLog("No DTS loaded.") # type: ignore[no-untyped-call]
218
262
  return
219
263
 
220
- # get file name into which to save log file while in foreground thread
221
- indexFile = cntlr.uiFileDialog("save",
222
- title=_("arelle - Save HTML EBA Tables Index file"),
223
- initialdir=cntlr.config.setdefault("htmlEbaTablesFileDir","."),
224
- filetypes=[(_("HTML index file .html"), "*.html")],
225
- defaultextension=".html")
226
- if not indexFile:
227
- return False
228
- import os
264
+ assert cntlr.config is not None
265
+ indexFile = cntlr.uiFileDialog( # type: ignore[no-untyped-call]
266
+ "save",
267
+ title=_("arelle - Save HTML EBA Tables Index file"),
268
+ initialdir=cntlr.config.setdefault("htmlEbaTablesFileDir", "."),
269
+ filetypes=[(_("HTML index file .html"), "*.html")],
270
+ defaultextension=".html",
271
+ )
272
+ if not isinstance(indexFile, str):
273
+ return
229
274
  cntlr.config["htmlEbaTablesFileDir"] = os.path.dirname(indexFile)
230
275
  cntlr.saveConfig()
231
276
 
232
- import threading
233
- thread = threading.Thread(target=lambda
234
- _dts=cntlr.modelManager.modelXbrl,
235
- _indexFile=indexFile:
236
- generateHtmlEbaTablesetFiles(_dts, _indexFile))
277
+ thread = threading.Thread(
278
+ target=lambda _dts=cntlr.modelManager.modelXbrl, _indexFile=indexFile: generateHtmlEbaTablesetFiles(
279
+ _dts, _indexFile
280
+ )
281
+ )
237
282
  thread.daemon = True
238
283
  thread.start()
239
284
 
240
- def saveHtmlEbaTablesCommandLineOptionExtender(parser, *args, **kwargs):
241
- # extend command line options with a save DTS option
242
- parser.add_option("--save-EBA-tablesets",
243
- action="store",
244
- dest="ebaTablesetIndexFile",
245
- help=_("Save HTML EBA Tablesets index file, with tablest HTML files to out directory specify 'generateOutFiles'."))
246
-
247
- def saveHtmlEbaTablesCommandLineXbrlLoaded(cntlr, options, modelXbrl, *args, **kwargs):
248
- # extend XBRL-loaded run processing for this option
249
- from arelle.ModelDocument import Type
250
- if getattr(options, "ebaTablesetIndexFile", None) and options.ebaTablesetIndexFile == "generateEBAFiles" and modelXbrl.modelDocument.type in (Type.TESTCASESINDEX, Type.TESTCASE):
251
- cntlr.modelManager.generateEBAFiles = True
252
-
253
- def saveHtmlEbaTablesCommandLineXbrlRun(cntlr, options, modelXbrl, *args, **kwargs):
254
- # extend XBRL-loaded run processing for this option
255
- if getattr(options, "ebaTablesetIndexFile", None) and options.ebaTablesetIndexFile != "generateEBAFiles":
285
+
286
+ class SaveHtmlEbaTablesPlugin(PluginHooks):
287
+ @staticmethod
288
+ def cntlrWinMainMenuTools(
289
+ cntlr: CntlrWinMain,
290
+ menu: Menu,
291
+ *args: Any,
292
+ **kwargs: Any,
293
+ ) -> None:
294
+ menu.add_command(label="Save HTML EBA Tables", underline=0, command=lambda: saveHtmlEbaTablesMenuCommand(cntlr))
295
+
296
+ @staticmethod
297
+ def cntlrCmdLineOptions(
298
+ parser: OptionParser,
299
+ *args: Any,
300
+ **kwargs: Any,
301
+ ) -> None:
302
+ parser.add_option(
303
+ "--save-EBA-tablesets",
304
+ action="store",
305
+ dest="ebaTablesetIndexFile",
306
+ help=_("Save HTML EBA Tablesets index file with provided filename."),
307
+ )
308
+
309
+ @staticmethod
310
+ def cntlrCmdLineXbrlLoaded(
311
+ cntlr: CntlrCmdLine,
312
+ options: RuntimeOptions,
313
+ modelXbrl: ModelXbrl,
314
+ *args: Any,
315
+ **kwargs: Any,
316
+ ) -> None:
317
+ ebaTablesetIndexFile = getattr(options, "ebaTablesetIndexFile", None)
318
+ modelDocType = getattr(modelXbrl.modelDocument, "type", None)
319
+ if ebaTablesetIndexFile == "generateEBAFiles" and modelDocType in (Type.TESTCASESINDEX, Type.TESTCASE):
320
+ cntlr.modelManager.generateEBAFiles = True # type: ignore[attr-defined]
321
+
322
+ @staticmethod
323
+ def cntlrCmdLineXbrlRun(
324
+ cntlr: CntlrCmdLine,
325
+ options: RuntimeOptions,
326
+ modelXbrl: ModelXbrl,
327
+ *args: Any,
328
+ **kwargs: Any,
329
+ ) -> None:
330
+ ebaTablesetIndexFile = getattr(options, "ebaTablesetIndexFile", None)
331
+ if ebaTablesetIndexFile is None or ebaTablesetIndexFile == "generateEBAFiles":
332
+ return
256
333
  if cntlr.modelManager is None or cntlr.modelManager.modelXbrl is None:
257
334
  cntlr.addToLog("No taxonomy loaded.")
258
335
  return
259
-
260
- from arelle import RenderingEvaluator
261
- RenderingEvaluator.init(modelXbrl)
262
- generateHtmlEbaTablesetFiles(cntlr.modelManager.modelXbrl, options.ebaTablesetIndexFile)
336
+ RenderingEvaluator.init(modelXbrl) # type: ignore[no-untyped-call]
337
+ generateHtmlEbaTablesetFiles(cntlr.modelManager.modelXbrl, ebaTablesetIndexFile)
263
338
 
264
339
 
265
340
  __pluginInfo__ = {
266
- 'name': 'Save HTML EBA Tables',
267
- 'version': '0.9',
268
- 'description': "This plug-in adds a feature to a directory containing HTML Tablesets with an EBA index page. ",
269
- 'license': 'Apache-2',
270
- 'author': authorLabel,
271
- 'copyright': copyrightLabel,
341
+ "name": "Save HTML EBA Tables",
342
+ "version": "0.10",
343
+ "description": "This plug-in adds a feature to a directory containing HTML Tablesets with an EBA index page.",
344
+ "license": "Apache-2",
345
+ "author": Version.authorLabel,
346
+ "copyright": Version.copyrightLabel,
272
347
  # classes of mount points (required)
273
- 'CntlrWinMain.Menu.Tools': saveHtmlEbaTablesMenuEntender,
274
- 'CntlrCmdLine.Options': saveHtmlEbaTablesCommandLineOptionExtender,
275
- 'CntlrCmdLine.Xbrl.Loaded': saveHtmlEbaTablesCommandLineXbrlLoaded,
276
- 'CntlrCmdLine.Xbrl.Run': saveHtmlEbaTablesCommandLineXbrlRun,
348
+ "CntlrWinMain.Menu.Tools": SaveHtmlEbaTablesPlugin.cntlrWinMainMenuTools,
349
+ "CntlrCmdLine.Options": SaveHtmlEbaTablesPlugin.cntlrCmdLineOptions,
350
+ "CntlrCmdLine.Xbrl.Loaded": SaveHtmlEbaTablesPlugin.cntlrCmdLineXbrlLoaded,
351
+ "CntlrCmdLine.Xbrl.Run": SaveHtmlEbaTablesPlugin.cntlrCmdLineXbrlRun,
277
352
  }
@@ -94,6 +94,33 @@ class PluginHooks(ABC):
94
94
  """
95
95
  raise NotImplementedError
96
96
 
97
+ @staticmethod
98
+ def cntlrCmdLineXbrlLoaded(
99
+ cntlr: CntlrCmdLine,
100
+ options: RuntimeOptions,
101
+ modelXbrl: ModelXbrl,
102
+ entrypoint: dict[str, str] | None = None,
103
+ responseZipStream: BinaryIO | None = None,
104
+ *args: Any,
105
+ **kwargs: Any,
106
+ ) -> None:
107
+ """
108
+ Plugin hook: `CntlrCmdLine.Xbrl.Loaded`
109
+
110
+ This hook is triggered after loading, but prior to validation (if requested) and loading views.
111
+ It's useful if you need to perform operations with the XBRL model prior to rendering views.
112
+
113
+ :param cntlr: The [Cntlr](#arelle.Cntlr.Cntlr) being initialized.
114
+ :param options: Parsed options object.
115
+ :param modelXbrl: The loaded [ModelXbrl](#arelle.ModelXbrl.ModelXbrl).
116
+ :param entrypoint: The entrypoint that was parsed to load the model.
117
+ :param responseZipStream: The response zip stream if loaded from the webserver and the user requested a zip response.
118
+ :param args: Argument capture to ensure new parameters don't break plugin hook.
119
+ :param kwargs: Argument capture to ensure new named parameters don't break plugin hook.
120
+ :return: None
121
+ """
122
+ raise NotImplementedError
123
+
97
124
  @staticmethod
98
125
  def cntlrCmdLineXbrlRun(
99
126
  cntlr: CntlrCmdLine,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: arelle-release
3
- Version: 2.36.27
3
+ Version: 2.36.28
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License: Apache-2.0
@@ -123,7 +123,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
123
123
  arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
124
124
  arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
125
125
  arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
- arelle/_version.py,sha256=KK-jzMUiNZ-nw4JlDwy7wbO1_sQsynI1BXJTEqnnBqc,515
126
+ arelle/_version.py,sha256=tJiPkS-G5y5JI9hiARsGARKpL6tXZNcDHhGYM05HqHo,515
127
127
  arelle/typing.py,sha256=Ct5lrNKRow_o9CraMEXNza8nFsJ_iGIKoUeGfPs2dxI,1084
128
128
  arelle/api/Session.py,sha256=Vd09RAutWX7mxHSsrW7Bl8CsE1UzXpQpAJsZb55mqng,6188
129
129
  arelle/archive/CustomLogger.py,sha256=v_JXOCQLDZcfaFWzxC9FRcEf9tQi4rCI4Sx7jCuAVQI,1231
@@ -321,7 +321,7 @@ arelle/plugin/profileCmdLine.py,sha256=uLL0fGshpiwtzyLKAtW0WuXAvcRtZgxQG6swM0e4B
321
321
  arelle/plugin/profileFormula.py,sha256=PK273bQpYO-87QMFpBVQmnqGKfjDElIXPhAwc8Nyp04,5548
322
322
  arelle/plugin/saveCHComponentFile.py,sha256=phW-n96P0BWONLqX3wBcuNcDa_IQ8QVfFWKL9NRrh7k,7242
323
323
  arelle/plugin/saveDTS.py,sha256=D8hfFiM9Gc9LE7ZV0-pDvOHeU7Y5ebOzZx_nXzoZrl8,3216
324
- arelle/plugin/saveHtmlEBAtables.py,sha256=fZcagH2uE8bGpapsArmVjKLlzexciCi08nlZ72P9RK0,12201
324
+ arelle/plugin/saveHtmlEBAtables.py,sha256=OITN4ftI_atUO5NvrDJDKUc0CAk67GY68Jl16xDBXiI,10961
325
325
  arelle/plugin/saveLoadableExcel.py,sha256=ZPYB6injFabav0dRzgS7adCFzfHkwtftjl3PfBjijuU,20539
326
326
  arelle/plugin/saveLoadableOIM.py,sha256=19R5mZzIDkPohEdabs22nOhIZoEPe8vR0HczqN3oSI0,36239
327
327
  arelle/plugin/saveSKOS.py,sha256=7Z1Qedf83zMo9EbigKkxNpiMjzkTYQvLEnwWMObLc1Y,12465
@@ -697,7 +697,7 @@ arelle/scripts-macOS/startWebServer.command,sha256=KXLSwAwchDZBlL-k9PYXdf39RNBtt
697
697
  arelle/scripts-unix/startWebServer.sh,sha256=_0puRzaGkdMZoFn3R7hDti9a3ryN6kTZAXwLweeZU1s,42
698
698
  arelle/scripts-windows/startWebServer.bat,sha256=qmnF1yrjNo__bi4QodONWlN0qHShVLTKptJQYyZtgcY,122
699
699
  arelle/utils/PluginData.py,sha256=GUnuZaApm1J4Xm9ZA1U2M1aask-AaNGviLtc0fgXbFg,265
700
- arelle/utils/PluginHooks.py,sha256=A9wUuY9ktxmQtDzg6pHm4kHu00f-M3V6iAeU-5CS31s,28997
700
+ arelle/utils/PluginHooks.py,sha256=USi7zEZzwzTnKdKSyM3Xjacodo6YcU_Lb3EPQwWiVg4,30220
701
701
  arelle/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
702
702
  arelle/utils/validate/Decorator.py,sha256=8LGmA171HZgKrALtsMKyHqMNM-XCdwJOv6KpZz4pC2c,3161
703
703
  arelle/utils/validate/DetectScriptsInXhtml.py,sha256=JOgsUP0_kvE6O3TuhzKxqKG1ZFP9LrUYZBgrar6JEm0,2178
@@ -716,6 +716,7 @@ tests/integration_tests/scripts/script_util.py,sha256=MuLV2X4NfaVcLXTSq1C4lxnInH
716
716
  tests/integration_tests/scripts/test_scripts.py,sha256=6kSN385J3KG3k9rQN3aPP9bx70vzpCb4PbUGbGbJYU8,506
717
717
  tests/integration_tests/scripts/tests/duplicate_facts_deduplication.py,sha256=qMWTwbIJmKMncEiDjoHIkQYJYKUmxj3JJ_S1hTlLAMs,4698
718
718
  tests/integration_tests/scripts/tests/duplicate_facts_validate.py,sha256=hxguoZZnyaPdWfAeLwfbLpIRpTbB6O-nJ5E4j7XYLRE,3743
719
+ tests/integration_tests/scripts/tests/eba_tablesets.py,sha256=6uayfssQ7p3zyw6IUcIhraQMJiTsIVPxqYzQWdLx5q8,4232
719
720
  tests/integration_tests/scripts/tests/entry_point_from_taxonomy_package.py,sha256=KCaK08pQ6N18lIUxhWbeyvNeYQdSHk0Rxi86On3W_n8,1525
720
721
  tests/integration_tests/scripts/tests/ixbrl-viewer_cli.py,sha256=OdXj9p148MG9fycO3opyNIClYEDhyf8z4HKGFhrEAYU,2222
721
722
  tests/integration_tests/scripts/tests/ixbrl-viewer_webserver.py,sha256=dDcftyfu9IA2qWih9CZUfoTmjeP_Zmng488N7Gp1wFU,2848
@@ -1552,9 +1553,9 @@ tests/unit_tests/arelle/oim/test_load.py,sha256=NxiUauQwJVfWAHbbpsMHGSU2d3Br8Pki
1552
1553
  tests/unit_tests/arelle/plugin/test_plugin_imports.py,sha256=bdhIs9frAnFsdGU113yBk09_jis-z43dwUItMFYuSYM,1064
1553
1554
  tests/unit_tests/arelle/plugin/validate/ESEF/ESEF_Current/test_validate_css_url.py,sha256=XHABmejQt7RlZ0udh7v42f2Xb2STGk_fSaIaJ9i2xo0,878
1554
1555
  tests/unit_tests/arelle/utils/validate/test_decorator.py,sha256=ZS8FqIY1g-2FCbjF4UYm609dwViax6qBMRJSi0vfuhY,2482
1555
- arelle_release-2.36.27.dist-info/LICENSE.md,sha256=rMbWwFLGzPgLoEjEu8LCmkpWDTqsvfOI-wzLSfeJsis,4107
1556
- arelle_release-2.36.27.dist-info/METADATA,sha256=h1D54IXAymwwmAytd4w-uoNt6wrgRDgcx9i7kBBE38k,9010
1557
- arelle_release-2.36.27.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
1558
- arelle_release-2.36.27.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
1559
- arelle_release-2.36.27.dist-info/top_level.txt,sha256=ZYmYGmhW5Jvo3vJ4iXBZPUI29LvYhntom04w90esJvU,13
1560
- arelle_release-2.36.27.dist-info/RECORD,,
1556
+ arelle_release-2.36.28.dist-info/LICENSE.md,sha256=rMbWwFLGzPgLoEjEu8LCmkpWDTqsvfOI-wzLSfeJsis,4107
1557
+ arelle_release-2.36.28.dist-info/METADATA,sha256=YwxTo5Ye5NCaTXAXdqZqO1d4XtgcHt782wELCABo0fs,9010
1558
+ arelle_release-2.36.28.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
1559
+ arelle_release-2.36.28.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
1560
+ arelle_release-2.36.28.dist-info/top_level.txt,sha256=ZYmYGmhW5Jvo3vJ4iXBZPUI29LvYhntom04w90esJvU,13
1561
+ arelle_release-2.36.28.dist-info/RECORD,,
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ import html.parser
4
+ import os
5
+ import urllib.request
6
+ import zipfile
7
+ from pathlib import Path
8
+ from shutil import rmtree
9
+
10
+ from tests.integration_tests.integration_test_util import get_s3_uri
11
+ from tests.integration_tests.scripts.script_util import (
12
+ assert_result,
13
+ parse_args,
14
+ prepare_logfile,
15
+ run_arelle,
16
+ validate_log_file,
17
+ )
18
+
19
+ errors = []
20
+ this_file = Path(__file__)
21
+ args = parse_args(this_file.stem, "Confirm EBA Tablesets report runs successfully from the command line.")
22
+ arelle_command = args.arelle
23
+ arelle_offline = args.offline
24
+ working_directory = Path(args.working_directory)
25
+ test_directory = Path(args.test_directory)
26
+ arelle_log_file = prepare_logfile(test_directory, this_file)
27
+ samples_zip_path = test_directory / "eba_samples.zip"
28
+ samples_directory = test_directory / "eba_samples"
29
+ target_path = samples_directory / "DUMMYLEI123456789012_GB_COREP030000_COREPLECON_2021-06-30_20201218154732000.xbrl"
30
+ tablesets_report_path = test_directory / "index.html"
31
+ sample_report_zip_url = get_s3_uri("ci/packages/eba-samples.zip", version_id="iDJU3nFy6_rQ289k.mosenHjUFrXCmCM")
32
+
33
+ samples_url = get_s3_uri("ci/packages/eba_samples.zip", version_id="O7uYHbSYmxe_20nBhWWoXMfjGpquNMMj")
34
+
35
+ print(f"Downloading EBA sample files: {samples_zip_path}")
36
+ urllib.request.urlretrieve(samples_url, samples_zip_path)
37
+
38
+ print(f"Extracting EBA sample files: {samples_directory}")
39
+ with zipfile.ZipFile(samples_zip_path, "r") as zip_ref:
40
+ zip_ref.extractall(samples_directory)
41
+
42
+ print(f"Generating EBA Tablesets report: {tablesets_report_path}")
43
+ run_arelle(
44
+ arelle_command,
45
+ plugins=["saveHtmlEBAtables"],
46
+ additional_args=[
47
+ "-f",
48
+ str(target_path),
49
+ "--package",
50
+ str(samples_directory / "TP-Eurofiling2.1.zip"),
51
+ "--package",
52
+ str(samples_directory / "EBA_CRD_IV_XBRL_3.0_Dictionary_3.0.1.0.Errata3.zip"),
53
+ "--package",
54
+ str(samples_directory / "EBA_CRD_IV_XBRL_3.0_Reporting_COREP_FINREP_Frameworks_hotfix.Errata3.zip"),
55
+ "--save-EBA-tablesets",
56
+ str(tablesets_report_path),
57
+ ],
58
+ offline=arelle_offline,
59
+ logFile=arelle_log_file,
60
+ )
61
+
62
+ print(f"Checking for EBA Tablesets report: {tablesets_report_path}")
63
+ if not tablesets_report_path.exists():
64
+ errors.append(f'EBA Tablesets report not generated at "{tablesets_report_path}"')
65
+
66
+ eba_table_files = [
67
+ "eba_tC_00.01.html",
68
+ "eba_tC_26.00.html",
69
+ "eba_tC_27.00.html",
70
+ "eba_tC_28.00.html",
71
+ "eba_tC_29.00.html",
72
+ ]
73
+
74
+ eba_tablesets_report_files = [
75
+ test_directory / f for f in ("index.html", "indexCenterLanding.html", "indexFormsFrame.html", *eba_table_files)
76
+ ]
77
+
78
+ for report_file in eba_tablesets_report_files:
79
+ if not report_file.exists():
80
+ errors.append(f'EBA Tablesets report file not generated at "{report_file}"')
81
+
82
+
83
+ class ButtonParser(html.parser.HTMLParser):
84
+ def __init__(self, table_files):
85
+ super().__init__()
86
+ self.table_files = table_files
87
+ self.found_buttons = {table_file: False for table_file in table_files}
88
+
89
+ def handle_starttag(self, tag, attrs):
90
+ if tag.lower() == "button":
91
+ attrs_dict = dict(attrs)
92
+ onclick = attrs_dict.get("onclick", "")
93
+ if onclick is not None:
94
+ for table_file in self.table_files:
95
+ expected_onclick = f"javascript:parent.loadContent('{table_file}');"
96
+ if expected_onclick in onclick:
97
+ self.found_buttons[table_file] = True
98
+
99
+
100
+ forms_frame_path = test_directory / "indexFormsFrame.html"
101
+ print("Checking for proper button elements in indexFormsFrame.html")
102
+ with open(forms_frame_path, encoding="utf-8") as fh:
103
+ html_content = fh.read()
104
+ parser = ButtonParser(eba_table_files)
105
+ parser.feed(html_content)
106
+ for table_file, found in parser.found_buttons.items():
107
+ if not found:
108
+ errors.append(f"Button for table {table_file} not found in indexFormsFrame.html")
109
+
110
+ print(f"Checking for log errors: {arelle_log_file}")
111
+ errors += validate_log_file(arelle_log_file)
112
+
113
+ assert_result(errors)
114
+
115
+ print("Cleaning up")
116
+ rmtree(samples_directory)
117
+ os.unlink(samples_zip_path)
118
+ for report_file in eba_tablesets_report_files:
119
+ os.unlink(report_file)