导入 CSV 并替换相应命名工作表中的数据

Import CSV and replace data in corresponding named sheets

请耐心等待,因为我完全没有编写脚本的经验。 我需要帮助的是一个 Google 应用程序脚本,它自动从我的 google 驱动器导入 10 个 .csv 文件,并覆盖包含一个 'dashboard' 的传播 sheet 中的数据sheet、一个'working' sheet等都是CSV导入的sheet,与csv文件名相同。到目前为止,我已经找到了执行此操作的 trial/paid 扩展程序,但我确实找到了这个可以很好地导入数据的脚本,但它将所有数据附加到一个 sheet 而不是替换 sheets与 csv 具有相同的文件名。 我什至不确定脚本是否是最好的方式或 sheet 中的公式是否可行,但我不确定。 非常感谢您的帮助

// Application constants
const APP_TITLE = 'Trigger-driven CSV import [App Script Sample]'; // Application name
const APP_FOLDER = 'Market Monitor'; // Application primary folder
const SOURCE_FOLDER = 'Inbound CSV'; // Folder for the update files.
const PROCESSED_FOLDER = 'Processed CSV'; // Folder to hold processed files.
const SHEET_REPORT_NAME = 'Copy of Market Monitor'; // Name of destination spreadsheet.

// Application settings
const CSV_HEADER_EXIST = true;  // Set to true if CSV files have a header row, false if not.
const HANDLER_FUNCTION = 'updateApplicationSheet'; // Function called by installable trigger to run data processing.

/**
 * Installs a time-driven trigger that runs daily to import CSVs into the main application spreadsheet.
 * Prior to creating a new instance, removes any existing triggers to avoid duplication.
 * 
 * Called by setupSample() or run directly setting up the application.
 */
function installTrigger() {

  // Checks for an existing trigger to avoid creating duplicate instances.
  // Removes existing if found.
  const projectTriggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < projectTriggers.length; i++) {
    if (projectTriggers[i].getHandlerFunction() == HANDLER_FUNCTION) {
      console.log(`Existing trigger with Handler Function of '${HANDLER_FUNCTION}' removed.`);
      ScriptApp.deleteTrigger(projectTriggers[i]);
    }
  }
  // Creates the new trigger.
  let newTrigger = ScriptApp.newTrigger(HANDLER_FUNCTION)
    .timeBased()
    .atHour(23)   // Runs at 11 PM in the time zone of this script.
    .everyDays(1) // Runs once per day.
    .create();
  console.log(`New trigger with Handler Function of '${HANDLER_FUNCTION}' created.`);
}

/**
 * Handler function called by the trigger created with the "installTrigger" function.
 * Run this directly to execute the entire automation process of the application with a trigger.
 * 
 * Process: Iterates through CSV files located in the source folder (SOURCE_FOLDER),
 * and appends them to the end of destination spreadsheet (SHEET_REPORT_NAME).
 * Successfully processed CSV files are moved to the processed folder (PROCESSED_FOLDER) to avoid duplication.
 * Sends summary email with status of the import.
 */
function updateApplicationSheet() {

  // Gets application & supporting folders.
  const folderAppPrimary = getApplicationFolder_(APP_FOLDER);
  const folderSource = getFolder_(SOURCE_FOLDER);
  const folderProcessed = getFolder_(PROCESSED_FOLDER);

  // Gets the application's destination spreadsheet {Spreadsheet object}
  let objSpreadSheet = getSpreadSheet_(SHEET_REPORT_NAME, folderAppPrimary)

  // Creates arrays to track every CSV file, categorized as processed sucessfully or not.
  let filesProcessed = [];
  let filesNotProcessed = [];

  // Gets all CSV files found in the source folder.
  let cvsFiles = folderSource.getFilesByType(MimeType.CSV);

  // Iterates through each CSV file.
  while (cvsFiles.hasNext()) {

    let csvFile = cvsFiles.next();
    let isSuccess;

    // Appends the unprocessed CSV data into the Google Sheets spreadsheet.
    isSuccess = processCsv_(objSpreadSheet, csvFile);

    if (isSuccess) {
      // Moves the processed file to the processed folder to prevent future duplicate data imports.
      csvFile.moveTo(folderProcessed);
      // Logs the successfully processed file to the filesProcessed array.
      filesProcessed.push(csvFile.getName());
      console.log(`Successfully processed: ${csvFile.getName()}`);

    } else if (!isSuccess) {
      // Doesn't move the unsuccesfully processed file so that it can be corrected and reprocessed later.
      // Logs the unsuccessfully processed file to the filesNotProcessed array.
      filesNotProcessed.push(csvFile.getName());
      console.log(`Not processed: ${csvFile.getName()}`);
    }
  }
  
  // Prepares summary email.
  // Gets variables to link to this Apps Script project.
  const scriptId = ScriptApp.getScriptId();
  const scriptUrl = DriveApp.getFileById(scriptId).getUrl();
  const scriptName = DriveApp.getFileById(scriptId).getName();

  // Gets variables to link to the main application spreadsheet.
  const sheetUrl = objSpreadSheet.getUrl()
  const sheetName = objSpreadSheet.getName()   

  // Gets user email and timestamp.
  const emailTo = Session.getEffectiveUser().getEmail();
  const timestamp = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm:ss zzzz");

  // Prepares lists and counts of processed CSV files.
  let processedList = "";
  const processedCount = filesProcessed.length
  for (const processed of filesProcessed) {
    processedList += processed + '<br>'
  };

  const unProcessedCount = filesNotProcessed.length
  let unProcessedList = "";
  for (const unProcessed of filesNotProcessed) {
    unProcessedList += unProcessed + '\n'
  };

  // Assembles email body as html.
  const eMailBody = `${APP_TITLE} ran an automated process at ${timestamp}.<br><br>` +
    `<b>Files successfully updated:</b> ${processedCount}<br>` +
    `${processedList}<br>` +
    `<b>Files not updated:</b> ${unProcessedCount}<br>` +
    `${unProcessedList}<br>` +
    `<br>View all updates in the Google Sheets spreadsheet ` +
    `<b><a href= "${sheetUrl}" target=\"_blank\">${sheetName}</a></b>.<br>` +
    `<br>*************<br>` +
    `<br>This email was generated by Google Apps Script. ` +
    `To learn more about this application or make changes, open the script project below: <br>` +
    `<a href= "${scriptUrl}" target=\"_blank\">${scriptName}</a>`

  MailApp.sendEmail({
    to: emailTo,
    subject: `Automated email from ${APP_TITLE}`,
    htmlBody: eMailBody
  });
  console.log(`Email sent to ${emailTo}`);
}

/**
 * Parses CSV data into an array and appends it after the last row in the destination spreadsheet.
 * 
 * @return {boolean} true if the update is successful, false if unexpected errors occur.
 */
function processCsv_(objSpreadSheet, csvFile) {

  try {
    // Gets the first sheet of the destination spreadsheet.
    let sheet = objSpreadSheet.getSheets()[0];

    // Parses CSV file into data array.
    let data = Utilities.parseCsv(csvFile.getBlob().getDataAsString());

    // Omits header row if application variable CSV_HEADER_EXIST is set to 'true'.
    if (CSV_HEADER_EXIST) {
      data.splice(0, 1);
    }
    // Gets the row and column coordinates for next available range in the spreadsheet. 
    let startRow = sheet.getLastRow() + 1;
    let startCol = 1;
    // Determines the incoming data size.
    let numRows = data.length;
    let numColumns = data[0].length;

    // Appends data into the sheet.
    sheet.getRange(startRow, startCol, numRows, numColumns).setValues(data);
    return true; // Success.

  } catch {
    return false; // Failure. Checks for CSV data file error.
  }
}

它是最后一个函数 processCsv_() 尝试更改行:

let sheet = objSpreadSheet.getSheets()[0];

与:

let sheet = objSpreadSheet.getSheetByName(csvFile.getName());

更新

如果你想替换工作表上的所有数据(而不是追加),可能你可以通过更改此行(在相同的函数 processCsv_() 中)来实现:

sheet.getRange(startRow, startCol, numRows, numColumns).setValues(data);

这样:

sheet.clearContents().getRange(1, startCol, numRows, numColumns).setValues(data);