ixbrl-viewer 1.4.13__py3-none-any.whl → 1.4.15__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 ixbrl-viewer might be problematic. Click here for more details.
- iXBRLViewerPlugin/__init__.py +27 -26
- iXBRLViewerPlugin/_version.py +2 -2
- iXBRLViewerPlugin/constants.py +2 -1
- iXBRLViewerPlugin/iXBRLViewer.py +17 -8
- iXBRLViewerPlugin/ui.py +5 -4
- iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
- iXBRLViewerPlugin/viewer/src/html/inspector.html +27 -0
- iXBRLViewerPlugin/viewer/src/i18n/en/translation.json +18 -1
- iXBRLViewerPlugin/viewer/src/i18n/es/translation.json +18 -1
- iXBRLViewerPlugin/viewer/src/icons/calculator.svg +13 -0
- iXBRLViewerPlugin/viewer/src/icons/circle-cross.svg +11 -0
- iXBRLViewerPlugin/viewer/src/icons/circle-tick.svg +11 -0
- iXBRLViewerPlugin/viewer/src/icons/dimension.svg +1 -5
- iXBRLViewerPlugin/viewer/src/icons/member.svg +2 -5
- iXBRLViewerPlugin/viewer/src/icons/multi-tag.svg +10 -0
- iXBRLViewerPlugin/viewer/src/js/accordian.js +2 -2
- iXBRLViewerPlugin/viewer/src/js/calculation.js +202 -0
- iXBRLViewerPlugin/viewer/src/js/calculation.test.js +306 -0
- iXBRLViewerPlugin/viewer/src/js/calculationInspector.js +190 -0
- iXBRLViewerPlugin/viewer/src/js/concept.js +7 -1
- iXBRLViewerPlugin/viewer/src/js/fact.js +59 -5
- iXBRLViewerPlugin/viewer/src/js/factset.js +54 -5
- iXBRLViewerPlugin/viewer/src/js/factset.test.js +71 -5
- iXBRLViewerPlugin/viewer/src/js/inspector.js +85 -30
- iXBRLViewerPlugin/viewer/src/js/interval.js +70 -0
- iXBRLViewerPlugin/viewer/src/js/interval.test.js +153 -0
- iXBRLViewerPlugin/viewer/src/js/test-utils.js +19 -0
- iXBRLViewerPlugin/viewer/src/js/viewer.js +6 -8
- iXBRLViewerPlugin/viewer/src/less/accordian.less +2 -2
- iXBRLViewerPlugin/viewer/src/less/calculation-inspector.less +83 -0
- iXBRLViewerPlugin/viewer/src/less/colours.less +2 -0
- iXBRLViewerPlugin/viewer/src/less/common.less +3 -3
- iXBRLViewerPlugin/viewer/src/less/dialog.less +8 -4
- iXBRLViewerPlugin/viewer/src/less/inspector.less +69 -25
- iXBRLViewerPlugin/viewer/src/less/validation-report.less +1 -2
- iXBRLViewerPlugin/viewer/src/less/viewer.less +12 -12
- {ixbrl_viewer-1.4.13.dist-info → ixbrl_viewer-1.4.15.dist-info}/METADATA +3 -3
- {ixbrl_viewer-1.4.13.dist-info → ixbrl_viewer-1.4.15.dist-info}/RECORD +43 -34
- iXBRLViewerPlugin/viewer/src/js/calculations.js +0 -111
- {ixbrl_viewer-1.4.13.dist-info → ixbrl_viewer-1.4.15.dist-info}/LICENSE +0 -0
- {ixbrl_viewer-1.4.13.dist-info → ixbrl_viewer-1.4.15.dist-info}/NOTICE +0 -0
- {ixbrl_viewer-1.4.13.dist-info → ixbrl_viewer-1.4.15.dist-info}/WHEEL +0 -0
- {ixbrl_viewer-1.4.13.dist-info → ixbrl_viewer-1.4.15.dist-info}/entry_points.txt +0 -0
- {ixbrl_viewer-1.4.13.dist-info → ixbrl_viewer-1.4.15.dist-info}/top_level.txt +0 -0
iXBRLViewerPlugin/__init__.py
CHANGED
|
@@ -9,7 +9,6 @@ import sys
|
|
|
9
9
|
import tempfile
|
|
10
10
|
import traceback
|
|
11
11
|
from optparse import OptionGroup, OptionParser
|
|
12
|
-
from typing import Optional, Union
|
|
13
12
|
|
|
14
13
|
from arelle import Cntlr
|
|
15
14
|
from arelle.LocalViewer import LocalViewer
|
|
@@ -18,7 +17,7 @@ from arelle.webserver.bottle import static_file
|
|
|
18
17
|
|
|
19
18
|
from .constants import CONFIG_FEATURE_PREFIX, CONFIG_LAUNCH_ON_LOAD, \
|
|
20
19
|
CONFIG_SCRIPT_URL, DEFAULT_LAUNCH_ON_LOAD, DEFAULT_OUTPUT_NAME, \
|
|
21
|
-
DEFAULT_VIEWER_PATH, FEATURE_CONFIGS
|
|
20
|
+
DEFAULT_JS_FILENAME, DEFAULT_VIEWER_PATH, FEATURE_CONFIGS
|
|
22
21
|
from .iXBRLViewer import IXBRLViewerBuilder, IXBRLViewerBuilderError
|
|
23
22
|
|
|
24
23
|
|
|
@@ -96,13 +95,15 @@ def iXBRLViewerCommandLineOptionExtender(parser, *args, **kwargs):
|
|
|
96
95
|
|
|
97
96
|
def generateViewer(
|
|
98
97
|
cntlr: Cntlr,
|
|
99
|
-
saveViewerDest:
|
|
98
|
+
saveViewerDest: io.BytesIO | str | None,
|
|
100
99
|
viewerURL: str = DEFAULT_VIEWER_PATH,
|
|
101
100
|
showValidationMessages: bool = False,
|
|
102
101
|
useStubViewer: bool = False,
|
|
103
102
|
zipViewerOutput: bool = False,
|
|
104
|
-
features:
|
|
105
|
-
packageDownloadURL: str = None
|
|
103
|
+
features: list[str] | None = None,
|
|
104
|
+
packageDownloadURL: str | None = None,
|
|
105
|
+
copyScript = True
|
|
106
|
+
) -> None:
|
|
106
107
|
"""
|
|
107
108
|
Generate and save a viewer at the given destination (file, directory, or in-memory file) with the given viewer URL.
|
|
108
109
|
If the viewer URL is a location on the local file system, a copy will be placed included in the output destination.
|
|
@@ -115,6 +116,8 @@ def generateViewer(
|
|
|
115
116
|
:param features: List of feature names to enable via generated JSON data.
|
|
116
117
|
"""
|
|
117
118
|
# extend XBRL-loaded run processing for this option
|
|
119
|
+
if saveViewerDest is None:
|
|
120
|
+
return
|
|
118
121
|
if (cntlr.modelManager is None
|
|
119
122
|
or len(cntlr.modelManager.loadedModelXbrls) == 0
|
|
120
123
|
or any(not mx.modelDocument for mx in cntlr.modelManager.loadedModelXbrls)):
|
|
@@ -135,24 +138,22 @@ def generateViewer(
|
|
|
135
138
|
else:
|
|
136
139
|
viewerAbsolutePath = getAbsoluteViewerPath(saveViewerDest, viewerURL)
|
|
137
140
|
|
|
138
|
-
if os.path.isfile(viewerAbsolutePath):
|
|
141
|
+
if copyScript and os.path.isfile(viewerAbsolutePath):
|
|
139
142
|
# The script was found on the local file system and will be copied into the
|
|
140
143
|
# destination directory, so the local path (just the basename) of viewerURL should
|
|
141
144
|
# be passed to the script tag
|
|
142
145
|
copyScriptPath = viewerURL
|
|
143
146
|
viewerURL = os.path.basename(viewerURL)
|
|
144
147
|
try:
|
|
145
|
-
|
|
146
|
-
if
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
iv
|
|
152
|
-
if iv is not None:
|
|
153
|
-
iv.save(out, zipOutput=zipViewerOutput, copyScriptPath=copyScriptPath)
|
|
148
|
+
viewerBuilder = IXBRLViewerBuilder(cntlr.modelManager.loadedModelXbrls)
|
|
149
|
+
if features:
|
|
150
|
+
for feature in features:
|
|
151
|
+
viewerBuilder.enableFeature(feature)
|
|
152
|
+
iv = viewerBuilder.createViewer(scriptUrl=viewerURL, showValidations=showValidationMessages, useStubViewer=useStubViewer, packageDownloadURL=packageDownloadURL)
|
|
153
|
+
if iv is not None:
|
|
154
|
+
iv.save(saveViewerDest, zipOutput=zipViewerOutput, copyScriptPath=copyScriptPath)
|
|
154
155
|
except IXBRLViewerBuilderError as ex:
|
|
155
|
-
print(ex
|
|
156
|
+
print(ex)
|
|
156
157
|
except Exception as ex:
|
|
157
158
|
cntlr.addToLog("Exception {} \nTraceback {}".format(ex, traceback.format_tb(sys.exc_info()[2])))
|
|
158
159
|
|
|
@@ -170,7 +171,7 @@ def getAbsoluteViewerPath(saveViewerPath: str, relativeViewerPath: str) -> str:
|
|
|
170
171
|
return os.path.join(saveViewerDir, relativeViewerPath)
|
|
171
172
|
|
|
172
173
|
|
|
173
|
-
def getFeaturesFromOptions(options:
|
|
174
|
+
def getFeaturesFromOptions(options: argparse.Namespace | OptionParser):
|
|
174
175
|
return [
|
|
175
176
|
featureConfig.key
|
|
176
177
|
for featureConfig in FEATURE_CONFIGS
|
|
@@ -206,7 +207,7 @@ def iXBRLViewerSaveCommand(cntlr):
|
|
|
206
207
|
generateViewer(
|
|
207
208
|
cntlr,
|
|
208
209
|
dialog.filename(),
|
|
209
|
-
dialog.scriptUrl(),
|
|
210
|
+
dialog.scriptUrl() or DEFAULT_VIEWER_PATH,
|
|
210
211
|
zipViewerOutput=dialog.zipViewerOutput(),
|
|
211
212
|
features=dialog.features()
|
|
212
213
|
)
|
|
@@ -249,23 +250,23 @@ def commandLineRun(*args, **kwargs):
|
|
|
249
250
|
class iXBRLViewerLocalViewer(LocalViewer):
|
|
250
251
|
# plugin-specific local file handler
|
|
251
252
|
def getLocalFile(self, file, relpath, request):
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
253
|
+
if file == DEFAULT_JS_FILENAME:
|
|
254
|
+
return static_file(DEFAULT_JS_FILENAME, os.path.dirname(DEFAULT_VIEWER_PATH))
|
|
255
|
+
_report, _, _file = file.partition("/")
|
|
256
|
+
if _report.isnumeric(): # in reportsFolder folder
|
|
256
257
|
# check if file is in the current or parent directory
|
|
257
258
|
_fileDir = self.reportsFolders[int(_report)]
|
|
258
259
|
_fileExists = False
|
|
259
260
|
if os.path.exists(os.path.join(_fileDir, _file)):
|
|
260
261
|
_fileExists = True
|
|
261
|
-
elif "/" in _file and os.path.exists(os.path.join(_fileDir, os.path.
|
|
262
|
+
elif "/" in _file and os.path.exists(os.path.join(_fileDir, os.path.basename(_file))):
|
|
262
263
|
# xhtml in a subdirectory for output files may refer to an image file in parent directory
|
|
263
264
|
_fileExists = True
|
|
264
|
-
_file = os.path.
|
|
265
|
+
_file = os.path.basename(_file)
|
|
265
266
|
if not _fileExists:
|
|
266
267
|
self.cntlr.addToLog("http://localhost:{}/{}".format(self.port, file), messageCode="localViewer:fileNotFound", level=logging.DEBUG)
|
|
267
268
|
return static_file(_file, root=_fileDir, headers=self.noCacheHeaders) # extra_headers modification to py-bottle
|
|
268
|
-
return static_file(file, root="/") #
|
|
269
|
+
return static_file(file, root="/") # absolute path used for ixbrlviewer.js.
|
|
269
270
|
|
|
270
271
|
|
|
271
272
|
def guiRun(cntlr, modelXbrl, attach, *args, **kwargs):
|
|
@@ -286,7 +287,7 @@ def guiRun(cntlr, modelXbrl, attach, *args, **kwargs):
|
|
|
286
287
|
generateViewer(
|
|
287
288
|
cntlr,
|
|
288
289
|
saveViewerDest=tempViewer.name,
|
|
289
|
-
viewerURL=cntlr.config.
|
|
290
|
+
viewerURL=cntlr.config.get(CONFIG_SCRIPT_URL) or DEFAULT_VIEWER_PATH,
|
|
290
291
|
useStubViewer=True,
|
|
291
292
|
features=features
|
|
292
293
|
)
|
iXBRLViewerPlugin/_version.py
CHANGED
iXBRLViewerPlugin/constants.py
CHANGED
|
@@ -13,7 +13,8 @@ CONFIG_ZIP_OUTPUT = 'iXBRLViewerZipOutput'
|
|
|
13
13
|
|
|
14
14
|
DEFAULT_LAUNCH_ON_LOAD = True
|
|
15
15
|
DEFAULT_OUTPUT_NAME = 'ixbrlviewer.html'
|
|
16
|
-
|
|
16
|
+
DEFAULT_JS_FILENAME = 'ixbrlviewer.js'
|
|
17
|
+
DEFAULT_VIEWER_PATH = os.path.join(os.path.dirname(__file__), "viewer", "dist", DEFAULT_JS_FILENAME)
|
|
17
18
|
|
|
18
19
|
FEATURE_CONFIGS = [
|
|
19
20
|
FeatureConfig(
|
iXBRLViewerPlugin/iXBRLViewer.py
CHANGED
|
@@ -12,7 +12,6 @@ import urllib.parse
|
|
|
12
12
|
import zipfile
|
|
13
13
|
from collections import defaultdict
|
|
14
14
|
from copy import deepcopy
|
|
15
|
-
from typing import Optional, Union
|
|
16
15
|
|
|
17
16
|
import pycountry
|
|
18
17
|
from arelle import XbrlConst
|
|
@@ -266,11 +265,15 @@ class IXBRLViewerBuilder:
|
|
|
266
265
|
factData["v"] = None
|
|
267
266
|
elif f.concept is not None and f.concept.isEnumeration:
|
|
268
267
|
qnEnums = f.xValue
|
|
269
|
-
if
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
268
|
+
if qnEnums is None:
|
|
269
|
+
factData["v"] = f.value
|
|
270
|
+
factData["err"] = 'INVALID_IX_VALUE'
|
|
271
|
+
else:
|
|
272
|
+
if not isinstance(qnEnums, list):
|
|
273
|
+
qnEnums = (qnEnums,)
|
|
274
|
+
factData["v"] = " ".join(self.nsmap.qname(qn) for qn in qnEnums)
|
|
275
|
+
for qn in qnEnums:
|
|
276
|
+
self.addConcept(report, report.qnameConcepts.get(qn))
|
|
274
277
|
else:
|
|
275
278
|
factData["v"] = f.value
|
|
276
279
|
if f.value == INVALIDixVALUE:
|
|
@@ -390,7 +393,13 @@ class IXBRLViewerBuilder:
|
|
|
390
393
|
self.taxonomyData["sourceReports"].append(sourceReport)
|
|
391
394
|
return sourceReport
|
|
392
395
|
|
|
393
|
-
def createViewer(
|
|
396
|
+
def createViewer(
|
|
397
|
+
self,
|
|
398
|
+
scriptUrl: str = DEFAULT_VIEWER_PATH,
|
|
399
|
+
useStubViewer: bool = False,
|
|
400
|
+
showValidations: bool = True,
|
|
401
|
+
packageDownloadURL: str | None = None,
|
|
402
|
+
) -> iXBRLViewer | None:
|
|
394
403
|
"""
|
|
395
404
|
Create an iXBRL file with XBRL data as a JSON blob, and script tags added.
|
|
396
405
|
:param scriptUrl: The `src` value of the script tag that loads the viewer script.
|
|
@@ -526,7 +535,7 @@ class iXBRLViewer:
|
|
|
526
535
|
def addFilingDoc(self, filingDocuments):
|
|
527
536
|
self.filingDocuments = filingDocuments
|
|
528
537
|
|
|
529
|
-
def save(self, destination:
|
|
538
|
+
def save(self, destination: io.BytesIO | str, zipOutput: bool = False, copyScriptPath: str | None = None):
|
|
530
539
|
"""
|
|
531
540
|
Save the iXBRL viewer.
|
|
532
541
|
:param destination: The target that viewer data/files will be written to (path to file/directory, or a file object itself).
|
iXBRLViewerPlugin/ui.py
CHANGED
|
@@ -10,8 +10,9 @@ import os
|
|
|
10
10
|
|
|
11
11
|
from .constants import CONFIG_FEATURE_PREFIX, CONFIG_FILE_DIRECTORY, CONFIG_LAUNCH_ON_LOAD, \
|
|
12
12
|
CONFIG_OUTPUT_FILE, CONFIG_SCRIPT_URL, CONFIG_ZIP_OUTPUT, DEFAULT_LAUNCH_ON_LOAD, \
|
|
13
|
-
|
|
13
|
+
GUI_FEATURE_CONFIGS
|
|
14
14
|
|
|
15
|
+
UNSET_SCRIPT_URL = ''
|
|
15
16
|
|
|
16
17
|
class BaseViewerDialog(Toplevel):
|
|
17
18
|
"""
|
|
@@ -28,7 +29,7 @@ class BaseViewerDialog(Toplevel):
|
|
|
28
29
|
featureVar.set(self.cntlr.config.setdefault(f'{CONFIG_FEATURE_PREFIX}{featureConfig.key}', featureConfig.guiDefault))
|
|
29
30
|
self._features[featureConfig.key] = featureVar
|
|
30
31
|
self._scriptUrl = StringVar()
|
|
31
|
-
self._scriptUrl.set(self.cntlr.config.setdefault(CONFIG_SCRIPT_URL,
|
|
32
|
+
self._scriptUrl.set(self.cntlr.config.setdefault(CONFIG_SCRIPT_URL, UNSET_SCRIPT_URL))
|
|
32
33
|
|
|
33
34
|
def addButtons(self, frame: Frame, x: int, y: int) -> int:
|
|
34
35
|
"""
|
|
@@ -56,7 +57,7 @@ class BaseViewerDialog(Toplevel):
|
|
|
56
57
|
:return: Row `y` that the last field was added on
|
|
57
58
|
"""
|
|
58
59
|
y += 1
|
|
59
|
-
scriptUrlLabel = Label(frame, text="Script URL")
|
|
60
|
+
scriptUrlLabel = Label(frame, text="Script URL (leave blank for default)")
|
|
60
61
|
scriptUrlEntry = Entry(frame, textvariable=self._scriptUrl, width=80)
|
|
61
62
|
scriptUrlLabel.grid(row=y, column=0, sticky=W, pady=3, padx=3)
|
|
62
63
|
scriptUrlEntry.grid(row=y, column=1, columnspan=2, sticky=EW, pady=3, padx=3)
|
|
@@ -247,6 +248,6 @@ class SettingsDialog(BaseViewerDialog):
|
|
|
247
248
|
Resets dialog variable values to default values
|
|
248
249
|
"""
|
|
249
250
|
self._launchOnLoad.set(DEFAULT_LAUNCH_ON_LOAD)
|
|
250
|
-
self._scriptUrl.set(
|
|
251
|
+
self._scriptUrl.set(UNSET_SCRIPT_URL)
|
|
251
252
|
for featureConfig in GUI_FEATURE_CONFIGS:
|
|
252
253
|
self._features[featureConfig.key].set(featureConfig.guiDefault)
|