Skip to content

Commit

Permalink
Merge pull request #14 from ideaconsult/fix-formula-spread
Browse files Browse the repository at this point in the history
Fix formula spread
XlsxPopulateAccess refactoringof `copySize()` and `copyStyle()`
  • Loading branch information
thejonan authored Apr 10, 2020
2 parents 0b826b7 + 4b9db3a commit 2c15426
Show file tree
Hide file tree
Showing 15 changed files with 63 additions and 77 deletions.
14 changes: 0 additions & 14 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ but can be used as a reference for implementing custom spreadsheet accessors.
* [.rangeRef(range, withSheet)](#XlsxPopulateAccess+rangeRef) ⇒ <code>String</code>
* [.forAllCells(cb)](#XlsxPopulateAccess+forAllCells)[<code>XlsxPopulateAccess</code>](#XlsxPopulateAccess)
* [.copyStyle(dest, src)](#XlsxPopulateAccess+copyStyle)[<code>XlsxPopulateAccess</code>](#XlsxPopulateAccess)
* [.copySize(dest, src)](#XlsxPopulateAccess+copySize)[<code>XlsxPopulateAccess</code>](#XlsxPopulateAccess)

<a name="new_XlsxPopulateAccess_new"></a>

Expand Down Expand Up @@ -348,16 +347,3 @@ Copies the styles from `src` cell to the `dest`-ination one.
| dest | <code>Cell</code> | Destination cell. |
| src | <code>Cell</code> | Source cell. |

<a name="XlsxPopulateAccess+copySize"></a>

### xlsxPopulateAccess.copySize(dest, src) ⇒ [<code>XlsxPopulateAccess</code>](#XlsxPopulateAccess)
Resize the column and row of the destination cell, if not changed already.

**Kind**: instance method of [<code>XlsxPopulateAccess</code>](#XlsxPopulateAccess)
**Returns**: [<code>XlsxPopulateAccess</code>](#XlsxPopulateAccess) - For invocation chaining.

| Param | Type | Description |
| --- | --- | --- |
| dest | <code>Cell</code> | The destination cell which row and column to resize. |
| src | <code>Cell</code> | The source (template) cell to take the size from. |

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ As formulas are a key Excel feature, so `xlsx-datafill` is trying to keep them a

So, the [template format](#template-format) has slightly different version:

* `iterators` determine how the formula will be populated, and is one of the following values: `all`, `rows`, `cols` or `none`. The latter can be replaced with an empty value.
* `iterators` determine how the formula will be populated, and is one of the following values: `both`, `rows`, `cols` or `none`. The latter can be replaced with an empty value.
* `extractor` is the actual formula and **must** start with `=` so the engine recognizes it as such.
* `reference` should be present, otherwise an error will be issued. If a non-referenced formula is needed - just don’t use the template format.

Expand All @@ -217,7 +217,7 @@ Two operations are performed during data population of the referenced template -
* When `none` is selected, the formula is not populated - The ranges inside it are expanded to match the size of the referenced data block.
* When `cols` is selected, the formula is populated across the columns of referenced data block, while each range inside the formula is expanded across the rows of the data block.
* When `rows` is selected, the population and expanding processes are reversed - the formula is populated across rows, and the ranges inside are expanded across columns.
* Finally, when `all` is selected - the formula is not expanded - it is just populated across the same area of cells, as the referenced data block.
* Finally, when `both` is selected - the formula is not expanded - it is just populated across the same area of cells, as the referenced data block.

It is important to note, that the formula population starts from the cell with the _formula_ template, or one with the same offset, as the current data block - review the [nested blocks](#nested-blocks) concept for more information.

Expand Down Expand Up @@ -253,7 +253,7 @@ If the _formula template_ was this one: `{{ B1 | cols | =SUM(B1:B1) }}`, then th

The formula was populated across the columns, with each one being expanded across rows, i.e. the formula in `C4` will be `=SUM(C1:C3)`.

Specifying `all` as _iterators_ keyword will result in the same size (3x5 in this example) table. It _does_ make sense if it includes anchored references, i.e a template like this `{{ B1 | all | =B1 * $A$5 }}` will result in a 3x5 table, starting from `B4`, with each value from `B1:F3` being multiplied by the value in `A5`. For example the formula in `C4` would be `C1 * $A$5`.
Specifying `both` as _iterators_ keyword will result in the same size (3x5 in this example) table. It _does_ make sense if it includes anchored references, i.e a template like this `{{ B1 | both | =B1 * $A$5 }}` will result in a 3x5 table, starting from `B4`, with each value from `B1:F3` being multiplied by the value in `A5`. For example the formula in `C4` would be `C1 * $A$5`.

## Some important notes

Expand Down
45 changes: 20 additions & 25 deletions browser/xlsx-datafill.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion browser/xlsx-datafill.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion browser/xlsx-datafill.min.js.map

Large diffs are not rendered by default.

Binary file modified examples/docs-output.xlsx
Binary file not shown.
Binary file modified examples/formula-output.xlsx
Binary file not shown.
Binary file modified examples/formula-template.xlsx
Binary file not shown.
Binary file modified examples/publishers-output-unmerged.xlsx
Binary file not shown.
Binary file modified examples/publishers-output.xlsx
Binary file not shown.
Binary file modified examples/stock-output.xlsx
Binary file not shown.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "xlsx-datafill",
"version": "0.10.0",
"version": "0.10.1",
"description": "Scalable, template based data population for Excel XLSX spreadsheets.",
"keywords": [
"excel",
Expand Down
29 changes: 16 additions & 13 deletions src/XlsxDataFill.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ class XlsxDataFill {
if (template.reference) {
const refFill = dataFills[template.reference];

if (!refFill) throw new Error(`Unable to find a reference '${template.reference}'!`);
if (!refFill)
throw new Error(`Unable to find a reference '${template.reference}'!`);

if (template.formula)
refFill.formulas.push(aFill);
Expand Down Expand Up @@ -182,13 +183,16 @@ class XlsxDataFill {

const parts = reMatch[1].split(this._opts.fieldSplitter).map(_.trim),
styles = !parts[4] ? null : parts[4].split(","),
extractor = parts[2] || "";
extractor = parts[2] || "",
cellRef = this._access.buildRef(cell, parts[0]);

if (parts.length < 2)
throw new Error(`Not enough components of the template '${reMatch[0]}'`);
if (!!parts[0] && !cellRef)
throw new Error(`Invalid reference passed: '${parts[0]}'`);

return {
reference: this._access.buildRef(cell, parts[0]),
reference: cellRef,
iterators: parts[1].split(/x|\*/).map(_.trim),
extractor: extractor,
formula: extractor.startsWith("="),
Expand Down Expand Up @@ -221,9 +225,7 @@ class XlsxDataFill {
});

return allTemplates
.sort((a, b) => a.reference == this._access.cellRef(b.cell) > -1
? 1
: b.reference == this._access.cellRef(a.cell) > -1 ? -1 : 0)
.sort((a, b) => b.reference == this._access.cellRef(a.cell) || !a.reference ? -1 : 1)
.forEach(cb);
}

Expand Down Expand Up @@ -306,15 +308,17 @@ class XlsxDataFill {
* @ignore
*/
putValues(cell, data, template) {
if (!cell) throw new Error("Crash! Null reference cell in 'putValues()'!");

let entrySize = data.sizes,
value = this.extractValues(data, template.extractor, cell);


// make sure, the
if (!entrySize || !entrySize.length) {
this._access
.cellValue(cell, value)
.copyStyle(cell, template.cell)
.copySize(cell, template.cell);
.copyStyle(cell, template.cell);
this.applyDataStyle(cell, data, template);
entrySize = template.cellSize;
} else if (entrySize.length <= 2) {
Expand All @@ -332,8 +336,7 @@ class XlsxDataFill {
this._access.getCellRange(cell, entrySize[0] - 1, entrySize[1] - 1).forEach((cell, ri, ci) => {
this._access
.cellValue(cell, value[ri][ci])
.copyStyle(cell, template.cell)
.copySize(cell, template.cell);
.copyStyle(cell, template.cell);
this.applyDataStyle(cell, data[ri][ci], template);
});
} else {
Expand Down Expand Up @@ -397,7 +400,7 @@ class XlsxDataFill {
|| colOffset > 1 && this._opts.mergeCells === 'horizontal')
this._access.rangeMerged(rng, true);

rng.forEach(cell => this._access.copySize(cell, template.cell));
rng.forEach(cell => this._access.copyStyle(cell, template.cell));
}

// Finally, calculate the next cell.
Expand Down Expand Up @@ -431,7 +434,7 @@ class XlsxDataFill {
let from = this._access.getCell(match[3], match[2]),
newRef = null;

if (offset[0] > 0 && offset[1] > 0)
if (offset[0] > 0 || offset[1] > 0)
from = this._access.offsetCell(from, offset[0], offset[1]);

newRef = !match[5]
Expand Down Expand Up @@ -468,7 +471,7 @@ class XlsxDataFill {
aFill.processed = true;
this._access.cellValue(cell, null);

if (entrySize[0] < 2 && entrySize[1] < 2 || iter === 'all') {
if (entrySize[0] < 2 && entrySize[1] < 2 || iter === 'both') {
formula = this.shiftFormula(formula, offset, [0, 0]);
rng = this._access.getCellRange(cell, entrySize[0] - 1, entrySize[1] - 1);
} else if (iter === 'cols') {
Expand Down
24 changes: 8 additions & 16 deletions src/XlsxPopulateAccess.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,31 +293,23 @@ class XlsxPopulateAccess {
* @returns {XlsxPopulateAccess} For invocation chaining.
*/
copyStyle(dest, src) {
if (!src || !dest) throw new Error("Crash! Null 'src' or 'dest' for copyStyle()!");
if (src == dest) return this;

if (src._style !== undefined)
dest.style(src._style);
else if (src._styleId > 0)
dest._styleId = src._styleId;

return this;
}

/**
* Resize the column and row of the destination cell, if not changed already.
* @param {Cell} dest The destination cell which row and column to resize.
* @param {Cell} src The source (template) cell to take the size from.
* @returns {XlsxPopulateAccess} For invocation chaining.
*/
copySize(dest, src) {
const row = dest.rowNumber(),
col = dest.columnNumber();
const destSheetId = dest.sheet().name(),
rowId = `'${destSheetId}':${dest.rowNumber()}`,
colId = `'${destSheetId}':${dest.columnNumber()}`;

if (this._rowSizes[row] === undefined)
dest.row().height(this._rowSizes[row] = src.row().height());
if (this._rowSizes[rowId] === undefined)
dest.row().height(this._rowSizes[rowId] = src.row().height());

if (this._colSizes[col] === undefined)
dest.column().width(this._colSizes[col] = src.column().width());
if (this._colSizes[colId] === undefined)
dest.column().width(this._colSizes[colId] = src.column().width());

return this;
}
Expand Down
16 changes: 13 additions & 3 deletions test/DataFill.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,23 +121,33 @@ describe("XlsxDataFill: ", () => {
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("B8", "Ref"))).toBe('SUM(B2:F4)'); // 345
});

it("expanded and copied the formula properly", () => {
it("expanded and spread the formula properly", () => {
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("B7", "Ref"))).toBe('SUM(B2:B4)'); // 63;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("C7", "Ref"))).toBe('SHARED'); // SUM(C2:C4) == 66;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("F7", "Ref"))).toBe('SHARED'); // SUM(F2:F4) == 75;
});

it("expanded & copied the formula properly with external multiplication", () => {
it("expanded & spread the formula properly with external multiplication", () => {
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("B9", "Ref"))).toBe('SUM(B2:B4) * $A$6'); // 126;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("C9", "Ref"))).toBe('SHARED'); // SUM(C2:C4) == 132;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("F9", "Ref"))).toBe('SHARED'); // SUM(F2:F4) * $A$6 == 150;
});

it("it multiplied the formula properly", () => {
it("it spread the formula properly", () => {
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("B10", "Ref"))).toBe('B2 * $A$6'); // 22;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("C11", "Ref"))).toBe('SHARED'); // C3 * $A$6 == 44;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("D12", "Ref"))).toBe('SHARED'); // D4 * $A$6 == 66;
});

it("it spread the formula on a nested reference properly", () => {
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("B7", "Split"))).toBe('SUM(B2:F2)'); // 65;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("B9", "Split"))).toBe('SUM(B4:F4)'); // 165;
});

it("it spread the formula on a nested reference with anchored cell properly", () => {
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("C7", "Split"))).toBe('SUM(B2:F2) * $A$6'); // 130;
expect(xlsxAccess.cellFormula(xlsxAccess.getCell("C9", "Split"))).toBe('SUM(B4:F4) * $A$6'); // 330
});
});

describe("Simple Books Template", () => {
Expand Down

0 comments on commit 2c15426

Please sign in to comment.