arelle-release 2.37.47__py3-none-any.whl → 2.37.48__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arelle-release might be problematic. Click here for more details.

Files changed (94) hide show
  1. arelle/CntlrCmdLine.py +5 -1
  2. arelle/ModelObjectFactory.py +18 -2
  3. arelle/_version.py +2 -2
  4. {arelle_release-2.37.47.dist-info → arelle_release-2.37.48.dist-info}/METADATA +1 -1
  5. {arelle_release-2.37.47.dist-info → arelle_release-2.37.48.dist-info}/RECORD +9 -94
  6. arelle/archive/CustomLogger.py +0 -43
  7. arelle/archive/LoadEFMvalidate.py +0 -32
  8. arelle/archive/LoadSavePreLbCsv.py +0 -26
  9. arelle/archive/LoadValidate.cs +0 -31
  10. arelle/archive/LoadValidate.py +0 -36
  11. arelle/archive/LoadValidateCmdLine.java +0 -69
  12. arelle/archive/LoadValidatePostedZip.java +0 -57
  13. arelle/archive/LoadValidateWebService.java +0 -34
  14. arelle/archive/SaveTableToExelle.py +0 -140
  15. arelle/archive/TR3toTR4.py +0 -88
  16. arelle/archive/plugin/ESEF_2022/__init__.py +0 -47
  17. arelle/archive/plugin/bigInstance.py +0 -394
  18. arelle/archive/plugin/cmdWebServerExtension.py +0 -43
  19. arelle/archive/plugin/crashTest.py +0 -38
  20. arelle/archive/plugin/functionsXmlCreation.py +0 -106
  21. arelle/archive/plugin/hello_i18n.pot +0 -26
  22. arelle/archive/plugin/hello_i18n.py +0 -32
  23. arelle/archive/plugin/importTestChild1.py +0 -21
  24. arelle/archive/plugin/importTestChild2.py +0 -22
  25. arelle/archive/plugin/importTestGrandchild1.py +0 -21
  26. arelle/archive/plugin/importTestGrandchild2.py +0 -21
  27. arelle/archive/plugin/importTestImported1.py +0 -23
  28. arelle/archive/plugin/importTestImported11.py +0 -22
  29. arelle/archive/plugin/importTestParent.py +0 -48
  30. arelle/archive/plugin/instanceInfo.py +0 -306
  31. arelle/archive/plugin/loadFromOIM-2018.py +0 -1282
  32. arelle/archive/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
  33. arelle/archive/plugin/objectmaker.py +0 -285
  34. arelle/archive/plugin/packagedImportTest/__init__.py +0 -47
  35. arelle/archive/plugin/packagedImportTest/importTestChild1.py +0 -21
  36. arelle/archive/plugin/packagedImportTest/importTestChild2.py +0 -22
  37. arelle/archive/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
  38. arelle/archive/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
  39. arelle/archive/plugin/packagedImportTest/importTestImported1.py +0 -24
  40. arelle/archive/plugin/packagedImportTest/importTestImported11.py +0 -21
  41. arelle/archive/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
  42. arelle/archive/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
  43. arelle/archive/plugin/sakaCalendar.py +0 -215
  44. arelle/archive/plugin/saveInstanceInfoset.py +0 -121
  45. arelle/archive/plugin/sphinx/FormulaGenerator.py +0 -823
  46. arelle/archive/plugin/sphinx/SphinxContext.py +0 -404
  47. arelle/archive/plugin/sphinx/SphinxEvaluator.py +0 -783
  48. arelle/archive/plugin/sphinx/SphinxMethods.py +0 -1287
  49. arelle/archive/plugin/sphinx/SphinxParser.py +0 -1093
  50. arelle/archive/plugin/sphinx/SphinxValidator.py +0 -163
  51. arelle/archive/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
  52. arelle/archive/plugin/sphinx/__init__.py +0 -285
  53. arelle/archive/plugin/streamingExtensions.py +0 -335
  54. arelle/archive/plugin/updateTableLB.py +0 -242
  55. arelle/archive/plugin/validate/SBRnl/CustomLoader.py +0 -19
  56. arelle/archive/plugin/validate/SBRnl/DTS.py +0 -305
  57. arelle/archive/plugin/validate/SBRnl/Dimensions.py +0 -357
  58. arelle/archive/plugin/validate/SBRnl/Document.py +0 -799
  59. arelle/archive/plugin/validate/SBRnl/Filing.py +0 -467
  60. arelle/archive/plugin/validate/SBRnl/__init__.py +0 -75
  61. arelle/archive/plugin/validate/SBRnl/config.xml +0 -26
  62. arelle/archive/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
  63. arelle/archive/plugin/validate/USBestPractices.py +0 -570
  64. arelle/archive/plugin/validate/USCorpAction.py +0 -557
  65. arelle/archive/plugin/validate/USSecTagging.py +0 -337
  66. arelle/archive/plugin/validate/XDC/__init__.py +0 -77
  67. arelle/archive/plugin/validate/XDC/config.xml +0 -20
  68. arelle/archive/plugin/validate/XFsyntax/__init__.py +0 -64
  69. arelle/archive/plugin/validate/XFsyntax/xf.py +0 -2227
  70. arelle/archive/plugin/validate/calc2.py +0 -536
  71. arelle/archive/plugin/validateSchemaLxml.py +0 -156
  72. arelle/archive/plugin/validateTableInfoset.py +0 -52
  73. arelle/archive/us-gaap-dei-docType-extraction-frm.xml +0 -90
  74. arelle/archive/us-gaap-dei-ratio-cash-frm.xml +0 -150
  75. arelle/examples/plugin/formulaSuiteConverter.py +0 -212
  76. arelle/examples/plugin/functionsCustom.py +0 -59
  77. arelle/examples/plugin/hello_dolly.py +0 -64
  78. arelle/examples/plugin/multi.py +0 -58
  79. arelle/examples/plugin/rssSaveOim.py +0 -96
  80. arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
  81. arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
  82. arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -50
  83. arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
  84. arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
  85. arelle/examples/plugin/validate/XYZ/rules/__init__.py +0 -0
  86. arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -110
  87. arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -59
  88. arelle/scripts-macOS/startWebServer.command +0 -3
  89. arelle/scripts-unix/startWebServer.sh +0 -1
  90. arelle/scripts-windows/startWebServer.bat +0 -5
  91. {arelle_release-2.37.47.dist-info → arelle_release-2.37.48.dist-info}/WHEEL +0 -0
  92. {arelle_release-2.37.47.dist-info → arelle_release-2.37.48.dist-info}/entry_points.txt +0 -0
  93. {arelle_release-2.37.47.dist-info → arelle_release-2.37.48.dist-info}/licenses/LICENSE.md +0 -0
  94. {arelle_release-2.37.47.dist-info → arelle_release-2.37.48.dist-info}/top_level.txt +0 -0
@@ -1,536 +0,0 @@
1
- '''
2
- See COPYRIGHT.md for copyright information.
3
- '''
4
-
5
- import time
6
- from math import isnan, isinf
7
- from collections import defaultdict
8
- from decimal import Decimal
9
- from arelle import Locale
10
- from arelle.PythonUtil import OrderedDefaultDict
11
- from arelle.ValidateXbrlCalcs import ZERO, inferredDecimals, rangeValue
12
- from arelle.Version import authorLabel, copyrightLabel
13
- from arelle.XbrlConst import link, xbrli, xl, xlink, domainMember
14
-
15
- calc2YYYY = "http://xbrl.org/WGWD/YYYY-MM-DD/calculation-2.0"
16
- calc2 = {calc2YYYY}
17
- calc2e = "http://xbrl.org/WGWD/YYYY-MM-DD/calculation-2.0/error"
18
- sectionFact = "http://xbrl.org/arcrole/WGWD/YYYY-MM-DD/section-fact"
19
- calc2linkroles = "{http://xbrl.org/WGWD/YYYY-MM-DD/calculation-2.0}linkroles"
20
- summationItem = "http://xbrl.org/arcrole/WGWD/YYYY-MM-DD/summation-item" # calc2 summation-item arc role
21
- balanceChanges = "http://xbrl.org/arcrole/WGWD/YYYY-MM-DD/balance-changes"
22
- aggregationDomain = "http://xbrl.org/arcrole/WGWD/YYYY-MM-DD/aggregation-domain"
23
- calc2Arcroles = (summationItem, balanceChanges, aggregationDomain)
24
-
25
- def nominalPeriod(duration): # account for month and year lengths
26
- if 364 < duration.days <= 366: return 365
27
- if 28 <= duration.days <= 31: return 31
28
- return duration.days
29
-
30
- def intervalZero():
31
- return (Decimal(0), Decimal(0))
32
-
33
- NIL = "(nil)" # singleton object, use "is" to compare, not the value
34
- NILinterval = (NIL,NIL)
35
-
36
- def intervalValue(fact, dec=None): # value in decimals
37
- if fact.isNil:
38
- return NILinterval
39
- if dec is None:
40
- dec = inferredDecimals(fact)
41
- return rangeValue(fact.value, dec)
42
-
43
- def addInterval(boundValues, key, intervalValue, weight=None):
44
- a, b, _inclA, _inclB = intervalValue
45
- if a is NIL:
46
- result = NILinterval
47
- else:
48
- r = boundValues[key]
49
- if r is NILinterval:
50
- return
51
- elif weight is not None:
52
- result = (r[0] + weight * a, r[1] + weight * b)
53
- else:
54
- result = (r[0] + a, r[1] + b)
55
- boundValues[key] = result
56
-
57
-
58
- class ValidateXbrlCalc2:
59
- def __init__(self, val):
60
- self.val = val
61
- self.cntlr = val.modelXbrl.modelManager.cntlr
62
- self.modelXbrl = val.modelXbrl
63
- self.standardTaxonomiesDict = val.disclosureSystem.standardTaxonomiesDict
64
- self.eqCntx = {} # contexts which are OIM-aspect equivalent
65
- self.eqUnit = {} # units which are equivalent
66
- self.sumInitArcrole = self.perBindArcrole = self.aggBindArcrole = None
67
- self.sumConceptBindKeys = defaultdict(set)
68
- self.sumBoundFacts = defaultdict(list)
69
- self.perConceptBindKeys = defaultdict(set)
70
- self.perBoundFacts = defaultdict(list)
71
- self.durationPeriodStarts = defaultdict(set)
72
- self.aggConceptBindKeys = defaultdict(set)
73
- self.aggBoundFacts = defaultdict(list)
74
- self.aggBoundConceptFacts = defaultdict(list)
75
- self.aggDimInit = set()
76
-
77
- def validate(self):
78
- modelXbrl = self.modelXbrl
79
- if not modelXbrl.contexts or not modelXbrl.facts:
80
- return # skip if no contexts or facts
81
-
82
- startedAt = time.time()
83
-
84
- # check balance attributes and weights, same as XBRL 2.1
85
- for rel in modelXbrl.relationshipSet(calc2Arcroles).modelRelationships:
86
- weight = rel.weight
87
- fromConcept = rel.fromModelObject
88
- toConcept = rel.toModelObject
89
- if fromConcept is not None and toConcept is not None:
90
- if rel.arcrole == aggregationDomain:
91
- rel.dimension = rel.arcElement.prefixedNameQname(rel.get("dimension"))
92
- if rel.dimension is None or not modelXbrl.qnameConcepts[rel.dimension].isDimensionItem:
93
- modelXbrl.error("calc2e:invalidAggregationDimension",
94
- _("Aggregation-domain relationship has invalid dimension %(dimension)s in link role %(linkrole)s"),
95
- modelObject=rel,
96
- dimension=rel.get("dimension"), linkrole=ELR)
97
- elif fromConcept != toConcept or not fromConcept.isDomainMember:
98
- modelXbrl.error("calc2e:invalidAggregationDomain",
99
- _("Calculation relationship has invalid domain %(domain)s in link role %(linkrole)s"),
100
- modelObject=rel,
101
- domain=fromConcept, linkrole=ELR)
102
- continue
103
- if rel.arcrole == balanceChanges:
104
- if fromConcept.periodType != "instant" or toConcept.periodType != "duration":
105
- modelXbrl.error("calc2e:invalidBalanceChangesPeriodType",
106
- _("Balance-changes relationship must have instant source concept and duration target concept in link role %(linkrole)s"),
107
- modelObject=rel, linkrole=ELR)
108
- if weight not in (1, -1):
109
- modelXbrl.error("calc2e:invalidWeight",
110
- _("Calculation relationship has invalid weight from %(source)s to %(target)s in link role %(linkrole)s"),
111
- modelObject=rel,
112
- source=fromConcept.qname, target=toConcept.qname, linkrole=ELR)
113
- fromBalance = fromConcept.balance
114
- toBalance = toConcept.balance
115
- if fromBalance and toBalance:
116
- if (fromBalance == toBalance and weight < 0) or \
117
- (fromBalance != toBalance and weight > 0):
118
- modelXbrl.error("calc2e:balanceCalcWeightIllegal" +
119
- ("Negative" if weight < 0 else "Positive"),
120
- _("Calculation relationship has illegal weight %(weight)s from %(source)s, %(sourceBalance)s, to %(target)s, %(targetBalance)s, in link role %(linkrole)s (per 5.1.1.2 Table 6)"),
121
- modelObject=rel, weight=weight,
122
- source=fromConcept.qname, target=toConcept.qname, linkrole=rel.linkrole,
123
- sourceBalance=fromBalance, targetBalance=toBalance,
124
- messageCodes=("calc2e:balanceCalcWeightIllegalNegative", "calc2:balanceCalcWeightIllegalPositive"))
125
- if not fromConcept.isNumeric or not toConcept.isNumeric:
126
- modelXbrl.error("calc2e:nonNumericCalc",
127
- _("Calculation relationship has illegal concept from %(source)s%(sourceNumericDecorator)s to %(target)s%(targetNumericDecorator)s in link role %(linkrole)s"),
128
- modelObject=rel,
129
- source=fromConcept.qname, target=toConcept.qname, linkrole=rel.linkrole,
130
- sourceNumericDecorator="" if fromConcept.isNumeric else _(" (non-numeric)"),
131
- targetNumericDecorator="" if toConcept.isNumeric else _(" (non-numeric)"))
132
-
133
- # identify equal contexts
134
- uniqueCntxHashes = {}
135
- self.modelXbrl.profileActivity()
136
- for cntx in modelXbrl.contexts.values():
137
- h = hash( (cntx.periodHash, cntx.entityIdentifierHash, cntx.dimsHash) ) # OIM-compatible hash
138
- if h in uniqueCntxHashes:
139
- if cntx.isEqualTo(uniqueCntxHashes[h]):
140
- self.eqCntx[cntx] = uniqueCntxHashes[h]
141
- else:
142
- uniqueCntxHashes[h] = cntx
143
- del uniqueCntxHashes
144
- self.modelXbrl.profileActivity("... identify aspect equal contexts", minTimeToShow=1.0)
145
-
146
- # identify equal units
147
- uniqueUnitHashes = {}
148
- for unit in self.modelXbrl.units.values():
149
- h = unit.hash
150
- if h in uniqueUnitHashes:
151
- if unit.isEqualTo(uniqueUnitHashes[h]):
152
- self.eqUnit[unit] = uniqueUnitHashes[h]
153
- else:
154
- uniqueUnitHashes[h] = unit
155
- del uniqueUnitHashes
156
- self.modelXbrl.profileActivity("... identify equal units", minTimeToShow=1.0)
157
-
158
-
159
- sectObjs = sorted(set(rel.fromModelObject # only have numerics with context and unit
160
- for rel in modelXbrl.relationshipSet(sectionFact).modelRelationships
161
- if rel.fromModelObject is not None and rel.fromModelObject.concept is not None),
162
- key=lambda s: (s.concept.label(), s.objectIndex)) # sort into document order for consistent error messages
163
- if not sectObjs:
164
- self.modelXbrl.error("calc2e:noSections",
165
- "Instance contains no sections, nothing to validate.",
166
- modelObject=modelXbrl)
167
-
168
- # check by section
169
- factByConceptCntxUnit = OrderedDefaultDict(list) # sort into document order for consistent error messages
170
- self.sectionFacts = []
171
- for sectObj in sectObjs:
172
- #print ("section {}".format(sectObj.concept.label()))
173
- self.section = sectObj.concept.label()
174
- sectLinkRoles = tuple(sectObj.concept.get(calc2linkroles,"").split())
175
- factByConceptCntxUnit.clear()
176
- for f in sorted((rel.toModelObject # sort into document order for consistent error messages
177
- for rel in modelXbrl.relationshipSet(sectionFact,sectLinkRoles).fromModelObject(sectObj)
178
- if rel.toModelObject is not None and # numeric facts with context and unit
179
- rel.fromModelObject is not None and
180
- rel.toModelObject.concept is not None and
181
- rel.toModelObject.context is not None and
182
- rel.toModelObject.unit is not None),
183
- key=lambda f: f.objectIndex):
184
- factByConceptCntxUnit[f.qname, self.eqCntx.get(f.context,f.context), self.eqUnit.get(f.unit,f.unit)].append(f)
185
- for fList in factByConceptCntxUnit.values():
186
- f0 = fList[0]
187
- if len(fList) == 1:
188
- self.sectionFacts.append(f0)
189
- else:
190
- if any(f.isNil for f in fList):
191
- _inConsistent = not all(f.isNil for f in fList)
192
- if _inConsistent: # pick a nil fact for f0 for calc validation
193
- for f in fList:
194
- if f.isNil:
195
- f0 = f
196
- break
197
- elif all(inferredDecimals(f) == inferredDecimals(f0) for f in fList[1:]): # same decimals
198
- v0 = intervalValue(f0)
199
- _inConsistent = not all(intervalValue(f) == v0 for f in fList[1:])
200
- else: # not all have same decimals
201
- d0 = inferredDecimals(f0)
202
- aMax, bMin, _inclA, _inclB = intervalValue(f0, d0)
203
- for f in fList[1:]:
204
- df = inferredDecimals(f0)
205
- a, b, _inclA, _inclB = intervalValue(f, df)
206
- if a > aMax: aMax = a
207
- if b < bMin: bMin = b
208
- if df > d0: # take most accurate fact in section
209
- f0 = f
210
- d0 = df
211
- _inConsistent = (bMin < aMax)
212
- if _inConsistent:
213
- modelXbrl.error("calc2e:inconsistentDuplicateInSection",
214
- "Section %(section)s contained %(fact)s inconsistent in contexts equivalent to %(contextID)s: values %(values)s",
215
- modelObject=fList, section=sectObj.concept.label(), fact=f0.qname, contextID=f0.contextID, values=", ".join(strTruncate(f.value, 128) for f in fList))
216
- self.sectionFacts.append(f0)
217
- # sectionFacts now in document order and deduplicated
218
- #print("section {} facts {}".format(sectObj.concept.label(), ", ".join(str(f.qname)+"="+f.value for f in self.sectionFacts)))
219
-
220
- # depth-first calc tree
221
- sectCalc2RelSet = modelXbrl.relationshipSet(calc2Arcroles, sectLinkRoles)
222
-
223
- # indexers for section based on calc2 arcrole
224
- self.sumInit = False
225
- self.sumConceptBindKeys.clear()
226
- self.sumBoundFacts.clear()
227
- self.perInit = False
228
- self.perConceptBindKeys.clear()
229
- self.perBoundFacts.clear()
230
- self.durationPeriodStarts.clear()
231
- self.aggDimInit = set()
232
- self.aggConceptBindKeys.clear()
233
- self.aggBoundFacts.clear()
234
- self.aggBoundConceptFacts.clear()
235
- self.aggDimInit.clear()
236
-
237
- inferredValues = {}
238
- for rootConcept in sorted(sectCalc2RelSet.rootConcepts,
239
- key=lambda r: sectCalc2RelSet.fromModelObject(r)[0].order):
240
- self.sectTreeRel(rootConcept, 1, sectCalc2RelSet, inferredValues, {rootConcept, None})
241
-
242
- # recursive depth-first tree descender, returns sum
243
- def sectTreeRel(self, parentConcept, n, sectCalc2RelSet, inferredParentValues, visited, dimQN=None):
244
- childRels = sectCalc2RelSet.fromModelObject(parentConcept)
245
- if childRels:
246
- visited.add(parentConcept)
247
- inferredChildValues = {}
248
-
249
- # setup summation bind keys for child objects
250
- sumParentBindKeys = self.sumConceptBindKeys[parentConcept]
251
- boundSumKeys = set() # these are contributing fact keys, parent may be inferred
252
- boundSums = defaultdict(intervalZero)
253
- boundSummationItems = defaultdict(list)
254
- boundPerKeys = set()
255
- boundPers = defaultdict(intervalZero)
256
- boundDurationItems = defaultdict(list)
257
- boundAggKeys = set()
258
- boundAggs = defaultdict(intervalZero)
259
- boundAggItems = defaultdict(list)
260
- boundAggConcepts = defaultdict(set)
261
- for rel in childRels:
262
- childConcept = rel.toModelObject
263
- if childConcept not in visited:
264
- if rel.arcrole == summationItem:
265
- if not self.sumInit:
266
- self.sumBindFacts()
267
- boundSumKeys |= self.sumConceptBindKeys[childConcept]
268
- elif rel.arcrole == balanceChanges:
269
- if not self.perInit:
270
- self.perBindFacts()
271
- boundPerKeys |= self.perConceptBindKeys[childConcept] # these are only duration items
272
- elif rel.arcrole == domainMember:
273
- boundAggKeys |= self.aggConceptBindKeys[dimQN]
274
- domQN = parentConcept.qname
275
- elif rel.arcrole == aggregationDomain: # this is in visited
276
- dimQN = rel.arcElement.prefixedNameQname(rel.get("dimension"))
277
- if dimQN not in self.aggDimInit:
278
- self.aggBindFacts(dimQN) # bind each referenced dimension's contexts
279
-
280
- # depth-first descent calc tree and process item after descent
281
- for rel in childRels:
282
- childConcept = rel.toModelObject
283
- if childConcept not in visited:
284
- # depth-first descent
285
- self.sectTreeRel(childConcept, n+1, sectCalc2RelSet, inferredChildValues, visited, dimQN)
286
- # post-descent summation (allows use of inferred value)
287
- if rel.arcrole == summationItem:
288
- weight = rel.weightDecimal
289
- for sumKey in boundSumKeys:
290
- cntx, unit = sumKey
291
- factKey = (childConcept, cntx, unit)
292
- if factKey in self.sumBoundFacts:
293
- for f in self.sumBoundFacts[factKey]:
294
- addInterval(boundSums, sumKey, intervalValue(f), weight)
295
- boundSummationItems[sumKey].append(f)
296
- elif factKey in inferredChildValues:
297
- addInterval(boundSums, sumKey, inferredChildValues[factKey], weight)
298
- elif factKey in inferredParentValues:
299
- addInterval(boundSums, sumKey, inferredParentValues[factKey], weight)
300
- elif rel.arcrole == balanceChanges:
301
- weight = rel.weightDecimal
302
- for perKey in boundPerKeys:
303
- hCntx, unit, start, end = perKey
304
- factKey = (childConcept, hCntx, unit, start, end)
305
- if factKey in self.perBoundFacts:
306
- for f in self.perBoundFacts[factKey]:
307
- addInterval(boundPers, perKey, intervalValue(f), weight)
308
- boundDurationItems[perKey].append(f)
309
- elif factKey in inferredChildValues:
310
- addInterval(boundPers, perKey, inferredChildValues[factKey], weight)
311
- elif factKey in inferredParentValues:
312
- addInterval(boundPers, perKey, inferredParentValues[factKey], weight)
313
- elif rel.arcrole == domainMember:
314
- memQN = childConcept.qname
315
- for aggKey in boundAggKeys:
316
- hCntx, unit = aggKey
317
- dimMemKey = (hCntx, unit, dimQN, memQN)
318
- if dimMemKey in self.aggBoundFacts:
319
- for f in self.aggBoundFacts[dimMemKey]:
320
- a, b, _inclA, _inclB = intervalValue(f)
321
- factDomKey = (f.concept, hCntx, unit, dimQN, domQN)
322
- addInterval(boundAggs, factDomKey, intervalValue(f))
323
- boundAggItems[aggKey].append(f)
324
- boundAggConcepts[aggKey].add(f.concept)
325
- elif rel.arcrole == aggregationDomain: # this is in visited
326
- childRelSet = self.modelXbrl.relationshipSet(domainMember,rel.get("targetRole"))
327
- self.sectTreeRel(childConcept, n+1, childRelSet, inferredParentValues, {None}, dimQN) # infer global to section
328
-
329
- # process child items bound to this calc subtree
330
- for sumKey in boundSumKeys:
331
- cntx, unit = sumKey
332
- factKey = (parentConcept, cntx, unit)
333
- ia, ib = boundSums[sumKey]
334
- if factKey in self.sumBoundFacts:
335
- for f in self.sumBoundFacts[factKey]:
336
- d = inferredDecimals(f)
337
- sa, sb, _inclA, _inclB = intervalValue(f, d)
338
- if ((ia is NIL) ^ (sa is NIL)) or ((ia is not NIL) and (sb < ia or sa > ib)):
339
- self.modelXbrl.log('INCONSISTENCY', "calc2e:summationInconsistency",
340
- _("Summation inconsistent from %(concept)s in section %(section)s reported sum %(reportedSum)s, computed sum %(computedSum)s context %(contextID)s unit %(unitID)s unreported contributing items %(unreportedContributors)s"),
341
- modelObject=boundSummationItems[sumKey],
342
- concept=parentConcept.qname, section=self.section,
343
- reportedSum=self.formatInterval(sa, sb, d),
344
- computedSum=self.formatInterval(ia, ib, d),
345
- contextID=f.context.id, unitID=f.unit.id,
346
- unreportedContributors=", ".join(str(c.qname) # list the missing/unreported contributors in relationship order
347
- for r in childRels
348
- for c in (r.toModelObject,)
349
- if r.arcrole == summationItem and c is not None and
350
- (c, cntx, unit) not in self.sumBoundFacts)
351
- or "none")
352
- elif inferredParentValues is not None: # value was inferred, return to parent level
353
- inferredParentValues[factKey] = (ia, ib)
354
- for perKey in boundPerKeys:
355
- hCntx, unit, start, end = perKey
356
- ia, ib = boundPers[perKey]
357
- endBalA = endBalB = ZERO
358
- endFactKey = (parentConcept, hCntx, unit, None, end)
359
- if endFactKey in self.perBoundFacts:
360
- for f in self.perBoundFacts[endFactKey]:
361
- if f.isNil:
362
- endBalA = endBalB = NIL
363
- d = 0
364
- break
365
- d = inferredDecimals(f)
366
- a, b, _inclA, _inclB = intervalValue(f,d)
367
- endBalA += a
368
- endBalB += b
369
- foundStartingFact = (endBalA is NIL)
370
- while not foundStartingFact:
371
- startFactKey = (parentConcept, hCntx, unit, None, start)
372
- if startFactKey in self.perBoundFacts:
373
- for f in self.perBoundFacts[startFactKey]:
374
- if f.isNil:
375
- endBalA = endBalB = NIL
376
- foundStartingFact = True
377
- break
378
- a, b, _inclA, _inclB = intervalValue(f)
379
- endBalA -= a
380
- endBalB -= b
381
- foundStartingFact = True
382
- break
383
- if not foundStartingFact:
384
- # infer backing up one period
385
- _nomPer = nominalPeriod(end - start)
386
- foundEarlierAdjacentPeriodStart = False
387
- for _start in self.durationPeriodStarts.get(_nomPer, ()):
388
- if nominalPeriod(start - _start) == _nomPer: # it's preceding period
389
- end = start
390
- start = _start
391
- perKey = hCntx, unit, start, end
392
- if perKey in boundPerKeys:
393
- chngs = boundPers[perKey]
394
- ia += chngs[0]
395
- ib += chngs[1]
396
- foundEarlierAdjacentPeriodStart = True
397
- break
398
- if not foundEarlierAdjacentPeriodStart:
399
- break
400
-
401
- if ((ia is NIL) ^ (endBalA is NIL)) or ((ia is not NIL) and (endBalB < ia or endBalA > ib)):
402
- self.modelXbrl.log('INCONSISTENCY', "calc2e:balanceInconsistency",
403
- _("Balance inconsistent from %(concept)s in section %(section)s reported sum %(reportedSum)s, computed sum %(computedSum)s context %(contextID)s unit %(unitID)s unreported contributing items %(unreportedContributors)s"),
404
- modelObject=boundDurationItems[perKey],
405
- concept=parentConcept.qname, section=self.section,
406
- reportedSum=self.formatInterval(endBalA, endBalB, d),
407
- computedSum=self.formatInterval(ia, ib, d),
408
- contextID=f.context.id, unitID=f.unit.id,
409
- unreportedContributors=", ".join(str(c.qname) # list the missing/unreported contributors in relationship order
410
- for r in childRels
411
- for c in (r.toModelObject,)
412
- if r.arcrole == balanceChanges and c is not None and
413
- (c, hCntx, unit, start, end) not in self.perBoundFacts)
414
- or "none")
415
- for aggKey in boundAggKeys:
416
- hCntx, unit = aggKey
417
- for concept in sorted(boundAggConcepts[aggKey], key=lambda c:c.objectIndex): # repeatable errors
418
- factDomKey = (concept, hCntx, unit, dimQN, domQN)
419
- ia, ib = boundAggs[factDomKey]
420
- if factDomKey in self.aggBoundConceptFacts:
421
- for f in self.aggBoundConceptFacts[factDomKey]:
422
- d = inferredDecimals(f)
423
- sa, sb, _inclA, _inclB = intervalValue(f, d)
424
- if ((ia is NIL) ^ (sa is NIL)) or ((ia is not NIL) and (sb < ia or sa > ib)):
425
- self.modelXbrl.log('INCONSISTENCY', "calc2e:aggregationInconsistency",
426
- _("Aggregation inconsistent for %(concept)s, domain %(domain)s in section %(section)s reported sum %(reportedSum)s, computed sum %(computedSum)s context %(contextID)s unit %(unitID)s unreported contributing members %(unreportedContributors)s"),
427
- modelObject=boundAggItems[factDomKey],
428
- concept=concept.qname,
429
- domain=parentConcept.qname, section=self.section,
430
- reportedSum=self.formatInterval(sa, sb, d),
431
- computedSum=self.formatInterval(ia, ib, d),
432
- contextID=f.context.id, unitID=f.unit.id,
433
- unreportedContributors=", ".join(str(c.qname) # list the missing/unreported contributors in relationship order
434
- for r in childRels
435
- for c in (r.toModelObject,)
436
- if r.arcrole == domainMember and c is not None and
437
- (concept, hCntx, unit, dimQN, c.qname) not in self.aggBoundConceptFacts)
438
- or "none")
439
- elif inferredParentValues is not None: # value was inferred, return to parent level
440
- # allow to be retrieved by factDomKey
441
- inferredParentValues[factDomKey] = (ia, ib)
442
- if self.modelXbrl.qnameDimensionDefaults.get(dimQN) == domQN:
443
- cntxKey = (hCntx, dimQN, domQN)
444
- if cntxKey in self.eqCntx:
445
- cntx = self.eqCntx[cntxKey]
446
- else:
447
- cntx = self.aggTotalContext(hCntx, dimQN, domQN)
448
- self.eqCntx[cntxKey] = cntx
449
- if cntx is not None:
450
- # allow to be retrieved by fact line item context key
451
- self.eqCntx[(hCntx, dimQN, domQN)] = cntx
452
- inferredParentValues[(concept, cntx, unit)] = (ia, ib)
453
- visited.remove(parentConcept)
454
-
455
- def sumBindFacts(self):
456
- # bind facts in section for summation-item
457
- for f in self.sectionFacts:
458
- concept = f.concept
459
- if concept.isNumeric:
460
- cntx = self.eqCntx.get(f.context,f.context)
461
- unit = self.eqUnit.get(f.unit,f.unit)
462
- self.sumConceptBindKeys[concept].add( (cntx,unit) )
463
- self.sumBoundFacts[concept, cntx, unit].append(f)
464
- self.sumInit = True
465
-
466
- def perBindFacts(self):
467
- # bind facts in section for domain aggreggation
468
- for f in self.sectionFacts:
469
- concept = f.concept
470
- if concept.isNumeric:
471
- cntx = self.eqCntx.get(f.context,f.context)
472
- if not cntx.isForeverPeriod:
473
- hCntx = hash( (cntx.entityIdentifierHash, cntx.dimsHash) )
474
- unit = self.eqUnit.get(f.unit,f.unit)
475
- self.perConceptBindKeys[concept].add( (hCntx, unit, cntx.startDatetime, cntx.endDatetime) )
476
- self.perBoundFacts[concept, hCntx, unit, cntx.startDatetime, cntx.endDatetime].append(f)
477
- if cntx.isStartEndPeriod:
478
- self.durationPeriodStarts[nominalPeriod(cntx.endDatetime - cntx.startDatetime)].add(cntx.startDatetime)
479
- self.perInit = True
480
-
481
- def aggBindFacts(self, dimQN):
482
- # bind facts in section for domain aggreggation
483
- for f in self.sectionFacts:
484
- concept = f.concept
485
- if concept.isNumeric:
486
- cntx = self.eqCntx.get(f.context,f.context)
487
- hCntx = hash( (cntx.periodHash, cntx.entityIdentifierHash,
488
- hash(frozenset(dimObj
489
- for _dimQN, dimObj in cntx.qnameDims.items()
490
- if _dimQN != dimQN))) )
491
- unit = self.eqUnit.get(f.unit,f.unit)
492
- memQN = cntx.dimMemberQname(dimQN, includeDefaults=True)
493
- if memQN is not None:
494
- self.aggConceptBindKeys[dimQN].add( (hCntx,unit) )
495
- self.aggBoundFacts[hCntx, unit, dimQN, memQN].append(f)
496
- self.aggBoundConceptFacts[f.concept, hCntx, unit, dimQN, memQN].append(f)
497
- self.aggDimInit.add(dimQN)
498
-
499
- def aggTotalContext(self, hCntx, dimQN, domQN):
500
- # find a context for aggregation total usable in line item and balance roll ups
501
- for cntx in self.modelXbrl.contexts.values():
502
- hCntx2 = hash( (cntx.periodHash, cntx.entityIdentifierHash,
503
- hash(frozenset(dimObj
504
- for _dimQN, dimObj in cntx.qnameDims.items()
505
- if _dimQN != dimQN))) )
506
- if hCntx == hCntx2 and cntx.dimMemberQname(dimQN, True) == domQN:
507
- return cntx
508
- return None
509
-
510
- def formatInterval(self, a, b, dec):
511
- if a is NIL:
512
- return "(nil)"
513
- if isnan(dec) or isinf(dec): dec = 4
514
- if a == b: # not an interval
515
- return Locale.format_decimal(self.modelXbrl.locale, a, 1, max(dec,0))
516
- return "[{}, {}]".format( # show as an interval
517
- Locale.format_decimal(self.modelXbrl.locale, a, 1, max(dec,0)),
518
- Locale.format_decimal(self.modelXbrl.locale, b, 1, max(dec,0)))
519
-
520
-
521
- def checkCalc2(val, *args, **kwargs):
522
- ValidateXbrlCalc2(val).validate()
523
-
524
-
525
-
526
- __pluginInfo__ = {
527
- # Do not use _( ) in pluginInfo itself (it is applied later, after loading
528
- 'name': 'Calc2',
529
- 'version': '0.9',
530
- 'description': '''Calculation 2.0 Validation.''',
531
- 'license': 'Apache-2',
532
- 'author': authorLabel,
533
- 'copyright': copyrightLabel,
534
- # classes of mount points (required)
535
- 'Validate.XBRL.Finally': checkCalc2
536
- }