arelle-release 2.37.55__py3-none-any.whl → 2.37.56__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/validate/EDINET/ControllerPluginData.py +32 -16
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +6 -1
- arelle/plugin/validate/EDINET/UploadContents.py +18 -2
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +1 -0
- arelle/plugin/validate/EDINET/rules/gfm.py +31 -0
- arelle/plugin/validate/EDINET/rules/upload.py +113 -26
- arelle/plugin/validate/ROS/rules/ros.py +2 -2
- {arelle_release-2.37.55.dist-info → arelle_release-2.37.56.dist-info}/METADATA +1 -1
- {arelle_release-2.37.55.dist-info → arelle_release-2.37.56.dist-info}/RECORD +14 -14
- {arelle_release-2.37.55.dist-info → arelle_release-2.37.56.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.55.dist-info → arelle_release-2.37.56.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.55.dist-info → arelle_release-2.37.56.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.55.dist-info → arelle_release-2.37.56.dist-info}/top_level.txt +0 -0
arelle/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.37.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 37,
|
|
31
|
+
__version__ = version = '2.37.56'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 37, 56)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -27,12 +27,14 @@ _: TypeGetText
|
|
|
27
27
|
@dataclass
|
|
28
28
|
class ControllerPluginData(PluginData):
|
|
29
29
|
_manifestInstancesById: dict[str, ManifestInstance]
|
|
30
|
+
_uploadContents: UploadContents | None
|
|
30
31
|
_usedFilepaths: set[Path]
|
|
31
32
|
|
|
32
33
|
def __init__(self, name: str):
|
|
33
34
|
super().__init__(name)
|
|
34
35
|
self._manifestInstancesById = {}
|
|
35
36
|
self._usedFilepaths = set()
|
|
37
|
+
self._uploadContents = None
|
|
36
38
|
|
|
37
39
|
def __hash__(self) -> int:
|
|
38
40
|
return id(self)
|
|
@@ -49,18 +51,21 @@ class ControllerPluginData(PluginData):
|
|
|
49
51
|
"""
|
|
50
52
|
return list(self._manifestInstancesById.values())
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
def getUploadContents(self) -> UploadContents | None:
|
|
55
|
+
return self._uploadContents
|
|
56
|
+
|
|
57
|
+
def setUploadContents(self, fileSource: FileSource) -> UploadContents:
|
|
55
58
|
reports = defaultdict(list)
|
|
56
59
|
uploadPaths = {}
|
|
57
|
-
for path in
|
|
60
|
+
for path, zipPath in self.getUploadFilepaths(fileSource).items():
|
|
58
61
|
if len(path.parts) == 0:
|
|
59
62
|
continue
|
|
63
|
+
assert isinstance(fileSource.basefile, str)
|
|
64
|
+
fullPath = Path(fileSource.basefile) / path
|
|
60
65
|
parents = list(reversed([p.name for p in path.parents if len(p.name) > 0]))
|
|
61
66
|
reportFolderType = None
|
|
62
67
|
isCorrection = True
|
|
63
|
-
isDirectory =
|
|
68
|
+
isDirectory = zipPath.is_dir()
|
|
64
69
|
isInSubdirectory = False
|
|
65
70
|
reportPath = None
|
|
66
71
|
if len(parents) > 0:
|
|
@@ -79,32 +84,43 @@ class ControllerPluginData(PluginData):
|
|
|
79
84
|
if not isCorrection:
|
|
80
85
|
reports[reportFolderType].append(path)
|
|
81
86
|
uploadPaths[path] = UploadPathInfo(
|
|
87
|
+
fullPath=fullPath,
|
|
82
88
|
isAttachment=reportFolderType is not None and reportFolderType.isAttachment,
|
|
83
89
|
isCorrection=isCorrection,
|
|
84
90
|
isCoverPage=not isDirectory and path.stem.startswith(Constants.COVER_PAGE_FILENAME_PREFIX),
|
|
85
|
-
isDirectory=
|
|
91
|
+
isDirectory=isDirectory,
|
|
86
92
|
isRoot=len(path.parts) == 1,
|
|
87
93
|
isSubdirectory=isInSubdirectory or (isDirectory and reportFolderType is not None),
|
|
88
94
|
path=path,
|
|
89
95
|
reportFolderType=reportFolderType,
|
|
90
96
|
reportPath=reportPath,
|
|
91
97
|
)
|
|
92
|
-
|
|
98
|
+
self._uploadContents = UploadContents(
|
|
93
99
|
reports={k: frozenset(v) for k, v in reports.items() if len(v) > 0},
|
|
94
|
-
uploadPaths=uploadPaths
|
|
100
|
+
uploadPaths=list(uploadPaths.values())
|
|
95
101
|
)
|
|
102
|
+
return self._uploadContents
|
|
96
103
|
|
|
97
104
|
@lru_cache(1)
|
|
98
|
-
def getUploadFilepaths(self, fileSource: FileSource) ->
|
|
105
|
+
def getUploadFilepaths(self, fileSource: FileSource) -> dict[Path, zipfile.Path]:
|
|
99
106
|
if not self.isUpload(fileSource):
|
|
100
|
-
return
|
|
101
|
-
paths =
|
|
107
|
+
return {}
|
|
108
|
+
paths = {}
|
|
102
109
|
assert isinstance(fileSource.fs, zipfile.ZipFile)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
paths
|
|
107
|
-
|
|
110
|
+
# First, fill in paths from zip file list
|
|
111
|
+
for file in fileSource.fs.filelist:
|
|
112
|
+
zipPath = zipfile.Path(fileSource.fs, file.filename)
|
|
113
|
+
paths[Path(file.filename)] = zipPath
|
|
114
|
+
# Then, fill in any parent directories that weren't in file list
|
|
115
|
+
for path in list(paths):
|
|
116
|
+
for parent in path.parents:
|
|
117
|
+
if parent in paths:
|
|
118
|
+
continue
|
|
119
|
+
paths[parent] = zipfile.Path(fileSource.fs, parent.as_posix() + '/')
|
|
120
|
+
return {
|
|
121
|
+
path: paths[path]
|
|
122
|
+
for path in sorted(paths)
|
|
123
|
+
}
|
|
108
124
|
|
|
109
125
|
@lru_cache(1)
|
|
110
126
|
def getUploadFileSizes(self, fileSource: FileSource) -> dict[Path, int]:
|
|
@@ -35,6 +35,7 @@ from .Constants import CORPORATE_FORMS, FormType, xhtmlDtdExtension, PROHIBITED_
|
|
|
35
35
|
from .ControllerPluginData import ControllerPluginData
|
|
36
36
|
from .ManifestInstance import ManifestInstance
|
|
37
37
|
from .Statement import Statement, STATEMENTS, BalanceSheet, StatementInstance, StatementType
|
|
38
|
+
from .UploadContents import UploadContents
|
|
38
39
|
|
|
39
40
|
_: TypeGetText
|
|
40
41
|
|
|
@@ -309,7 +310,7 @@ class PluginValidationDataExtension(PluginData):
|
|
|
309
310
|
for elt in modelDocument.xmlRootElement.iter():
|
|
310
311
|
if not isinstance(elt, ModelObject):
|
|
311
312
|
continue
|
|
312
|
-
for attributeName in elt.attrib
|
|
313
|
+
for attributeName in elt.attrib:
|
|
313
314
|
if attributeName in PROHIBITED_HTML_ATTRIBUTES:
|
|
314
315
|
results.append((elt, str(attributeName)))
|
|
315
316
|
return results
|
|
@@ -327,6 +328,10 @@ class PluginValidationDataExtension(PluginData):
|
|
|
327
328
|
elts.append(elt)
|
|
328
329
|
return elts
|
|
329
330
|
|
|
331
|
+
def getUploadContents(self, modelXbrl: ModelXbrl) -> UploadContents | None:
|
|
332
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
333
|
+
return controllerPluginData.getUploadContents()
|
|
334
|
+
|
|
330
335
|
@lru_cache(1)
|
|
331
336
|
def getUriAttributeValues(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str, str]]:
|
|
332
337
|
results: list[tuple[ModelObject, str, str]] = []
|
|
@@ -4,6 +4,7 @@ See COPYRIGHT.md for copyright information.
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
+
from functools import cached_property
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
from .ReportFolderType import ReportFolderType
|
|
@@ -12,15 +13,30 @@ from .ReportFolderType import ReportFolderType
|
|
|
12
13
|
@dataclass(frozen=True)
|
|
13
14
|
class UploadContents:
|
|
14
15
|
reports: dict[ReportFolderType, frozenset[Path]]
|
|
15
|
-
uploadPaths:
|
|
16
|
+
uploadPaths: list[UploadPathInfo]
|
|
16
17
|
|
|
17
18
|
@property
|
|
18
19
|
def sortedPaths(self) -> list[Path]:
|
|
19
|
-
return sorted(self.uploadPaths
|
|
20
|
+
return sorted(uploadPath.path for uploadPath in self.uploadPaths)
|
|
21
|
+
|
|
22
|
+
@cached_property
|
|
23
|
+
def uploadPathsByFullPath(self) -> dict[Path, UploadPathInfo]:
|
|
24
|
+
return {
|
|
25
|
+
uploadPath.fullPath: uploadPath
|
|
26
|
+
for uploadPath in self.uploadPaths
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@cached_property
|
|
30
|
+
def uploadPathsByPath(self) -> dict[Path, UploadPathInfo]:
|
|
31
|
+
return {
|
|
32
|
+
uploadPath.path: uploadPath
|
|
33
|
+
for uploadPath in self.uploadPaths
|
|
34
|
+
}
|
|
20
35
|
|
|
21
36
|
|
|
22
37
|
@dataclass(frozen=True)
|
|
23
38
|
class UploadPathInfo:
|
|
39
|
+
fullPath: Path
|
|
24
40
|
isAttachment: bool
|
|
25
41
|
isCorrection: bool
|
|
26
42
|
isCoverPage: bool
|
|
@@ -37,6 +37,7 @@ class ValidationPluginExtension(ValidationPlugin):
|
|
|
37
37
|
}, level=logging.INFO
|
|
38
38
|
)
|
|
39
39
|
pluginData = ControllerPluginData.get(filesource.cntlr, self.name)
|
|
40
|
+
pluginData.setUploadContents(filesource)
|
|
40
41
|
entrypointFiles = []
|
|
41
42
|
for instance in instances:
|
|
42
43
|
pluginData.addManifestInstance(instance)
|
|
@@ -670,3 +670,34 @@ def rule_gfm_1_3_8(
|
|
|
670
670
|
msg=_("The submitter-specific taxonomy has an embedded linkbase."),
|
|
671
671
|
modelObject=embeddedElements
|
|
672
672
|
)
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
@validation(
|
|
676
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
677
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
678
|
+
)
|
|
679
|
+
def rule_gfm_1_5_6(
|
|
680
|
+
pluginData: PluginValidationDataExtension,
|
|
681
|
+
val: ValidateXbrl,
|
|
682
|
+
*args: Any,
|
|
683
|
+
**kwargs: Any,
|
|
684
|
+
) -> Iterable[Validation]:
|
|
685
|
+
"""
|
|
686
|
+
EDINET.EC5700W: [GFM 1.5.6] The length of a label must be less than 511 characters unless it its role is documentation.
|
|
687
|
+
"""
|
|
688
|
+
labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
689
|
+
if labelRelationshipSet is None:
|
|
690
|
+
return
|
|
691
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
692
|
+
labelRels = labelRelationshipSet.fromModelObject(concept)
|
|
693
|
+
for rel in labelRels:
|
|
694
|
+
label = rel.toModelObject
|
|
695
|
+
if label.role != XbrlConst.documentationLabel and label.viewText() is not None and len(label.viewText()) > 511:
|
|
696
|
+
yield Validation.warning(
|
|
697
|
+
codes='EDINET.EC5700W.GFM.1.5.6',
|
|
698
|
+
msg=_("The concept of '%(concept)s' has a label classified as '%(role)s' that is longer than 511 characters: %(label)s"),
|
|
699
|
+
concept=concept.qname,
|
|
700
|
+
role=label.role,
|
|
701
|
+
label=label.viewText(),
|
|
702
|
+
modelObject=label
|
|
703
|
+
)
|
|
@@ -122,8 +122,10 @@ def rule_EC0100E(
|
|
|
122
122
|
For this implementation, we will allow all directories that may be valid for at least one submission type.
|
|
123
123
|
This allows for a false-negative outcome when a non-correction submission has a correction-only root directory.
|
|
124
124
|
"""
|
|
125
|
-
uploadContents = pluginData.getUploadContents(
|
|
126
|
-
|
|
125
|
+
uploadContents = pluginData.getUploadContents()
|
|
126
|
+
if uploadContents is None:
|
|
127
|
+
return
|
|
128
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items():
|
|
127
129
|
if pathInfo.isRoot and path.name not in ALLOWED_ROOT_FOLDERS:
|
|
128
130
|
yield Validation.error(
|
|
129
131
|
codes='EDINET.EC0100E',
|
|
@@ -153,8 +155,8 @@ def rule_EC0124E_EC0187E(
|
|
|
153
155
|
"""
|
|
154
156
|
uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
|
|
155
157
|
emptyDirectories = []
|
|
156
|
-
for path in uploadFilepaths:
|
|
157
|
-
if
|
|
158
|
+
for path, zipPath in uploadFilepaths.items():
|
|
159
|
+
if not zipPath.is_dir():
|
|
158
160
|
continue
|
|
159
161
|
if not any(path in p.parents for p in uploadFilepaths):
|
|
160
162
|
emptyDirectories.append(path)
|
|
@@ -227,8 +229,10 @@ def rule_EC0130E(
|
|
|
227
229
|
"""
|
|
228
230
|
EDINET.EC0130E: File extensions must match the file extensions allowed in Figure 2-1-3 and Figure 2-1-5.
|
|
229
231
|
"""
|
|
230
|
-
uploadContents = pluginData.getUploadContents(
|
|
231
|
-
|
|
232
|
+
uploadContents = pluginData.getUploadContents()
|
|
233
|
+
if uploadContents is None:
|
|
234
|
+
return
|
|
235
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items():
|
|
232
236
|
if pathInfo.reportFolderType is None or pathInfo.isDirectory:
|
|
233
237
|
continue
|
|
234
238
|
validExtensions = pathInfo.reportFolderType.getValidExtensions(pathInfo.isCorrection, pathInfo.isSubdirectory)
|
|
@@ -265,7 +269,9 @@ def rule_EC0132E(
|
|
|
265
269
|
"""
|
|
266
270
|
EDINET.EC0132E: Store the manifest file directly under the relevant folder.
|
|
267
271
|
"""
|
|
268
|
-
uploadContents = pluginData.getUploadContents(
|
|
272
|
+
uploadContents = pluginData.getUploadContents()
|
|
273
|
+
if uploadContents is None:
|
|
274
|
+
return
|
|
269
275
|
for reportFolderType, paths in uploadContents.reports.items():
|
|
270
276
|
if reportFolderType.isAttachment:
|
|
271
277
|
continue
|
|
@@ -353,8 +359,10 @@ def rule_EC0192E(
|
|
|
353
359
|
PublicDoc cover file. Please delete the cover file from PrivateDoc and upload
|
|
354
360
|
it again.
|
|
355
361
|
"""
|
|
356
|
-
uploadContents = pluginData.getUploadContents(
|
|
357
|
-
|
|
362
|
+
uploadContents = pluginData.getUploadContents()
|
|
363
|
+
if uploadContents is None:
|
|
364
|
+
return
|
|
365
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items():
|
|
358
366
|
if not pathInfo.isCoverPage:
|
|
359
367
|
continue
|
|
360
368
|
# Only applies to PrivateDoc correction reports
|
|
@@ -383,8 +391,8 @@ def rule_EC0198E(
|
|
|
383
391
|
"""
|
|
384
392
|
fileCounts: dict[Path, int] = defaultdict(int)
|
|
385
393
|
uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
|
|
386
|
-
for path in uploadFilepaths:
|
|
387
|
-
if
|
|
394
|
+
for path, zipPath in uploadFilepaths.items():
|
|
395
|
+
if zipPath.is_dir():
|
|
388
396
|
continue
|
|
389
397
|
for directory in FILE_COUNT_LIMITS.keys():
|
|
390
398
|
if directory in path.parents:
|
|
@@ -421,10 +429,12 @@ def rule_EC0233E(
|
|
|
421
429
|
NOTE: This includes files in subdirectories. For example, PublicDoc/00000000_images/image.png
|
|
422
430
|
comes before PublicDoc/0000000_header_*.htm
|
|
423
431
|
"""
|
|
424
|
-
uploadContents = pluginData.getUploadContents(
|
|
432
|
+
uploadContents = pluginData.getUploadContents()
|
|
433
|
+
if uploadContents is None:
|
|
434
|
+
return
|
|
425
435
|
directories = defaultdict(list)
|
|
426
436
|
for path in uploadContents.sortedPaths:
|
|
427
|
-
pathInfo = uploadContents.
|
|
437
|
+
pathInfo = uploadContents.uploadPathsByPath[path]
|
|
428
438
|
if pathInfo.isDirectory:
|
|
429
439
|
continue
|
|
430
440
|
if pathInfo.reportFolderType in (ReportFolderType.PRIVATE_DOC, ReportFolderType.PUBLIC_DOC):
|
|
@@ -462,8 +472,10 @@ def rule_EC0234E(
|
|
|
462
472
|
"""
|
|
463
473
|
EDINET.EC0234E: A cover file exists in an unsupported subdirectory.
|
|
464
474
|
"""
|
|
465
|
-
uploadContents = pluginData.getUploadContents(
|
|
466
|
-
|
|
475
|
+
uploadContents = pluginData.getUploadContents()
|
|
476
|
+
if uploadContents is None:
|
|
477
|
+
return
|
|
478
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items():
|
|
467
479
|
if pathInfo.isDirectory:
|
|
468
480
|
continue
|
|
469
481
|
if pathInfo.reportFolderType not in (ReportFolderType.PRIVATE_DOC, ReportFolderType.PUBLIC_DOC):
|
|
@@ -549,14 +561,16 @@ def rule_EC0349E(
|
|
|
549
561
|
EDINET.EC0349E: An unexpected directory or file exists in the XBRL directory.
|
|
550
562
|
Only PublicDoc, PrivateDoc, or AuditDoc directories may exist beneath the XBRL directory.
|
|
551
563
|
"""
|
|
552
|
-
|
|
564
|
+
uploadContents = pluginData.getUploadContents()
|
|
565
|
+
if uploadContents is None:
|
|
566
|
+
return
|
|
553
567
|
xbrlDirectoryPath = Path('XBRL')
|
|
554
568
|
allowedPaths = {p.xbrlDirectory for p in (
|
|
555
569
|
ReportFolderType.AUDIT_DOC,
|
|
556
570
|
ReportFolderType.PRIVATE_DOC,
|
|
557
571
|
ReportFolderType.PUBLIC_DOC,
|
|
558
572
|
)}
|
|
559
|
-
for path, pathInfo in
|
|
573
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items():
|
|
560
574
|
if path.parent != xbrlDirectoryPath:
|
|
561
575
|
continue
|
|
562
576
|
if path not in allowedPaths:
|
|
@@ -583,8 +597,10 @@ def rule_EC0352E(
|
|
|
583
597
|
"""
|
|
584
598
|
EDINET.EC0352E: An XBRL file with an invalid name exists.
|
|
585
599
|
"""
|
|
586
|
-
|
|
587
|
-
|
|
600
|
+
uploadContents = pluginData.getUploadContents()
|
|
601
|
+
if uploadContents is None:
|
|
602
|
+
return
|
|
603
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items():
|
|
588
604
|
if (
|
|
589
605
|
pathInfo.isDirectory or
|
|
590
606
|
pathInfo.isCorrection or
|
|
@@ -603,6 +619,43 @@ def rule_EC0352E(
|
|
|
603
619
|
)
|
|
604
620
|
|
|
605
621
|
|
|
622
|
+
@validation(
|
|
623
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
624
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
625
|
+
)
|
|
626
|
+
def rules_cover_page(
|
|
627
|
+
pluginData: PluginValidationDataExtension,
|
|
628
|
+
val: ValidateXbrl,
|
|
629
|
+
*args: Any,
|
|
630
|
+
**kwargs: Any,
|
|
631
|
+
) -> Iterable[Validation]:
|
|
632
|
+
"""
|
|
633
|
+
EDINET.EC1000E: Cover page must contain "【表紙】".
|
|
634
|
+
"""
|
|
635
|
+
uploadContents = pluginData.getUploadContents(val.modelXbrl)
|
|
636
|
+
if uploadContents is None:
|
|
637
|
+
return
|
|
638
|
+
for url, doc in val.modelXbrl.urlDocs.items():
|
|
639
|
+
path = Path(url)
|
|
640
|
+
pathInfo = uploadContents.uploadPathsByFullPath.get(path)
|
|
641
|
+
if pathInfo is None or not pathInfo.isCoverPage:
|
|
642
|
+
continue
|
|
643
|
+
rootElt = doc.xmlRootElement
|
|
644
|
+
coverPageTextFound = False
|
|
645
|
+
for elt in rootElt.iterdescendants():
|
|
646
|
+
if not coverPageTextFound and elt.text and '【表紙】' in elt.text:
|
|
647
|
+
coverPageTextFound = True
|
|
648
|
+
# TODO: Other checks related to the cover page will be implemented here.
|
|
649
|
+
if not coverPageTextFound:
|
|
650
|
+
yield Validation.error(
|
|
651
|
+
codes='EDINET.EC1000E',
|
|
652
|
+
msg=_("There is no '【表紙】' on the cover page. "
|
|
653
|
+
"File name: '%(file)s'. "
|
|
654
|
+
"Please add '【表紙】' to the relevant file."),
|
|
655
|
+
file=doc.basename,
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
|
|
606
659
|
@validation(
|
|
607
660
|
hook=ValidationHook.XBRL_FINALLY,
|
|
608
661
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -644,8 +697,13 @@ def rule_uri_references(
|
|
|
644
697
|
"""
|
|
645
698
|
EDINET.EC1007E: The URI in the HTML specifies a URL or absolute path.
|
|
646
699
|
EDINET.EC1013E: The URI in the HTML specifies a path not under a subdirectory.
|
|
647
|
-
EDINET.EC1014E: The URI in the HTML specifies a path to a
|
|
700
|
+
EDINET.EC1014E: The URI in the HTML specifies a path to a directory.
|
|
701
|
+
EDINET.EC1021E: The URI in the HTML specifies a path to a file that doesn't exist.
|
|
702
|
+
EDINET.EC1023E: The URI in the HTML specifies a path to a PDF file.
|
|
648
703
|
"""
|
|
704
|
+
uploadContents = pluginData.getUploadContents(val.modelXbrl)
|
|
705
|
+
if uploadContents is None:
|
|
706
|
+
return
|
|
649
707
|
for uriReference in pluginData.uriReferences:
|
|
650
708
|
if UrlUtil.isAbsolute(uriReference.attributeValue):
|
|
651
709
|
yield Validation.error(
|
|
@@ -671,10 +729,11 @@ def rule_uri_references(
|
|
|
671
729
|
)
|
|
672
730
|
continue
|
|
673
731
|
fullPath = Path(uriReference.document.uri).parent / path
|
|
674
|
-
|
|
732
|
+
pathInfo = uploadContents.uploadPathsByFullPath.get(fullPath)
|
|
733
|
+
if pathInfo is not None and pathInfo.isDirectory:
|
|
675
734
|
yield Validation.error(
|
|
676
735
|
codes='EDINET.EC1014E',
|
|
677
|
-
msg=_("The URI in the HTML specifies a path to a
|
|
736
|
+
msg=_("The URI in the HTML specifies a path to a directory. "
|
|
678
737
|
"File name: '%(file)s' (line %(line)s). "
|
|
679
738
|
"Please update the URI to reference a file."),
|
|
680
739
|
file=uriReference.document.basename,
|
|
@@ -682,6 +741,29 @@ def rule_uri_references(
|
|
|
682
741
|
modelObject=uriReference.element,
|
|
683
742
|
)
|
|
684
743
|
continue
|
|
744
|
+
if path.suffix.lower() == '.pdf':
|
|
745
|
+
yield Validation.error(
|
|
746
|
+
codes='EDINET.EC1023E',
|
|
747
|
+
msg=_("The URI in the HTML specifies a path to a PDF file. "
|
|
748
|
+
"File name: '%(file)s' (line %(line)s). "
|
|
749
|
+
"Please remove the link from the relevant file."),
|
|
750
|
+
file=uriReference.document.basename,
|
|
751
|
+
line=uriReference.element.sourceline,
|
|
752
|
+
modelObject=uriReference.element,
|
|
753
|
+
)
|
|
754
|
+
continue
|
|
755
|
+
if not val.modelXbrl.fileSource.exists(str(fullPath)):
|
|
756
|
+
yield Validation.error(
|
|
757
|
+
codes='EDINET.EC1021E',
|
|
758
|
+
msg=_("The linked file ('%(path)s') does not exist. "
|
|
759
|
+
"File name: '%(file)s' (line %(line)s). "
|
|
760
|
+
"Please update the URI to reference a file."),
|
|
761
|
+
path=str(path),
|
|
762
|
+
file=uriReference.document.basename,
|
|
763
|
+
line=uriReference.element.sourceline,
|
|
764
|
+
modelObject=uriReference.element,
|
|
765
|
+
)
|
|
766
|
+
continue
|
|
685
767
|
|
|
686
768
|
|
|
687
769
|
@validation(
|
|
@@ -727,10 +809,12 @@ def rule_EC1017E(
|
|
|
727
809
|
"""
|
|
728
810
|
EDINET.EC1017E: There is an unused file.
|
|
729
811
|
"""
|
|
730
|
-
uploadContents = pluginData.getUploadContents(
|
|
812
|
+
uploadContents = pluginData.getUploadContents()
|
|
813
|
+
if uploadContents is None:
|
|
814
|
+
return
|
|
731
815
|
existingSubdirectoryFilepaths = {
|
|
732
816
|
path
|
|
733
|
-
for path, pathInfo in uploadContents.
|
|
817
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items()
|
|
734
818
|
if pathInfo.isSubdirectory and not pathInfo.isDirectory
|
|
735
819
|
}
|
|
736
820
|
usedFilepaths = pluginData.getUsedFilepaths()
|
|
@@ -810,7 +894,7 @@ def rule_EC1031E(
|
|
|
810
894
|
msg=_("Prohibited attribute '%(attributeName)s' is used in HTML. "
|
|
811
895
|
"File name: %(file)s (line %(line)s). "
|
|
812
896
|
"Please correct the tag attributes of the relevant file."),
|
|
813
|
-
attributeName=
|
|
897
|
+
attributeName=attributeName,
|
|
814
898
|
file=doc.basename,
|
|
815
899
|
line=elt.sourceline,
|
|
816
900
|
modelObject=elt,
|
|
@@ -838,7 +922,10 @@ def rule_filenames(
|
|
|
838
922
|
than those allowed (alphanumeric characters, '-' and '_').
|
|
839
923
|
Note: Applies ONLY to files directly beneath non-correction report folders.
|
|
840
924
|
"""
|
|
841
|
-
|
|
925
|
+
uploadContents = pluginData.getUploadContents()
|
|
926
|
+
if uploadContents is None:
|
|
927
|
+
return
|
|
928
|
+
for path, pathInfo in uploadContents.uploadPathsByPath.items():
|
|
842
929
|
isReportFile = (
|
|
843
930
|
not pathInfo.isAttachment and
|
|
844
931
|
not pathInfo.isCorrection and
|
|
@@ -302,14 +302,14 @@ def rule_ros19(
|
|
|
302
302
|
equity_facts = val.modelXbrl.factsByLocalName.get(EQUITY, set())
|
|
303
303
|
largest_equity_fact = None
|
|
304
304
|
for e_fact in equity_facts:
|
|
305
|
-
if e_fact.xValid >= VALID:
|
|
305
|
+
if e_fact.xValid >= VALID and e_fact.xValue is not None:
|
|
306
306
|
if largest_equity_fact is None or abs(convertToDecimal(e_fact.xValue)) > abs(convertToDecimal(largest_equity_fact.xValue)):
|
|
307
307
|
largest_equity_fact = e_fact
|
|
308
308
|
|
|
309
309
|
turnover_facts = val.modelXbrl.factsByLocalName.get(TURNOVER_REVENUE, set())
|
|
310
310
|
largest_turnover_fact = None
|
|
311
311
|
for t_fact in turnover_facts:
|
|
312
|
-
if t_fact.xValid >= VALID:
|
|
312
|
+
if t_fact.xValid >= VALID and t_fact.xValue is not None:
|
|
313
313
|
if largest_turnover_fact is None or convertToDecimal(t_fact.xValue) > convertToDecimal(largest_turnover_fact.xValue):
|
|
314
314
|
largest_turnover_fact = t_fact
|
|
315
315
|
|
|
@@ -125,7 +125,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
|
|
|
125
125
|
arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
|
|
126
126
|
arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
|
|
127
127
|
arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
128
|
-
arelle/_version.py,sha256=
|
|
128
|
+
arelle/_version.py,sha256=fDTO72M4k2Bg8Q2mmpC5_JjetoiOyfrQn3nJSak1r6w,708
|
|
129
129
|
arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
|
|
130
130
|
arelle/api/Session.py,sha256=kgSxS7VckA1sQ7xp0pJiK7IK-vRxAdAZKUo8gEx27s8,7549
|
|
131
131
|
arelle/config/creationSoftwareNames.json,sha256=5MK7XUjfDJ9OpRCCHXeOErJ1SlTBZji4WEcEOdOacx0,3128
|
|
@@ -313,14 +313,14 @@ arelle/plugin/validate/DBA/rules/tr.py,sha256=4TootFjl0HXsKZk1XNBCyj-vnjRs4lg35h
|
|
|
313
313
|
arelle/plugin/validate/EBA/__init__.py,sha256=x3zXNcdSDJ3kHfL7kMs0Ve0Vs9oWbzNFVf1TK4Avmy8,45924
|
|
314
314
|
arelle/plugin/validate/EBA/config.xml,sha256=37wMVUAObK-XEqakqD8zPNog20emYt4a_yfL1AKubF8,2022
|
|
315
315
|
arelle/plugin/validate/EDINET/Constants.py,sha256=IkJ1R0ZQAs9kUXY8eB7e8azYlC4m500VILrQhniB5Q4,1949
|
|
316
|
-
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=
|
|
316
|
+
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=zsBOWkSatXza0v_ZB-jIdFLSIj9Btzd50GdNIgVw6Fo,8027
|
|
317
317
|
arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
|
|
318
318
|
arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
|
|
319
|
-
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=
|
|
319
|
+
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=CVTtWTYrvC_oeK9iWHLt5l9dN3KmzgLAEZmGdlPqv3g,16362
|
|
320
320
|
arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=Q-9a-5tJfhK-cjY8WUB2AT1NI-Nn9cFtARVOIJoLRGU,2979
|
|
321
321
|
arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
|
|
322
|
-
arelle/plugin/validate/EDINET/UploadContents.py,sha256=
|
|
323
|
-
arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=
|
|
322
|
+
arelle/plugin/validate/EDINET/UploadContents.py,sha256=o29mDoX48M3S2jQqrJO4ZaulltAPt4vD-qdsWTMCUPc,1196
|
|
323
|
+
arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=8LNqvXzNaWP54dShEjet5ely4BnM8ByCSyimKpUx3_s,2577
|
|
324
324
|
arelle/plugin/validate/EDINET/__init__.py,sha256=ECWgqzwHA7MZ3g7SeoFI7ttR9Wq_lywV-TlqeUW_juY,3186
|
|
325
325
|
arelle/plugin/validate/EDINET/resources/config.xml,sha256=7uT4GcRgk5veMLpFhPPQJxbGKiQvM52P8EMrjn0qd0g,646
|
|
326
326
|
arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5OY3vn5hQxVFAAxOmDSOYpuyQe6VnWSY0,16285
|
|
@@ -328,9 +328,9 @@ arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
|
|
|
328
328
|
arelle/plugin/validate/EDINET/rules/contexts.py,sha256=KPoyWfRaURvxoGVcWP64mTMTAKPMSmQSX06RClCLddw,7590
|
|
329
329
|
arelle/plugin/validate/EDINET/rules/edinet.py,sha256=VYrDZaKbsQuQEvOY5F0Pv4Jzk9YZ4iETOkAFOggrhEY,12632
|
|
330
330
|
arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
|
|
331
|
-
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=
|
|
331
|
+
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=1SBUQG2M4VOhxp4e2ALnm_Uq8p3g-S0WbKkRCEH-ttw,26769
|
|
332
332
|
arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
|
|
333
|
-
arelle/plugin/validate/EDINET/rules/upload.py,sha256=
|
|
333
|
+
arelle/plugin/validate/EDINET/rules/upload.py,sha256=opGEEFf80RLKk_Z43gkUYHJLBvq_Hd-BMzMMg2Vhnns,40064
|
|
334
334
|
arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
|
|
335
335
|
arelle/plugin/validate/ESEF/Dimensions.py,sha256=MOJM7vwNPEmV5cu-ZzPrhx3347ZvxgD6643OB2HRnIk,10597
|
|
336
336
|
arelle/plugin/validate/ESEF/Util.py,sha256=QH3btcGqBpr42M7WSKZLSdNXygZaZLfEiEjlxoG21jE,7950
|
|
@@ -365,7 +365,7 @@ arelle/plugin/validate/ROS/__init__.py,sha256=KuWg1MHVzA2S6eaHFptvP3Vu_5gQWf3OUY
|
|
|
365
365
|
arelle/plugin/validate/ROS/config.xml,sha256=ZCpCFgr1ZAjoUuhb1eRpDnmKrae-sXA9yl6SOWnrfm8,654
|
|
366
366
|
arelle/plugin/validate/ROS/resources/config.xml,sha256=HXWume5HlrAqOx5AtiWWqgADbRatA8YSfm_JvZGwdgQ,657
|
|
367
367
|
arelle/plugin/validate/ROS/rules/__init__.py,sha256=wW7BUAIb7sRkOxC1Amc_ZKrz03FM-Qh1TyZe6wxYaAU,1567
|
|
368
|
-
arelle/plugin/validate/ROS/rules/ros.py,sha256=
|
|
368
|
+
arelle/plugin/validate/ROS/rules/ros.py,sha256=yGvvJZJwENgFLpA5r464eez1aHf3pKZm0HyzaWiQn6E,19930
|
|
369
369
|
arelle/plugin/validate/UK/ValidateUK.py,sha256=h7-tnCubHme8Meaif-o55TV2rCfMWuikfpZCcK6NNDs,56447
|
|
370
370
|
arelle/plugin/validate/UK/__init__.py,sha256=GT67T_7qpOASzdmgRXFPmLxfPg3Gjubli7luQDK3zck,27462
|
|
371
371
|
arelle/plugin/validate/UK/config.xml,sha256=mUFhWDfBzGTn7v0ZSmf4HaweQTMJh_4ZcJmD9mzCHrA,1547
|
|
@@ -680,9 +680,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
|
|
|
680
680
|
arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
681
681
|
arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
682
682
|
arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
|
|
683
|
-
arelle_release-2.37.
|
|
684
|
-
arelle_release-2.37.
|
|
685
|
-
arelle_release-2.37.
|
|
686
|
-
arelle_release-2.37.
|
|
687
|
-
arelle_release-2.37.
|
|
688
|
-
arelle_release-2.37.
|
|
683
|
+
arelle_release-2.37.56.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
|
|
684
|
+
arelle_release-2.37.56.dist-info/METADATA,sha256=Wtmg0dACPtcIDHq93LetjVlxqOerxZ1VGQdsa1ZgUds,9327
|
|
685
|
+
arelle_release-2.37.56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
686
|
+
arelle_release-2.37.56.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
|
|
687
|
+
arelle_release-2.37.56.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
|
|
688
|
+
arelle_release-2.37.56.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|