简体   繁体   English

从 Google 表格范围内以图像/博客/png 形式发送迷你图

[英]Email Sparkline graphs as image/blog/png from Google Sheets range

I tried applying this solution to my case:我尝试将此解决方案应用于我的案例:
Emailing SPARKLINE charts sends blank cells instead of data 通过电子邮件发送 SPARKLINE 图表会发送空白单元格而不是数据

But when I try to apply it to my situation an error pops up with:但是当我尝试将它应用于我的情况时,会弹出一个错误:

TypeError: Cannot read property '0' of null

On the executions there is more information about this error:在执行中有关于此错误的更多信息:

在此处输入图像描述

My GAS code for my Email solution is able to send just the values, and it's here:我的电子邮件解决方案的 GAS 代码只能发送值,它在这里:

function alertDailyInfo() {

    let emailAddress = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F1").getValue();
    
    let treeIconUrl = "https://d1nhio0ox7pgb.cloudfront.net/_img/g_collection_png/standard/256x256/tree.png";
    let treeIconBlob = UrlFetchApp
                       .fetch(treeIconUrl)
                       .getBlob()
                       .setName("treeIconBlob");
    
    let treeUpdate = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F6").getValue();
    let waterUpdate = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F11").getValue();

            if (treeUpdate > 0) {
            
                    MailApp.sendEmail({
                        to: emailAddress,
                        subject: "TREE WATER UPDATE",
                        htmlBody: "<img src='cid:treeIcon'><br>" + '<br>' + '<br>' +  
                        '<b><u>Tree average is:</u></b>'+ '<br>' + treeUpdate + '<br>' + '<br>' +
                        '<b><u>Water average is:</u></b>'+ '<br>' + waterUpdate + '<br>' + '<br>' 
                        ,
                        inlineImages:
                        {
                            treeIcon: treeIconBlob,
                        }
                    });                
                }
            }

The code from the solution presented on the link above and which I have tried to adapt to my situation (please check my file below) is here:上面链接中提供的解决方案的代码以及我试图适应我的情况的代码(请查看下面的文件)在这里:

drawTable();

function drawTable() {

 let emailAddress1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F1").getValue();

    var ss_data = getData();
    var data = ss_data[0];
    var background = ss_data[1];
    var fontColor = ss_data[2];
    var fontStyles = ss_data[3];
    var fontWeight = ss_data[4];
    var fontSize = ss_data[5];
    var html = "<table border='1'>";
    var images = {}; // Added
    for (var i = 0; i < data.length; i++) {
        html += "<tr>"
        for (var j = 0; j < data[i].length; j++) {
            if (typeof data[i][j] == "object") { // Added
                html += "<td style='height:20px;background:" + background[i][j] + ";color:" + fontColor[i][j] + ";font-style:" + fontStyles[i][j] + ";font-weight:" + fontWeight[i][j] + ";font-size:" + (fontSize[i][j] + 6) + "px;'><img src='cid:img" + i + "'></td>"; // Added
                images["img" + i] = data[i][j]; // Added
            } else {
                html += "<td style='height:20px;background:" + background[i][j] + ";color:" + fontColor[i][j] + ";font-style:" + fontStyles[i][j] + ";font-weight:" + fontWeight[i][j] + ";font-size:" + (fontSize[i][j] + 6) + "px;'>" + data[i][j] + "</td>";
            }
        }
        html += "</tr>";
    }
    html + "</table>"
    MailApp.sendEmail({
        to: emailAddress1,
        subject: "Spreadsheet Data",
        htmlBody: html,
        inlineImages: images // Added
    })
}

function getData(){
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX");
  var ss = sheet.getDataRange();
  var val = ss.getDisplayValues();
  var background = ss.getBackgrounds();
  var fontColor = ss.getFontColors();
  var fontStyles = ss.getFontStyles();
  var fontWeight = ss.getFontWeights();
  var fontSize = ss.getFontSizes();
  var formulas = ss.getFormulas(); // Added
  val = val.map(function(e, i){return e.map(function(f, j){return f ? f : getSPARKLINE(sheet, formulas[i][j])})}); // Added
  return [val,background,fontColor,fontStyles,fontWeight,fontSize]; 
}

// Added
function getSPARKLINE(sheet, formula) {
  formula = formula.toUpperCase();
  if (~formula.indexOf("SPARKLINE")) {
    var chart = sheet.newChart()
      .setChartType(Charts.ChartType.SPARKLINE)
      .addRange(sheet.getRange(formula.match(/\w+:\w+/)[0]))
      .setTransposeRowsAndColumns(true)
      .setOption("showAxisLines", false)
      .setOption("showValueLabels", false)
      .setOption("width", 200)
      .setOption("height", 100)
      .setPosition(1, 1, 0, 0)
      .build();
    sheet.insertChart(chart); 
    var createdChart = sheet.getCharts()[0];
    var blob = createdChart.getAs('image/png');
    sheet.removeChart(createdChart);
    return blob;
  }
}

The code that is working just for the values, which I pasted above (1st block of code), will send me an email like this:我在上面粘贴的仅适用于值的代码(第一个代码块)将向我发送一封这样的电子邮件:

在此处输入图像描述

But I need to receive the email like this, with the Sparklines below the values like so:但我需要收到这样的电子邮件,迷你图的值如下:

在此处输入图像描述

The code for the Email solution, just for the values, I pasted above (1st block of code) is working.电子邮件解决方案的代码,仅用于值,我粘贴在上面(第一个代码块)正在工作。 But for some reason when the code from the solution linked above (2nd block of code) is imported/saved into my Google Sheets file GAS script library and adapted to my case, everything stops working, displaying the errors mentioned above.但是由于某种原因,当上面链接的解决方案中的代码(第二个代码块)被导入/保存到我的 Google 表格文件 GAS 脚本库并适应我的情况时,一切都停止工作,显示上述错误。

So basically, as you might have already understood, I need to send emails with the values from Tree Average and Water Average , and I managed to get that working.所以基本上,正如您可能已经理解的那样,我需要发送包含Tree AverageWater Average值的电子邮件,并且我设法让它工作。 But I also need for the Sparkline graphs that you can see below, and by checking my file linked below too, to also be sent as images/blobs, just below the info, like in the screenshot above.但我还需要您可以在下面看到的迷你图,并通过检查下面链接的文件,也需要作为图像/blob 发送,就在信息下方,就像上面的屏幕截图一样。

在此处输入图像描述

Can anyone provide any pointers on what can be missing in applying the solution above or is there a better alternative to sending a SPARKLINE graph as image/blob by email?任何人都可以就应用上述解决方案可能缺少的内容提供任何指示,或者是否有更好的替代方法来通过电子邮件将 SPARKLINE 图作为图像/blob 发送?

Here is my file:这是我的文件:
https://docs.google.com/spreadsheets/d/1ExXtmQ8nyuV1o_UtabVJ-TifIbORItFMWjtN6ZlruWc/edit?usp=sharing https://docs.google.com/spreadsheets/d/1ExXtmQ8nyuV1o_UtabVJ-TifIbORItFMWjtN6ZlruWc/edit?usp=sharing

EDIT_1:编辑_1:
I made some edits to bring more clarity.我进行了一些编辑以提高清晰度。

EDIT_2: EDIT_2:
As requested this is the formula applied to the first Sparkline, the 2nd one is pretty much the same:根据要求,这是应用于第一个 Sparkline 的公式,第二个几乎相同:

=ARRAYFORMULA( SPARKLINE(QUERY({IFERROR(DATEVALUE(SANDBOX!$A$2:$A)), SANDBOX!$B$2:$B}, 
 "select Col2 
  where Col2 is not null 
  and Col1 <= "&INT(MAX(SANDBOX!$A$2:$A))&"
  and Col1 >  "&INT(MAX(SANDBOX!$A$2:$A))-(
IFERROR(
          VLOOKUP(
           SUBSTITUTE($F$4," ",""),
            {"24HOURS",0;
            "2DAYS",1;
            "3DAYS",4; 
            "7DAYS",8; 
            "2WEEKS",16;  
            "1MONTH",30;
            "3MONTHS",90; 
            "6MONTHS",180; 
            "1YEAR",365;
            "2YEARS",730; 
            "3YEARS",1095},
           2,FALSE))
)-1, 0),
 {"charttype","column";"color","#00bb21";"empty","ignore";"nan","ignore"}))

EDIT_3 :编辑_3
At the advice of Rubén I have removed drawTable();在 Rubén 的建议下,我删除drawTable(); at the beggining of the code block.在代码块的开头。

I have also transfered the formula for the Sparkline to another helper sheet and link it to the main sheet.我还将 Sparkline 的公式转移到另一个帮助表并将其链接到主表。 After trying it seems the error does not appear anymore.尝试后似乎不再出现错误。 Although the email received has 2 problems:虽然收到的邮件有两个问题:

  • I receive the whole sheet in table form, where I just wanted the Sparklines.我收到了表格形式的整张纸,我只想要迷你图。
  • Also the Sparklines do not come as images, they do not show up at all.迷你图也不是图像,它们根本不会出现。 Also where they should appear it says undefined .他们应该出现的地方也写着undefined

I guess the whole sheet is being set because the function getting the range getDataRange();我猜整个工作表正在设置,因为函数获取范围getDataRange(); is getting the whole sheet range.正在获取整个工作表范围。

Here is a screenshot:这是一个屏幕截图:

在此处输入图像描述

As the question you reference explains:正如您引用的问题所解释的:

the chart created by SPARKLINE cannot be directly imported to the email. SPARKLINE创建的图表不能直接导入到邮件中。

Why isn't the script working?为什么脚本不起作用? Because you have not made any significant modifications to it and because you are using a more complex formula than the one proposed in the other question, it is very difficult (if not impossible) to make it work without any modifications.因为您没有对其进行任何重大修改,并且因为您使用的公式比另一个问题中提出的公式更复杂,所以在不进行任何修改的情况下使其工作非常困难(如果不是不可能的话)。

What are the options?有什么选择? In my opinion you have 3 different options.在我看来,您有 3 种不同的选择。

  1. Follow the logic of the solution proposed by Tanaike in the other question and using EmbeddedChartBuilder try to shred the content of the FORMULA to achieve the same as with SPARKLINE .遵循Tanaike在另一个问题中提出的解决方案的逻辑,并使用EmbeddedChartBuilder尝试粉碎 FORMULA 的内容以达到与SPARKLINE相同的效果。

  2. Use the SpreadsheetApp methods to directly get the values from the sheet and build the chart from there.Here is a small example of how you can do it using Chart Service (You could achieve exactly the same with EmbeddedChartBuilder ).使用SpreadsheetApp方法直接从工作表中获取值并从那里构建图表。这是一个小示例,说明如何使用Chart Service进行操作(您可以使用EmbeddedChartBuilder实现完全相同的效果)。 As you already have a Blob object, you can insert it inside an email as I do inside the Sheet.由于您已经有一个Blob对象,因此您可以将它插入到电子邮件中,就像我在工作表中所做的那样。

function constCreateChart() {
  const sS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('HELPER')
  const chart = Charts.newDataTable()
    .addColumn(Charts.ColumnType.NUMBER, '')
    .addColumn(Charts.ColumnType.NUMBER, '')

  // Modfify with your data
  // getRange('A2:A15').getValues()...
  const builder = [...Array(100).keys()].forEach(n => {
    chart.addRow([n, n * n * Math.random()])
  })
  chart.build()

  const chartShap = Charts.newColumnChart()
    .setDataTable(chart)
    .setLegendPosition(Charts.Position.NONE)
    .setOption('hAxis.ticks', [])
    .setOption('vAxis.ticks', [])
    .build()

  sS.insertImage(chartShap.getAs('image/png'), 5, 5)

}
Result结果

在此处输入图像描述

  1. Use this form to request Google to add the possibility to convert charts obtained using SPARKLINES to Blob objects that can be used inside an email.使用此表单请求 Google 添加将使用SPARKLINES获得的图表转换为可在电子邮件中使用的 Blob 对象的可能性。
Documentation文档
  1. Remove drawTable();删除drawTable(); as this line makes that the drawTable function be executed when any function be called.因为这一行使得在调用任何函数时都会执行drawTable函数。
  2. Apparently the error occurs on .addRange(sheet.getRange(formula.match(/\w+:\w+/)[0])) , more specifically because formula.match(/\w+:\w+/) (this expression is intended to extract a range reference of the form A1:B10 ) returns null .显然错误发生在.addRange(sheet.getRange(formula.match(/\w+:\w+/)[0]))上,更具体地说是因为formula.match(/\w+:\w+/) (这个表达式是有意的提取A1:B10形式的范围引用)返回null Unfortunately the question doesn't include the formula.不幸的是,这个问题不包括公式。 One possible solution might be as simple as replacing sheet.getRange(formula.match(/\w+:\w+/)[0]) by another way to set the source range for the temporary chart, but might be a more complex, ie adding a helper sheet to be used as the data source for the temporary chart.一种可能的解决方案可能就像用另一种方式替换sheet.getRange(formula.match(/\w+:\w+/)[0])一样简单,以设置临时图表的源范围,但可能更复杂,即添加一个辅助表以用作临时图表的数据源。

NOTE: On Rev 11 one in-cell sparklines chart formula was added.注意:在 Rev 11 中,添加了一个单元内迷你图公式。 As the formula is pretty complex, the simplest solution is to add a helper sheet to add the QUERY function由于公式相当复杂,最简单的解决方案是添加辅助表以添加 QUERY 函数

QUERY({IFERROR(DATEVALUE(SANDBOX!$A$2:$A)), SANDBOX!$B$2:$B}, 
 "select Col2 
  where Col2 is not null 
  and Col1 <= "&INT(MAX(SANDBOX!$A$2:$A))&"
  and Col1 >  "&INT(MAX(SANDBOX!$A$2:$A))-(
IFERROR(
          VLOOKUP(
           SUBSTITUTE($F$4," ",""),
            {"24HOURS",0;
            "2DAYS",1;
            "3DAYS",4; 
            "7DAYS",8; 
            "2WEEKS",16;  
            "1MONTH",30;
            "3MONTHS",90; 
            "6MONTHS",180; 
            "1YEAR",365;
            "2YEARS",730; 
            "3YEARS",1095},
           2,FALSE))
)-1, 0)

Then instead of sheet.getRange(formula.match(/\w+:\w+/)[0]) use helperSheet.getDataRange() .然后代替sheet.getRange(formula.match(/\w+:\w+/)[0])使用helperSheet.getDataRange() You will have to set an appropriate way to declare helperSheet .您必须设置适当的方式来声明helperSheet


Related to Rev. 8与第 8 版相关

The code on Tanaike's answer reads data from Sheet1 but your sheet is named SANDBOX. Tanaike 答案中的代码从 Sheet1 读取数据,但您的工作表名为 SANDBOX。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM