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 +2 -2
- arelle/plugin/saveHtmlEBAtables.py +290 -215
- arelle/utils/PluginHooks.py +27 -0
- {arelle_release-2.36.27.dist-info → arelle_release-2.36.28.dist-info}/METADATA +1 -1
- {arelle_release-2.36.27.dist-info → arelle_release-2.36.28.dist-info}/RECORD +10 -9
- tests/integration_tests/scripts/tests/eba_tablesets.py +119 -0
- {arelle_release-2.36.27.dist-info → arelle_release-2.36.28.dist-info}/LICENSE.md +0 -0
- {arelle_release-2.36.27.dist-info → arelle_release-2.36.28.dist-info}/WHEEL +0 -0
- {arelle_release-2.36.27.dist-info → arelle_release-2.36.28.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.36.27.dist-info → arelle_release-2.36.28.dist-info}/top_level.txt +0 -0
arelle/_version.py
CHANGED
|
@@ -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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
<html
|
|
20
|
-
<head
|
|
21
|
-
<
|
|
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
|
|
24
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
dts.modelManager.cntlr.addToLog("viewing: " +
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
200
|
+
elif tableName.startswith("srb_t"):
|
|
74
201
|
tableName = tableName.removeprefix("srb_t")
|
|
75
|
-
viewRenderedGrid(dts,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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", "
|
|
88
|
-
elt.set("
|
|
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
|
-
|
|
93
|
-
|
|
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,
|
|
227
|
+
for modelTable, _order in sorted(modelTables, key=itemgetter(1)):
|
|
111
228
|
viewTable(modelTable)
|
|
112
229
|
|
|
113
|
-
with open(indexBase + "FormsFrame.html", "
|
|
114
|
-
XmlUtil.writexml(fh,
|
|
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, "
|
|
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
|
-
|
|
190
|
-
|
|
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(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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(
|
|
203
|
-
|
|
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
|
-
|
|
221
|
-
indexFile = cntlr.uiFileDialog(
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
}
|
arelle/utils/PluginHooks.py
CHANGED
|
@@ -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,
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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.
|
|
1556
|
-
arelle_release-2.36.
|
|
1557
|
-
arelle_release-2.36.
|
|
1558
|
-
arelle_release-2.36.
|
|
1559
|
-
arelle_release-2.36.
|
|
1560
|
-
arelle_release-2.36.
|
|
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)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|