Skip to content

Commit

Permalink
feat: add column aliasing
Browse files Browse the repository at this point in the history
I have a use-case where the column/field defined by WooCmmerce Product Add-ons is absurdly long, and
I want to rename when generating the CSV output.
  • Loading branch information
JaredReisinger committed Sep 10, 2024
1 parent 3869e8b commit 3295b4b
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 37 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ The various `--omit...` and `--include` options control whether columns are incl

If the `--omit-...` options are too coarse, you can enable or disable individual columns using the `--include` and `--omit` options, or for complete control use the `--columns` option to specify _exactly_ what columns you want displayed and in what order they will appear. Using `--list-columns` will list all of the columns present in a given set of items. Do note that unless you use `--columns` to specify an exact set, the following columns are _always_ included: "order#", "date", "name", "email", "qty", and "total".

The `--columns` option also allows for columns/fields with commas in the name by backslash-escaping the comma, `as\,such`. You can also provide column aliases if you need to rename the output column using the `alias=real_name` syntax.

### Examples

```sh
Expand All @@ -99,7 +101,7 @@ Retrieve all order line-items after 1 January 2019 (inclusive) with a status of
orders host1 --after 2019-01-01 --status processing --sku some-sku --out some-sku.csv
```

Retrieve all order line-items as in the previous command, filter the items to the SKU `some-sku`, and write to 'some-sku.csv'.
Retrieve all order line-items as in the previous command, filter the items to the sku `some-sku`, and write to 'some-sku.csv'.

```sh
orders host1 --sku-prefix some-sku --list-columns
Expand All @@ -112,3 +114,9 @@ orders host1 --after 2019-01-01 --sku-prefix some-sku --columns "name,email,imag
```

Retrieve all order line-items with a sku that starts with `some-sku`, and write only the "name", "email", and "image" columns and to 'some-sku.csv'

```sh
orders host1 --after 2019-01-01 --sku some-sku --columns "name,count=qty" --out some-sku.csv
```

Retrieve all order line-items with the sku `some-sku` and write the column "name" and the "qty" column with the label "count" to 'some-sku.csv'
18 changes: 18 additions & 0 deletions src/commands/get.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,24 @@ test('Get.run() can handle escaped-comma columns', async (t) => {
await t.notThrowsAsync(result);
});

test('Get.createColumnFields() can handle list of columns', (t) => {
const actual = Get.createColumnFields('one,two,three', []);
t.like(actual[0], { label: 'one' });
t.like(actual[1], { label: 'two' });
t.like(actual[2], { label: 'three' });
});

test('Get.createColumnFields() can handle escaped-comma columns', (t) => {
const actual = Get.createColumnFields('one\\,two,three', []);
t.like(actual[0], { label: 'one,two' });
t.like(actual[1], { label: 'three' });
});

test('Get.createColumnFields() can handle column alias', (t) => {
const actual = Get.createColumnFields('the_sku=sku', []);
t.like(actual[0], { label: 'the_sku' });
});

test('Get.run() can output CSV to a file', async (t) => {
const get = createGet();
const { client, stub } = createClientStub();
Expand Down
103 changes: 67 additions & 36 deletions src/commands/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ About Column Filtering:
If the "--omit-..." options are too coarse, you can enable or disable individual columns using the "--include" and "--omit" options, or for complete control use the "--columns" option to specify *exactly* what columns you want displayed and in what order they will appear. Using "--list-columns" will list all of the column names present in a given set of items. Do note that Do note that unless you use "--columns" to specify an exact set, the following columns are *always* included: "order#", "date", "name", "email", "qty", and "total".
The "--columns" option also allows for columns/fields with commas in the name by backslash-escaping the comma, "as\\,such". You can also provide column aliases if you need to rename the output column using the "alias=real_name" syntax.
Examples:
$0 host1 --after 2018-01-01 --status processing --list-skus
Expand Down Expand Up @@ -372,42 +374,7 @@ Examples:
let fields: typeof allFields;

if (argv.columns) {
// If columns is given, create *exactly* those fields... note that a
// column/meta name *can* contain a comma; we use "\," for this. We used
// to have a simple map() to process, but we need a loop to handle
// "compressing" elements together if needed.
const cols = argv.columns.split(',');
for (let i = 0; i < cols.length; i++) {
// if the column ends with \, combine, and restart processing from the
// same field (in case there's another escape!)
const s = cols[i];
if (s.endsWith('\\') && i + 1 < cols.length) {
cols[i] = `${s.substring(0, s.length - 1)},${cols[i + 1]}`;
cols.splice(i + 1, 1);
i--;
continue;
}

cols[i] = s.trim();
}

// We *used* to filter to only existing (found) columns/fields, but there
// are cases where we need a placeholder column in the CSV... for an
// occasional field, we don't want the output to change based on a
// particular range *not* having the column!
fields = cols.map(
(c) =>
allFields.find((f) => f.label === c) || {
label: c,
value: () => '',
config: {},
meta: {
allBlank: true,
allIdentical: true,
maximumWidth: c.length,
},
}
);
fields = Get.createColumnFields(argv.columns, allFields);
} else {
// Regardless of the "--omit-..." options, we *always* want to include the
// quantity and total.
Expand Down Expand Up @@ -729,6 +696,70 @@ Examples:
formatAmount(amt: string, code: string, currencies: WooCurrencies) {
return `${currencies.getSymbol(code)}${amt}`;
}

static createColumnFields(
columns: string,
allFields: AugmentedFieldInfo<WooItem>[]
) {
// If columns is given, create *exactly* those fields... note that a
// column/meta name *can* contain a comma; we use "\," for this. We used
// to have a simple map() to process, but we need a loop to handle
// "compressing" elements together if needed.
const cols = columns.split(',');
for (let i = 0; i < cols.length; i++) {
// if the column ends with \, combine, and restart processing from the
// same field (in case there's another escape!)
const s = cols[i];
if (s.endsWith('\\') && i + 1 < cols.length) {
cols[i] = `${s.substring(0, s.length - 1)},${cols[i + 1]}`;
cols.splice(i + 1, 1);
i--;
continue;
}

cols[i] = s.trim();
}

// We *used* to filter to only existing (found) columns/fields, but there
// are cases where we need a placeholder column in the CSV... for an
// occasional field, we don't want the output to change based on a
// particular range *not* having the column!
const fields = cols.map((c) => {
// We now support `alias=field_name` for a column, so we need to handle
// parsing that.
const alias = c.split('=', 2);
const label = alias[0];
const sourceLabel = alias[alias.length - 1];
let col = allFields.find(
(f) => f.label === sourceLabel || f.label === c
) || {
label: alias[0],
value: () => '',
config: {},
meta: {
allBlank: true,
allIdentical: true,
maximumWidth: c.length,
},
};

if (alias.length > 1) {
col = {
...col,
label,
meta: {
...col.meta,
maximumWidth: Math.max(col.meta.maximumWidth, label.length),
},
};
}

return col;
});

console.log('COLUMNS', fields);
return fields;
}
}

// REVIEW: should this move to WooClient?
Expand Down

0 comments on commit 3295b4b

Please sign in to comment.