如何使用 Google App Scripts 从数据透视表中将详细信息表附加为 pdf 或 excel?

Posted

技术标签:

【中文标题】如何使用 Google App Scripts 从数据透视表中将详细信息表附加为 pdf 或 excel?【英文标题】:How to attach a detail sheet as pdf or excel from a pivot table using Google App Scripts? 【发布时间】:2020-09-12 06:26:58 【问题描述】:

我有一个学生考勤系统。它在来自 TempDataSet 选项卡的数据透视表的 COUNT 列中具有每个学生的出勤计数值。我的期望可以通过以下两种方式之一实现:

流程 01:当我手动单击仪表板中数据透视表的计数列的任何单元格时,它会生成一个出勤详细信息 那个学生的数据来自“TempDataSet”,比如他/她参加了哪几天 班级。然后工作表名称是 details-abc@gmail.com。然后我可以发送 将详细信息表作为 pdf 手动发送到学生的电子邮件地址。现在整个过程都可以使用 Google App Scripts 自动完成了吗?

OR 过程 02:根据学生电子邮件 ID 拆分 TempDataSet(与附件图像相同的模式)选项卡,并通过电子邮件单独作为附件发送给学生。但是,我一个班有 50-60 名学生,所以所有都应该通过从仪表板单击来完成,现在工作正常,但问题是它将整个 TempDataSet 选项卡发送给所有学生,而不是拆分 TempDataSet 信息并发送每个学生的具体信息。

以下代码适用于单个工作表选项卡下载和电子邮件作为 excel 附件(所有学生的出勤信息在单个工作表中发送给所有学生:

function autoEmailing()
  var sss = SpreadsheetApp.getActiveSpreadsheet();
  var ssID = sss.getId();
  var sheetName = sss.getName(); 
  var sheet = sss.getSheetByName("TempDataSet");
  var sheet1 = sss.insertSheet('TempDataSet_temp');
  sheet.getDataRange().copyTo(sheet1.getActiveRange(), 
  SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
  sheet.getDataRange().copyTo(sheet1.getActiveRange(), 
  SpreadsheetApp.CopyPasteType.PASTE_FORMAT, false);  

  var shID = sheet1.getSheetId().toString();
  sheet1.getRange(2, 1, sheet.getLastRow() -1, 
  sheet.getLastColumn()).sort(column: 3, ascending: true); 
  var columns_delete = [7,5,4,2];
  columns_delete.forEach(col=>sheet1.deleteColumn(col));
 
  var subject = 'Your Attendance Record at BDU';
  var body = 'Dear Student,'+ '\n\n' + 'Greetings! Please find the attendance record attached for your reference.' + '\n\n' + 'Thank you.';
  
  var requestData = "method": "GET", "headers":"Authorization":"Bearer "+ScriptApp.getOAuthToken();  
  var url = "https://docs.google.com/spreadsheets/d/"+ ssID + "/export?format=xlsx&id="+ssID+"&gid="+shID;

  var result = UrlFetchApp.fetch(url , requestData);  
  var contents = result.getContent();
  sss.deleteSheet(sss.getSheetByName('TempDataSet_temp'));   
  
  var sheet2 = sss.getSheetByName('StudentList');  
  var data = sheet2.getLastRow();
  var students = [];
  var students = sheet2.getRange(2, 6, data).getValues(); 
  //MailApp.sendEmail(students.toString(), subject ,body, attachments:[fileName:sheetName+".xlsx", content:contents, mimeType:"MICROSOFT_EXCEL"]);
  
  for (var i=0; i<students.length; i++) // you are looping through rows and selecting the 1st and only column index
    if (students[i][0] !== '')           
      MailApp.sendEmail(students[i][0].toString(), subject ,body, attachments:[fileName:sheetName+".xlsx", content:contents, mimeType:"MICROSOFT_EXCEL"]); 
     
    

【问题讨论】:

请描述这个过程是如何完成的: >当我手动点击它时,它会生成该学生的出勤详细信息,例如她/他上课的日期。 当我双击 Column2 (COUNT) 上的任何值时,它会生成该学生的详细出勤表。例如,在附图中,如果您单击值为 99 的 B13 单元格,它将生成他/她参加的 99 次出勤的详细信息。请将工作表视为数据透视表,生成的详细信息表位于第二个选项卡。 我明白了,请与输入共享示例表,并使用所需的输出更新您的问题。这将使我们能够更好地帮助您。 @Alessandro,我根据您的建议更新了我的帖子。谢谢 你的意思是在这种情况下只发送1802001@email.com 的第 13 行吗?或者Detail30-1802001@email.comSheet的内容(如果是这种情况请分享如何获取Detail-sheet)? 【参考方案1】:

解释:

以下脚本遍历数据透视表(Dashboard)和 对于每封学生的电子邮件,它都会过滤相关数据,为此 特定学生,从 TempDataSet 工作表中复制到 名为temp_sh 的临时工作表。最后,后者通过电子邮件发送为 给特定学生的 excel 文件。

值得一提的是,Spreadsheet.flush() 在这种情况下确实是必要的,因为脚本会不断创建和删除临时工作表,因此每次迭代都需要进行待处理的更改。


解决方案:

function emailSender()

const ss = SpreadsheetApp.getActive();
const sh_db = ss.getSheetByName('Dashboard');
const sh_tds = ss.getSheetByName('TempDataSet');

const u_emails = sh_db.getRange('A13:A57').getValues().flat(); // adjust this to your specific range
const data = sh_tds.getRange('A1:G'+sh_tds.getLastRow()).getValues();

const subject = 'Your Attendance Record at BDU';
const body = 'Dear Student,'+ '\n\n' + 'Greetings! Please find the attendance record attached for your reference.' + '\n\n' + 'Thank you.';
const from = Session.getActiveUser().getEmail();
const requestData = "method": "GET", "headers":"Authorization":"Bearer "+ScriptApp.getOAuthToken(); 
const ssID = ss.getId();

u_emails.forEach(e=>

  var temp_data = data.filter( row => 
    return (row[2] == e || row[2] == 'Umail'); 
  );
  
  var temp_sh = ss.insertSheet('temp_sheet');
  temp_sh.getRange(1,1,temp_data.length,temp_data[0].length).setValues(temp_data);
  SpreadsheetApp.flush();
  var shID = temp_sh.getSheetId();
  var url = "https://docs.google.com/spreadsheets/d/"+ ssID + "/export?format=xlsx&id="+ ssID +"&gid="+shID;
  var result = UrlFetchApp.fetch(url , requestData);  
  var contents = result.getContent();
  ss.deleteSheet(temp_sh);
  
  GmailApp.sendEmail(e, subject ,body, from: from, attachments:[fileName:"YourAttendaceRecord.xlsx", content:contents, mimeType:"MICROSOFT_EXCEL"]);

);


【讨论】:

最初它在发送 5-6 封带附件的电子邮件后无法正常工作。然后我在这行代码之前放了一行 [Utilities.sleep(10000)]: var result = UrlFetchApp.fetch(url , requestData);现在它工作正常,虽然它很耗时。我将睡眠时间减少到 5 秒,但又失败了。可能 10 秒更好,但我看到触发器中的总执行时间为 551 秒。还好吗? 我的电子邮件范围始终从仪表板的 A13 单元格(固定)开始,但结束单元格会根据选择而变化。所以它可能是A57、A67或A90。实际上它从 A13 单元格开始,然后向下直到单元格(例如 A57、A67 或任何)在同一列中显示为空白。所以最好能动态定义。能否请您更新。谢谢。【参考方案2】:

解决方案

无法通过“双击”方式自动获取细节。那么这个解决方法呢?

通过 Apps 脚本构建细节

由于您按电子邮件地址进行过滤,因此很容易根据您的源数据获得过滤后的详细报告。首先,您将使用数据透视表电子邮件地址列过滤数据,然后您将使用过滤后的行和列构建一个临时工作表。

以下是该脚本如何工作的示例:

function sendEmails() 
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var pivot = ss.getActiveSheet(); // You will start the script while in the Pivot Table Dashboard
  var from = Session.getActiveUser().getEmail(); // Gets the email address of the active user executing the script
  var db = ss.getSheetByName("Database"); // Pivot table source data sheet
  var emails = pivot.getRange("A2:A").getValues(); // Pivot table column with email addresses
  emails.forEach(email => 
             var filteredRows = db.getDataRange().getValues().filter(row => row[6] == email[0]); // Filters the database with each email address
             var temp = ss.insertSheet("TempDataSet_temp"); // Creates the temporary sheet
             temp.appendRow(["Timestamp","StudentID-Name","Umail", "..."]); // Fills the header of the report
             filteredRows.forEach(row => temp.appendRow(row)); // Appends the filtered rows to the temporary Sheet

             var subject = 'Your Attendance Record at BDU';
             var body = 'Dear Student,'+ '\n\n' + 'Greetings! Please find the attendance record attached for your reference.' + '\n\n' + 'Thank you.';
  
             var requestData = "method": "GET", "headers":"Authorization":"Bearer "+ScriptApp.getOAuthToken();  
             var url = "https://docs.google.com/spreadsheets/d/"+ ss.getId() + "/export?format=xlsx&id="+ss.getId()+"&gid="+temp.getSheetId();
  
             var result = UrlFetchApp.fetch(url , requestData);  
             var contents = result.getContent();
             ss.deleteSheet(temp); // Once the Spreadsheet is exported I will delete the temporary sheet for future operations
             
             MailApp.sendEmail(email[0], subject ,body, from: from, attachments:[fileName:"YourAttendaceRecord.xlsx", content:contents, mimeType:"MICROSOFT_EXCEL"]); // Send the emails to the correct recipient and his/her relative report

  );

参考文献

getValues()

appendRow()

Array.forEach()

【讨论】:

它正在部分工作。第 6 封电子邮件是在 50 封电子邮件中发送的,附件只有正确的列标题,所有其他行都是空的。我在状态栏中看到 TempDataSet_temp 被创建了 6 次并被删除,然后它被卡住了。我检查了触发器执行,它显示“异常:服务电子表格在访问 id 为 1uIs3NQUcNrp_5kdDZ_fGxGTmRI2CMaEg_W_0aQbLFhc 的文档时失败。在 [unknown function](classattendance:493:47) at [unknown function](classattendance:493:27) at sendEmails(classattendance :489:10) at classAttendance(classattendance:13:7)" @Alessandro 在 MailApp.sendEmail() 中,它显示 'from' 对于编辑器(所有者除外)是无效参数。所以编辑不能从他们的电子邮件地址发送电子邮件。

以上是关于如何使用 Google App Scripts 从数据透视表中将详细信息表附加为 pdf 或 excel?的主要内容,如果未能解决你的问题,请参考以下文章

如何为具有多个帐户的用户在Google App Scripts中选择帐户?

使用 Google App Scripts 在电子邮件中附加 Google Docs 文件

使用 Google Scripts 从 Google People API 获取 google 联系人电子邮件目录中的电子邮件列表

有没有办法访问 Google App Scripts 中的 Adwords?

Google App Scripts(表格)未连接YouTube品牌帐户

如何在 Google Cloud App Engine 上使用 PubSub 创建订阅者,该订阅者通过 Publisher 从 Google Cloud App Engine Flex 收听消息?