我在 Google Apps 脚本中编写了一个脚本,用于生成 PDF 格式的测试结果报告,并显示在电子表格中。显然脚本是正确的,但显然不是,因为每次它都会返回错误
“异常:无法使用空数据表创建图表。”
有人对该怎么做有建议吗?
脚本如下,我还在电子表格中打印了部分数据。
// Constants that store specific Google Drive files and folder IDs
const DOCID = '1cfVmsXj1rtmWEM3NL28oTiDPsGjxQelv8aHoqkw4brw'; // The ID of the Google Document template
const FOLDERID = '1NqdCopDxoyANKcmfkXJCnh4pE6AvNNN5'; // The ID of the target Google Drive folder
// This function runs every time the Google Sheet is opened
function onOpen() {
// Get the UI of the Google Sheet and add menu items
SpreadsheetApp.getUi()
.createMenu('Test result reports')
.addItem('Generate reports', 'generateReports')
.addItem('Send reports', 'sendReports')
.addToUi();
}
// Helper function to validate email addresses
function isValidEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
}
// FUNCTION THAT GENERATES THE TEST REPORTS
function generateReports() {
const activeSpreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId(); // Get the active spreadsheet's ID dynamically
const sheet = SpreadsheetApp.openById(activeSpreadsheetId).getSheetByName('Envio de report');
const data = sheet.getDataRange().getValues();
const temp = DriveApp.getFileById(DOCID);
const folder = DriveApp.getFolderById(FOLDERID);
const rows = data.slice(1); // Skip the headers row
const skillNames = ["Objetividade Argumentativa", "Análise sob Incerteza", "Análise de Padrões", "Inteligência Espacial", "Pensamento Matemático", "Total Cognitivo", "Inglês"]; // Skill names array
rows.forEach((row, index) => {
const email = row[2];
// Check if report hasn't been generated yet (column AR in the row must be empty)
if (row[43] == '') {
const file = temp.makeCopy(folder);
const doc = DocumentApp.openById(file.getId());
const body = doc.getBody();
// Loop through the spreadsheet header row to replace placeholders in the document body
data[0].forEach((heading, i) => {
body.replaceText(`{${heading}}`, row[i]);
});
// Replace placeholders in the document header
const headers = doc.getHeader();
if (headers) {
data[0].forEach((heading, i) => {
headers.replaceText(`{${heading}}`, row[i]);
});
}
// Generate chart comparing candidate's score, top quintile average, and median score
const candidateScoresIndices = [4, 7, 10, 13, 16, 19, 22];
const topQuintileIndices = candidateScoresIndices.map(index => index + 1);
const medianIndices = candidateScoresIndices.map(index => index + 2);
// Log the row values to debug
Logger.log('Row values: ' + row);
Logger.log('Candidate Scores: ' + candidateScoresIndices.map(i => row[i]));
Logger.log('Top Quintile: ' + topQuintileIndices.map(i => row[i]));
Logger.log('Median: ' + medianIndices.map(i => row[i]));
const dataTable = Charts.newDataTable()
.addColumn(Charts.ColumnType.STRING, 'Habilidade')
.addColumn(Charts.ColumnType.NUMBER, 'Nota')
.addColumn(Charts.ColumnType.NUMBER, 'Top Quintile')
.addColumn(Charts.ColumnType.NUMBER, 'Mediana')
.build();
// Use the provided skill names and format the scores
for(let i = 0; i < candidateScoresIndices.length; i++) {
dataTable.addRow([skillNames[i], formatAsPercentage(row[candidateScoresIndices[i]]), formatAsPercentage(row[topQuintileIndices[i]]), formatAsPercentage(row[medianIndices[i]])]);
}
const chartBuilder = Charts.newComboChart()
.setDataTable(dataTable)
.setDimensions(600, 400)
.setRange(0, 1)
.setSeries([{
type: Charts.ChartType.BARS,
targetAxisIndex: 0,
color: '#7B70AD'
}, {
type: Charts.ChartType.LINE,
targetAxisIndex: 0,
color: '#B46AAB'
}, {
type: Charts.ChartType.STEPPED_AREA,
targetAxisIndex: 0,
color: '#D8D5E7'
}])
.setLegendPosition(Charts.Position.BOTTOM);
const chart = chartBuilder.build();
// Insert the chart where bookmark named 'CHART_POSITION' is
const bookmark = doc.getBookmarkById('CHART_POSITION');
const position = bookmark.getPosition();
body.insertImage(position, chart.getAs('image/png')).setAltDescription('Resultado por habilidade').setWidth(600).setHeight(400);
// Set the name of the new document based on the user name
doc.setName('Relatório individual - ' + row[1]);
// Convert doc to PDF
const blob = doc.getAs(MimeType.PDF);
doc.saveAndClose();
// Save the PDF in the specified folder and set its name
folder.createFile(blob).setName('[enter image description here](https://i.stack.imgur.com/imgl6.png)Relatório individual - ' + row[1] + '.pdf');
// Set the report creation date in column U for the current row
const creationDateCell = sheet.getRange(index + 2, 21); // +2 because arrays are 0-indexed and we skipped the header row
const formattedDate = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'dd/MM/yyyy');
creationDateCell.setValue(formattedDate);
}
});
}
// Helper function to format values as percentages
function formatAsPercentage(value) {
return (value * 100).toFixed(0) + '%';
}
这是构建散点图的简单示例。正如我所指出的,
newComboChart
不是 Charts
的方法。我还注意到 ScatterChartBuilder
和 DataTableBuilder
的大多数方法都会返回 this
,因此您不需要链接方法。
代码.gs
function drawChart() {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spread.getSheetByName("Scatter");
let data = sheet.getDataRange().getValues();
let header = data.shift();
let dataTable = Charts.newDataTable();
dataTable.addColumn(Charts.ColumnType.NUMBER,header[0]);
dataTable.addColumn(Charts.ColumnType.NUMBER,header[1]);
dataTable.addColumn(Charts.ColumnType.NUMBER,header[2]);
data.forEach( row => dataTable.addRow(row) );
dataTable = dataTable.build();
let chart = Charts.newScatterChart()
.setDataTable(dataTable)
.build();
let doc = DocumentApp.create("TestChart");
let body = doc.getBody();
body.appendImage(chart.getBlob());
}
截图
参考