Skip to content

Commit

Permalink
Split reports data into sourceReports/targetReports.
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Warren committed Oct 4, 2023
1 parent e82713e commit 86f50e3
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 138 deletions.
20 changes: 14 additions & 6 deletions iXBRLViewerPlugin/iXBRLViewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def __init__(self, reports: List[ModelXbrl], basenameSuffix: str = ''):
self.roleMap = NamespaceMap()
self.reports = reports
self.taxonomyData = {
"reports": [],
"sourceReports": [],
"features": [],
"languages": {},
}
Expand Down Expand Up @@ -145,9 +145,13 @@ def addLanguage(self, langCode):
if langCode not in self.taxonomyData["languages"]:
self.taxonomyData["languages"][langCode] = self.makeLanguageName(langCode)

@property
def currentSourceReportData(self):
return self.taxonomyData["sourceReports"][-1]

@property
def currentReportData(self):
return self.taxonomyData["reports"][-1]
return self.currentSourceReportData["targetReports"][-1]

def addELR(self, report: ModelXbrl, elr):
prefix = self.roleMap.getPrefix(elr)
Expand Down Expand Up @@ -379,9 +383,13 @@ def getStubDocument(self):
return etree.parse(fin)

def addReport(self):
self.taxonomyData["reports"].append({
"concepts": {},
"facts": {},
self.taxonomyData["sourceReports"].append({
"targetReports": [
{
"concepts": {},
"facts": {},
}
]
})

def createViewer(self, scriptUrl: str = DEFAULT_VIEWER_PATH, useStubViewer: bool = False, showValidations: bool = True, packageDownloadURL: str = None) -> Optional[iXBRLViewer]:
Expand Down Expand Up @@ -479,7 +487,7 @@ def createViewer(self, scriptUrl: str = DEFAULT_VIEWER_PATH, useStubViewer: bool
}

if docSetFiles is not None:
self.currentReportData["docSetFiles"] = list(urllib.parse.quote(f) for f in docSetFiles)
self.currentSourceReportData["docSetFiles"] = list(urllib.parse.quote(f) for f in docSetFiles)

self.taxonomyData["prefixes"] = self.nsmap.prefixmap
self.taxonomyData["roles"] = self.roleMap.prefixmap
Expand Down
61 changes: 32 additions & 29 deletions iXBRLViewerPlugin/viewer/src/js/reportset.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,36 @@ export class ReportSet {
fnorder.sort((a,b) => this._ixNodeMap[a].docOrderindex - this._ixNodeMap[b].docOrderindex);

// Create Fact objects for all facts.
for (const [reportIndex, reportData] of this.reportsData().entries()) {
const report = new XBRLReport(this, reportData);
this.reports.push(report);
for (const [id, factData] of Object.entries(reportData.facts)) {
const vuid = viewerUniqueId(reportIndex, id);
this._items[vuid] = new Fact(report, vuid, factData);
}
for (const [reportIndex, sourceReport] of (this._data.sourceReports ?? [ { "targetReports": [ this._data ] } ]).entries()) {
for (const reportData of sourceReport.targetReports) {
const report = new XBRLReport(this, reportData);
this.reports.push(report);
for (const [id, factData] of Object.entries(reportData.facts)) {
const vuid = viewerUniqueId(reportIndex, id);
this._items[vuid] = new Fact(report, vuid, factData);
}

// Now resolve footnote references, creating footnote objects for "normal"
// footnotes, and finding Fact objects for fact->fact footnotes.
//
// Associate source facts with target footnote/facts to allow two way
// navigation.
for (const [id, factData] of Object.entries(reportData.facts)) {
const vuid = viewerUniqueId(reportIndex, id);
const fact = this._items[vuid];
const fns = factData.fn || [];
fns.forEach((fnid) => {
const fnvuid = viewerUniqueId(reportIndex, fnid);
var fn = this._items[fnvuid];
if (fn === undefined) {
fn = new Footnote(fact.report, fnvuid, "Footnote " + (fnorder.indexOf(fnvuid) + 1));
this._items[fnvuid] = fn;
}
// Associate fact with footnote
fn.addLinkedFact(fact);
fact.addFootnote(fn);
});
// Now resolve footnote references, creating footnote objects for "normal"
// footnotes, and finding Fact objects for fact->fact footnotes.
//
// Associate source facts with target footnote/facts to allow two way
// navigation.
for (const [id, factData] of Object.entries(reportData.facts)) {
const vuid = viewerUniqueId(reportIndex, id);
const fact = this._items[vuid];
const fns = factData.fn || [];
fns.forEach((fnid) => {
const fnvuid = viewerUniqueId(reportIndex, fnid);
var fn = this._items[fnvuid];
if (fn === undefined) {
fn = new Footnote(fact.report, fnvuid, "Footnote " + (fnorder.indexOf(fnvuid) + 1));
this._items[fnvuid] = fn;
}
// Associate fact with footnote
fn.addLinkedFact(fact);
fact.addFootnote(fn);
});
}
}
}
}
Expand Down Expand Up @@ -181,7 +183,7 @@ export class ReportSet {
}

reportsData() {
return this._data.reports ?? [ this._data ];
return this._data.sourceReports?.flatMap(sr => sr.targetReports) ?? [ this._data ];
}

/**
Expand All @@ -193,7 +195,8 @@ export class ReportSet {
* @return {List} A list of objects describing each file
*/
reportFiles() {
return this.reportsData().map((x, n) => (x.docSetFiles ?? []).map(file => ({ index: n, file: file }))).flat();
const sourceReports = this._data.sourceReports ?? [ this._data ];
return sourceReports.map((x, n) => (x.docSetFiles ?? []).map(file => ({ index: n, file: file }))).flat();
}

/**
Expand Down
144 changes: 76 additions & 68 deletions iXBRLViewerPlugin/viewer/src/js/reportset.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,83 +36,91 @@ function multiReportTestData(withAnchoring) {
"role3": "https://www.example.com/role3",
"role4": "https://www.example.com/role4"
},
"reports": [
{
"roleDefs": {
"role1": { "en": "Role 1 Label" },
"role2": { "en": null },
"role3": {}
},
"concepts": {
"eg:Concept1": {
"labels": {
"std": {
"en": "English label"
}
}
},
"eg:Concept2": {
"labels": {
"std": {
"en": "English label for concept two",
"en-us": "English (US) label for concept two",
"sourceReports": [
{
"targetReports": [
{
"roleDefs": {
"role1": { "en": "Role 1 Label" },
"role2": { "en": null },
"role3": {}
},
"concepts": {
"eg:Concept1": {
"labels": {
"std": {
"en": "English label"
}
}
},
"eg:Concept2": {
"labels": {
"std": {
"en": "English label for concept two",
"en-us": "English (US) label for concept two",
}
}
},
"eg:Concept3": {
"labels": {
"std": {
"en": "Concept three"
}
}
}
},

"facts": {
...createNumericFact("f1", "eg:Concept1", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
...createNumericFact("f2", "eg:Concept2", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
}
},
"eg:Concept3": {
"labels": {
"std": {
"en": "Concept three"
}
}
}
},

"facts": {
...createNumericFact("f1", "eg:Concept1", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
...createNumericFact("f2", "eg:Concept2", "iso2417:USD", "2018-01-01/2019-01-01", 1000, -3),
}
]
},
{
"roleDefs": {
},
"concepts": {
"eg:Concept1": {
"labels": {
"std": {
"en": "English label"
"targetReports": [
{
"roleDefs": {
},
"concepts": {
"eg:Concept1": {
"labels": {
"std": {
"en": "English label"
}
}
},
"eg:Concept2": {
"labels": {
"std": {
"en": "Report 2 English label for concept two",
}
}
},
"eg:Concept3": {
"labels": {
"std": {
"en-gb": "Concept three"
}
}
}
}
},
"eg:Concept2": {
"labels": {
"std": {
"en": "Report 2 English label for concept two",
}
}
},
"eg:Concept3": {
"labels": {
"std": {
"en-gb": "Concept three"
}
}
}
},
},

"facts": {
...createNumericFact("f1", "eg:Concept1", "iso2417:USD", "2018-01-01/2019-01-01", 2000, -3),
},
"facts": {
...createNumericFact("f1", "eg:Concept1", "iso2417:USD", "2018-01-01/2019-01-01", 2000, -3),
},

"rels": {
...anchoringRelationShips(withAnchoring)
}
"rels": {
...anchoringRelationShips(withAnchoring)
}
}
]
}
]
};
}

// Legacy report data format - no "reports" array
// Legacy report data format - no "sourceReports" array
function singleReportTestData() {
return {
"features": [],
Expand Down Expand Up @@ -254,16 +262,16 @@ describe("Multi report - anchoring", () => {
describe("Multi report - doc set files", () => {
test("Not overlapping", () => {
const data = multiReportTestData(false);
data.reports[0].docSetFiles = ["a.html", "b.html"];
data.reports[1].docSetFiles = ["c.html", "d.html"];
data.sourceReports[0].docSetFiles = ["a.html", "b.html"];
data.sourceReports[1].docSetFiles = ["c.html", "d.html"];
const testReportSet = new ReportSet(data);
testReportSet._initialize();
expect(testReportSet.reportFiles()).toStrictEqual([{"index": 0, "file": "a.html"}, {"index": 0, "file": "b.html"}, {"index": 1, "file": "c.html"}, {"index": 1, "file": "d.html"}]);
});
test("Repeated document set", () => {
const data = multiReportTestData(false);
data.reports[0].docSetFiles = ["a.html", "b.html"];
data.reports[1].docSetFiles = ["a.html", "b.html"];
data.sourceReports[0].docSetFiles = ["a.html", "b.html"];
data.sourceReports[1].docSetFiles = ["a.html", "b.html"];
const testReportSet = new ReportSet(data);
testReportSet._initialize();
expect(testReportSet.reportFiles()).toStrictEqual([{"index": 0, "file": "a.html"}, {"index": 0, "file": "b.html"}, {"index": 1, "file": "a.html"}, {"index": 1, "file": "b.html"}]);
Expand Down
6 changes: 3 additions & 3 deletions iXBRLViewerPlugin/viewer/src/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,13 @@ export function isTransparent(rgba) {
* in order to generate an ID that is guaranteed to be unique within the
* viewer. This is already within an iXBRL Document Set, but not across
* separate iXBRL documents or document sets.
* @param {Integer} reportIndex Index of the report document containing the fact
* @param {Integer} sourceReportIndex - index of the report document containing the fact
* @return {String} a viewer unique ID
*/

export function viewerUniqueId(reportIndex, id) {
export function viewerUniqueId(sourceReportIndex, id) {
if (id === null) {
return null;
}
return reportIndex.toString() + "-" + id;
return sourceReportIndex.toString() + "-" + id;
}
Loading

0 comments on commit 86f50e3

Please sign in to comment.