Created
September 24, 2023 03:21
-
-
Save cddouglass/98febc5535bf5d941ce2f3bc5662a467 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let settings = input.config({ | |
title: 'Create bar chart in Markdown', | |
description: 'This script will create a bar chart from your data and output it in Markdown', | |
items: [ | |
input.config.table('table', { | |
label: 'Table' | |
}), | |
input.config.view('view', { | |
parentTable: 'table', | |
label: 'View' | |
}), | |
input.config.field('label', { | |
parentTable: 'table', | |
label: 'Label field', | |
}), | |
input.config.field('value', { | |
parentTable: 'table', | |
label: 'Value field', | |
}), | |
], | |
}); | |
let barChart = function(title, bars, options) { | |
options = { | |
...{ | |
'character': '█', | |
'totalNumberOfBlocks': 30, | |
'emptyBlock': ' ', | |
'sortOnValue': false, // ASC or DESC | |
'displayPercentages': true | |
}, | |
...options || {} | |
}; | |
let sumOfAllValues = bars.reduce((a, b) => a + b.value, 0); | |
let sort = function(bars, direction) { | |
if (direction === 'ASC') { | |
bars.sort((a, b) => (a.value > b.value) ? 1 : -1); | |
} else if (direction === 'DESC') { | |
bars.sort((a, b) => (a.value > b.value) ? -1 : 1); | |
} | |
return bars; | |
}; | |
let formatValue = function(value) { | |
if (options.displayPercentages) { | |
value = Math.round(value / sumOfAllValues * 100) + '%'; | |
} | |
return value; | |
}; | |
sort(bars, options.sortOnValue) | |
return markdownTable([ | |
['', title, ''], | |
...bars.map((bar) => { | |
let fullNumberOfblocks = Math.ceil(options.totalNumberOfBlocks * (bar.value / sumOfAllValues)); | |
let emptyNumberOfBlocks = Math.floor(options.totalNumberOfBlocks - fullNumberOfblocks); | |
return ['**' + bar.label + '** |', options.character.repeat(fullNumberOfblocks) + options.emptyBlock.repeat(emptyNumberOfBlocks), ' | **' + formatValue(bar.value) + '**']; | |
}) | |
], { | |
align: ['r', 'l', 'l'] | |
}); | |
}; | |
async function generateChart() { | |
let result = await settings.view.selectRecordsAsync(); | |
let bars = []; | |
result.records.forEach(function(record) { | |
bars.push({ | |
'label': record.getCellValueAsString(settings.label.id), | |
'value': record.getCellValue(settings.value.id), | |
}); | |
}); | |
output.markdown(barChart('test', bars)); | |
} | |
await generateChart(); | |
/** | |
* Code to generate markdown table from array. | |
* Grabbed from https://github.com/wooorm/markdown-table/blob/main/index.js | |
*/ | |
function markdownTable(table, options) { | |
const settings = options || {} | |
const align = (settings.align || []).concat() | |
const stringLength = settings.stringLength || defaultStringLength | |
/** @type {number[]} Character codes as symbols for alignment per column. */ | |
const alignments = [] | |
let rowIndex = -1 | |
/** @type {string[][]} Cells per row. */ | |
const cellMatrix = [] | |
/** @type {number[][]} Sizes of each cell per row. */ | |
const sizeMatrix = [] | |
/** @type {number[]} */ | |
const longestCellByColumn = [] | |
let mostCellsPerRow = 0 | |
/** @type {number} */ | |
let columnIndex | |
/** @type {string[]} Cells of current row */ | |
let row | |
/** @type {number[]} Sizes of current row */ | |
let sizes | |
/** @type {number} Sizes of current cell */ | |
let size | |
/** @type {string} Current cell */ | |
let cell | |
/** @type {string[]} Chunks of current line. */ | |
let line | |
/** @type {string} */ | |
let before | |
/** @type {string} */ | |
let after | |
/** @type {number} */ | |
let code | |
// This is a superfluous loop if we don’t align delimiters, but otherwise we’d | |
// do superfluous work when aligning, so optimize for aligning. | |
while (++rowIndex < table.length) { | |
columnIndex = -1 | |
row = [] | |
sizes = [] | |
if (table[rowIndex].length > mostCellsPerRow) { | |
mostCellsPerRow = table[rowIndex].length | |
} | |
while (++columnIndex < table[rowIndex].length) { | |
cell = serialize(table[rowIndex][columnIndex]) | |
if (settings.alignDelimiters !== false) { | |
size = stringLength(cell) | |
sizes[columnIndex] = size | |
if ( | |
longestCellByColumn[columnIndex] === undefined || | |
size > longestCellByColumn[columnIndex] | |
) { | |
longestCellByColumn[columnIndex] = size | |
} | |
} | |
row.push(cell) | |
} | |
cellMatrix[rowIndex] = row | |
sizeMatrix[rowIndex] = sizes | |
} | |
// Figure out which alignments to use. | |
columnIndex = -1 | |
if (typeof align === 'object' && 'length' in align) { | |
while (++columnIndex < mostCellsPerRow) { | |
alignments[columnIndex] = toAlignment(align[columnIndex]) | |
} | |
} else { | |
code = toAlignment(align) | |
while (++columnIndex < mostCellsPerRow) { | |
alignments[columnIndex] = code | |
} | |
} | |
// Inject the alignment row. | |
columnIndex = -1 | |
row = [] | |
sizes = [] | |
while (++columnIndex < mostCellsPerRow) { | |
code = alignments[columnIndex] | |
before = '' | |
after = '' | |
if (code === 99 /* `c` */ ) { | |
before = ':' | |
after = ':' | |
} else if (code === 108 /* `l` */ ) { | |
before = ':' | |
} else if (code === 114 /* `r` */ ) { | |
after = ':' | |
} | |
// There *must* be at least one hyphen-minus in each alignment cell. | |
size = | |
settings.alignDelimiters === false ? | |
1 : | |
Math.max( | |
1, | |
longestCellByColumn[columnIndex] - before.length - after.length | |
) | |
cell = before + '-'.repeat(size) + after | |
if (settings.alignDelimiters !== false) { | |
size = before.length + size + after.length | |
if (size > longestCellByColumn[columnIndex]) { | |
longestCellByColumn[columnIndex] = size | |
} | |
sizes[columnIndex] = size | |
} | |
row[columnIndex] = cell | |
} | |
// Inject the alignment row. | |
cellMatrix.splice(1, 0, row) | |
sizeMatrix.splice(1, 0, sizes) | |
rowIndex = -1 | |
/** @type {string[]} */ | |
const lines = [] | |
while (++rowIndex < cellMatrix.length) { | |
row = cellMatrix[rowIndex] | |
sizes = sizeMatrix[rowIndex] | |
columnIndex = -1 | |
line = [] | |
while (++columnIndex < mostCellsPerRow) { | |
cell = row[columnIndex] || '' | |
before = '' | |
after = '' | |
if (settings.alignDelimiters !== false) { | |
size = longestCellByColumn[columnIndex] - (sizes[columnIndex] || 0) | |
code = alignments[columnIndex] | |
if (code === 114 /* `r` */ ) { | |
before = ' '.repeat(size) | |
} else if (code === 99 /* `c` */ ) { | |
if (size % 2) { | |
before = ' '.repeat(size / 2 + 0.5) | |
after = ' '.repeat(size / 2 - 0.5) | |
} else { | |
before = ' '.repeat(size / 2) | |
after = before | |
} | |
} else { | |
after = ' '.repeat(size) | |
} | |
} | |
if (settings.delimiterStart !== false && !columnIndex) { | |
line.push('|') | |
} | |
if ( | |
settings.padding !== false && | |
// Don’t add the opening space if we’re not aligning and the cell is | |
// empty: there will be a closing space. | |
!(settings.alignDelimiters === false && cell === '') && | |
(settings.delimiterStart !== false || columnIndex) | |
) { | |
line.push(' ') | |
} | |
if (settings.alignDelimiters !== false) { | |
line.push(before) | |
} | |
line.push(cell) | |
if (settings.alignDelimiters !== false) { | |
line.push(after) | |
} | |
if (settings.padding !== false) { | |
line.push(' ') | |
} | |
if ( | |
settings.delimiterEnd !== false || | |
columnIndex !== mostCellsPerRow - 1 | |
) { | |
line.push('|') | |
} | |
} | |
lines.push( | |
settings.delimiterEnd === false ? | |
line.join('').replace(/ +$/, '') : | |
line.join('') | |
) | |
} | |
return lines.join('\n') | |
} | |
/** | |
* @param {string|null|undefined} [value] | |
* @returns {string} | |
*/ | |
function serialize(value) { | |
return value === null || value === undefined ? '' : String(value) | |
} | |
/** | |
* @param {string} value | |
* @returns {number} | |
*/ | |
function defaultStringLength(value) { | |
return value.length | |
} | |
/** | |
* @param {string|null|undefined} value | |
* @returns {number} | |
*/ | |
function toAlignment(value) { | |
const code = typeof value === 'string' ? value.charCodeAt(0) : 0 | |
return code === 67 /* `C` */ || code === 99 /* `c` */ ? | |
99 /* `c` */ : | |
code === 76 /* `L` */ || code === 108 /* `l` */ ? | |
108 /* `l` */ : | |
code === 82 /* `R` */ || code === 114 /* `r` */ ? | |
114 /* `r` */ : | |
0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment