Skip to content

Commit d7fd22a

Browse files
committed
Explore using a Range class instead of an object
Currently we pass around an object to act as our range, but this puts the onus on the caller to validate and also ensure that iterating over the range is accurate - this new Range class aims to encapsulate that functionality in one place
1 parent 3d27c9e commit d7fd22a

File tree

11 files changed

+405
-232
lines changed

11 files changed

+405
-232
lines changed

lib/importer/src/dudk/backend.js

Lines changed: 32 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const attributeTypes = require('./types/attribute-types');
55
const guesser = require('./types/guesser');
66
const store = require("./session_store")
77
const errorTypes = require('./util/errors');
8+
const Range = require('./util/range');
89

910
// An implementation of the interface described in https://struct.register-dynamics.co.uk/trac/wiki/DataImporter/API
1011

@@ -78,13 +79,12 @@ exports.SessionGetSheets = (sid) => {
7879
exports.SessionSetHeaderRange = (sid, range) => {
7980
assert(range != null, "Null range passed to SessionSetHeaderRange");
8081
assert(range.sheet != null, "Range with null sheet passed to SessionSetHeaderRange");
81-
8282
sessionStore.apply(sid, (s) => { s.headerRanges[range.sheet] = range; })
8383
};
8484

8585
exports.SessionGetHeaderRange = (sid, sheetName) => {
8686
let session = sessionStore.get(sid);
87-
return session.headerRanges[sheetName]
87+
return session.headerRanges[sheetName];
8888
};
8989

9090
exports.SessionSetFooterRange = (sid, range) => {
@@ -95,7 +95,7 @@ exports.SessionSetFooterRange = (sid, range) => {
9595

9696
exports.SessionGetFooterRange = (sid, sheetName) => {
9797
let session = sessionStore.get(sid);
98-
return session.footerRanges[sheetName]
98+
return session.footerRanges[sheetName];
9999
};
100100

101101
function getDimensions(sid) {
@@ -237,46 +237,28 @@ exports.SessionSuggestDataRange = (sid, headerRange, footerRange) => {
237237
// the header.
238238

239239
// Header and footer, so just go from the row below the header to the row above the footer
240-
return {
241-
sheet: sheetName,
242-
start: {
243-
row: headerRange.start.row + 1,
244-
column: headerRange.start.column
245-
},
246-
end: {
247-
row: footerRange.start.row,
248-
column: headerRange.end.column
249-
}
250-
};
240+
return new Range(
241+
sheetName,
242+
{ row: headerRange.start.row + 1, column: headerRange.start.column },
243+
{ row: footerRange.start.row, column: headerRange.end.column }
244+
);
251245
} else {
252246
// Header, but no footer, so just go from the row below the header to the end
253-
return {
254-
sheet: sheetName,
255-
start: {
256-
row: headerRange.start.row + 1,
257-
column: headerRange.start.column
258-
},
259-
end: {
260-
row: sheetDimensions.rows - 1,
261-
column: headerRange.end.column
262-
}
263-
};
247+
return new Range(
248+
sheetName,
249+
{ row: headerRange.start.row + 1, column: headerRange.start.column },
250+
{ row: sheetDimensions.rows - 1, column: headerRange.end.column }
251+
);
264252
}
265253
} else {
266254
// No header range specified, so take all but the first row of the first sheet
267255
const sheetName = sessionStore.get(sid).sheetNames[0]; // Find the first sheet
268256
const sheetDimensions = dimensions.sheetDimensions.get(sheetName);
269-
return {
270-
sheet: sheetName,
271-
start: {
272-
row: 1,
273-
column: 0
274-
},
275-
end: {
276-
row: sheetDimensions.rows - 1,
277-
column: sheetDimensions.columns - 1
278-
}
279-
};
257+
return new Range(
258+
sheetName,
259+
{ row: 1, column: 0 },
260+
{ row: sheetDimensions.rows - 1, column: sheetDimensions.columns - 1 }
261+
);
280262
}
281263
}
282264

@@ -375,7 +357,8 @@ function cellsToSamples(row) {
375357
return row.map(cellToSample);
376358
}
377359

378-
// Returns a sample of rows in a range. range is of the form {sheet: 'Foo', start:{row: X, column: Y}, end:{row: X, column: Y}}.
360+
// Returns a sample of rows in a range. range is and instance of the Range class and
361+
// is of the form {sheet: 'Foo', start:{row: X, column: Y}, end:{row: X, column: Y}}.
379362

380363
// Returns three arrays - one with startCount rows from the top of the range,
381364
// one with a random selectino of middleCount rows from the middle, and another
@@ -385,15 +368,15 @@ function cellsToSamples(row) {
385368
exports.SessionGetInputSampleRows = (sid, range, startCount, middleCount, endCount) => {
386369
assert(range != null, "Null range passed to SessionGetInputSampleRows");
387370
assert(sessionStore.has(sid), `No such session ${sid} when getting input sample rows`);
388-
assert(sessionStore.get(sid).wb.Sheets[range.sheet], `No such sheet ${range.sheet} in session ${sid} when getting input sample rows`);
371+
assert(sessionStore.get(sid).wb.Sheets[range.sheet], `No such sheet ${range.sheet} in session ${sid} when getting input values`);
372+
389373
if (range.end.row < range.start.row || range.end.column < range.start.column) {
390374
// Range is empty
391375
return [[], [], []];
392376
}
393377
// If the caller asked for more rows than we have, or if the start/middle/end would overlap, clamp the counts
394378
const totalRowsInRange = (range.end.row - range.start.row + 1);
395379
[startCount, middleCount, endCount] = clampCounts(startCount, middleCount, endCount, totalRowsInRange);
396-
397380
const sheet = sessionStore.get(sid).wb.Sheets[range.sheet];
398381
const data = sheet["!data"];
399382
const merges = sheet["!merges"];
@@ -418,7 +401,6 @@ exports.SessionGetInputSampleRows = (sid, range, startCount, middleCount, endCou
418401
let endRows = extractColsFromRows(getMergedRows(sheet, range.end.row - endCount + 1, range.end.row),
419402
range.start.column, range.end.column + 1);
420403

421-
422404
// Given an index generate a function that we can use to map across an array to turn it into
423405
// an object with both index and the array data.
424406
const mapWithIndex = (arr, startIndex) => {
@@ -430,12 +412,6 @@ exports.SessionGetInputSampleRows = (sid, range, startCount, middleCount, endCou
430412
}
431413
}
432414

433-
// FIXME: Work out how the xlsx library represents styles and
434-
// rowspan/colspan and make sure that what we return does something useful
435-
// with that information. We DO want styling information to be available in
436-
// the preview, so that users can see their spreadsheet in a more familiar
437-
// form, and because styling information might be a significant part of the
438-
// data.
439415
return [
440416
startRows.map(mapWithIndex(startRows, range.start.row)),
441417
middleRows.map(mapWithIndex(middleRows, middleStart)),
@@ -532,8 +508,8 @@ exports.SessionGuessTypes = (sid, range) => {
532508
// objects with string fields "displayName" and "description", and an optional Map
533509
// field "formats" that maps from format names (as returned by SessionGetTypes)
534510
// to objects with strings field "displayName" and "description".
535-
exports.SessionGetSupportedTypes = (sid) => {
536-
return types.supportedTypes;
511+
exports.SessionGetSupportedTypes = (_sid) => {
512+
return attributeTypes.supportedTypes;
537513
};
538514

539515
// Given guesses as returned by SessionGuessTypes and a list of domain-model
@@ -573,8 +549,7 @@ exports.SessionSuggestFields = (sid, typeGuesses, domainModelFields) => {
573549
exports.SessionGetInputValues = (sid, range, maxValues) => {
574550
assert(sessionStore.get(sid), `No such session ${sid} when getting input values`);
575551
assert(sessionStore.get(sid).wb.Sheets[range.sheet], `No such sheet ${range.sheet} in session ${sid} when getting input values`);
576-
assert(range.end.row >= range.start.row, `End row (${range.end.row}) must be >= start row (${range.start.row}) to get input values`);
577-
assert(range.end.column >= range.start.column, `End column (${range.end.column}) must be >= start column (${range.start.column}) to get input values`);
552+
assert(range.isValid(), `Range is invalid: end (${JSON.stringify(range.end)}) must be >= start (${JSON.stringify(range.start)}) to get input values`);
578553

579554
const sheet = sessionStore.get(sid).wb.Sheets[range.sheet];
580555
const data = sheet["!data"];
@@ -679,7 +654,7 @@ exports.SessionPerformMappingJob = (sid, range, mapping, includeErrorRow = false
679654
validateMapping(range, mapping);
680655

681656
const records = new Array(); // Array of output records
682-
const errors = new Array();;
657+
const errors = new Array();
683658
const warnings = new Array();
684659

685660
const sheet = sessionStore.get(sid).wb.Sheets[range.sheet];
@@ -688,15 +663,14 @@ exports.SessionPerformMappingJob = (sid, range, mapping, includeErrorRow = false
688663
const attrMap = Object.entries(mapping.attributeMappings);
689664
const attrTypes = mapping.attributeTypes || {};
690665

691-
692-
// TODO: Replace this with a map/sum once range is a real type.
666+
// Use Range.applyRows to determine expectedColumnCount
693667
let expectedColumnCount = 0;
694-
for (let rowIdx = range.start.row; rowIdx <= range.end.row; rowIdx++) {
668+
range.applyRows((rowIdx) => {
695669
let row = getMergedRow(data, merges, rowIdx);
696670
if (row && row.length > expectedColumnCount) expectedColumnCount = row.length;
697-
}
671+
});
698672

699-
for (let rowIdx = range.start.row; rowIdx <= range.end.row; rowIdx++) {
673+
range.applyRows((rowIdx) => {
700674
let rowErrorCount = 0;
701675
let row = getMergedRow(data, merges, rowIdx);
702676
let recordCells = {};
@@ -706,7 +680,7 @@ exports.SessionPerformMappingJob = (sid, range, mapping, includeErrorRow = false
706680
// Ensure we exclude footer rows from the validation as they may not
707681
// have the same number of columns as the header row.
708682
if (rowIdx == range.end.row && footerRange) {
709-
continue;
683+
return;
710684
}
711685

712686
if (row.length < expectedColumnCount) {
@@ -777,7 +751,7 @@ exports.SessionPerformMappingJob = (sid, range, mapping, includeErrorRow = false
777751
} else {
778752
warnings.push({ row: rowIdx, type: errorTypes.ValidationError.EmptyRow({}) })
779753
}
780-
}
754+
});
781755

782756
let jid = makeAccessToken("j");
783757
let job = new JobResult(records, errors, warnings);

0 commit comments

Comments
 (0)