├── Code.gs ├── appsscript.json ├── CustomFunction.js ├── GetMaps.gs ├── ClassroomData.js ├── PodcastGenAI ├── Sidebar.html ├── Script.js └── audio.py ├── LimitResponses.js ├── youtubePlaylist.js ├── PaLM.js ├── GetClassroomData.js ├── PaLM-GAPS.gs ├── ExtractCellNotes.js ├── ExtractURL.js ├── Timestamp.js ├── Bard_Lang.js ├── GetEvents_CalendarAPI.js ├── ApplyLabels.js ├── DriveGAPS.js ├── Create_Events.js ├── ChangeCellColour.js ├── ChatGPT.gs ├── FiltersGoogleSheets.js ├── SendCharts.js ├── YouTube_DataAPI.js ├── GoogleMapsDistance.gs ├── GeminiAPI.js ├── SendBulkEmails.js ├── BulkEmails.js ├── BlogSummaryPaLM.js ├── BlogSummaryAI.gs ├── BlogSummaryDoc.gs ├── AddGoogleMeet.js ├── file.html ├── SendPDF.js ├── CreateBulkClassroom.js ├── DocsAgentADK ├── code.js └── AIVertex.js ├── SendEmail.js ├── ServiceAccount.js ├── README.md ├── Renewal.js ├── GeminiDataExtract.js ├── PDF.js ├── Fetch&SummariseDocComments.js ├── LabelConfig.js ├── DriveLabelAutomation.js └── DriveLabelsAutomation.js /Code.gs: -------------------------------------------------------------------------------- 1 | function doGet(e) { 2 | 3 | var htmlOutput = HtmlService.createTemplateFromFile('file'); 4 | htmlOutput.search = ''; 5 | return htmlOutput.evaluate(); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Asia/Kolkata", 3 | "dependencies": {}, 4 | "exceptionLogging": "STACKDRIVER", 5 | "runtimeVersion": "V8", 6 | "webapp": { 7 | "executeAs": "USER_DEPLOYING", 8 | "access": "MYSELF" 9 | } 10 | } -------------------------------------------------------------------------------- /CustomFunction.js: -------------------------------------------------------------------------------- 1 | // This code is wriiten in Google Apps Script(JavaScript) 2 | // This code adds timestamp to Google Sheets using custom functions. 3 | // To add a Timestamp to a cell, use the name of the function and pass the parameter. 4 | 5 | 6 | function SetTimestamp(x) { 7 | 8 | if(x != ""){ 9 | 10 | return new Date(); 11 | 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /GetMaps.gs: -------------------------------------------------------------------------------- 1 | function GetDirection(){ 2 | 3 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 4 | var sheet = ss.getSheetByName("Sheet1"); 5 | 6 | var start = sheet.getRange("A2").getValue(); 7 | var end = sheet.getRange("B2").getValue(); 8 | 9 | 10 | var directions = Maps.newDirectionFinder() 11 | .setOrigin(start) 12 | .setDestination(end) 13 | .setMode(Maps.DirectionFinder.Mode.DRIVING) 14 | .getDirections(); 15 | 16 | Logger.log(directions.routes[0].legs[0].duration.text); 17 | Logger.log(directions.routes[0].legs[0].distance.text); 18 | } 19 | -------------------------------------------------------------------------------- /ClassroomData.js: -------------------------------------------------------------------------------- 1 | function getData(){ 2 | 3 | 4 | const classdata = Classroom.Courses.list().courses; 5 | 6 | //console.log(data); 7 | 8 | 9 | const data = classdata.map(c => { 10 | 11 | 12 | return [c.name, c.section,c.room,c.description,c.enrollmentCode]; 13 | 14 | }); 15 | 16 | 17 | const ss = SpreadsheetApp.getActiveSpreadsheet(); 18 | const sheet = ss.getSheetByName("Sheet1"); 19 | 20 | const START_ROW = 2; 21 | const START_COLUMN = 1; 22 | 23 | sheet.getRange(START_ROW,START_COLUMN,data.length,data[0].length).setValues(data); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /PodcastGenAI/Sidebar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Generate Content

8 |

Select the text you want to create a script out of

9 | 10 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /LimitResponses.js: -------------------------------------------------------------------------------- 1 | // This code is written in Google Apps Script(JavaScript) 2 | // This codes limit responses in Google Forms 3 | // As soon as the form responses reaches the limit, it closes the Google Form 4 | 5 | function Limit_Responses() { 6 | 7 | max_responses = 5;// Maximum number of responses you want 8 | var form = FormApp.openById("Your Form ID ");// Get the form by ID 9 | var responses = form.getResponses();// Get the responses 10 | len = responses.length;// Get the length of the responses 11 | //Logger.log(len); 12 | if (len == max_responses){ 13 | form.setAcceptingResponses(false); // Close the form and stop the responses 14 | 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /youtubePlaylist.js: -------------------------------------------------------------------------------- 1 | function addvideos() { 2 | 3 | const ss = SpreadsheetApp.getActiveSpreadsheet(); 4 | 5 | const sheet = ss.getActiveSheet(); 6 | 7 | const START_ROW = 2; 8 | 9 | const START_COLUMN = 1; 10 | 11 | const video_ID = sheet.getRange(START_ROW,START_COLUMN,sheet.getLastRow()).getValues().flat([1]); 12 | 13 | const playlistId = "PL36VXnx9Wxva1nNai3erO1Hpzuu-2zwES"; 14 | 15 | video_ID.forEach( vid => 16 | 17 | YouTube.PlaylistItems.insert({ 18 | 19 | snippet: { 20 | 21 | playlistId: playlistId, 22 | 23 | resourceId: { 24 | 25 | kind: "youtube#video", 26 | 27 | videoId: vid 28 | 29 | } 30 | } 31 | }, "snippet")); 32 | } 33 | -------------------------------------------------------------------------------- /PaLM.js: -------------------------------------------------------------------------------- 1 | function BARD(prompt) { 2 | var apiKey = "your_api_key"; 3 | var apiUrl = "https://generativelanguage.googleapis.com/v1beta2/models/text-bison-001:generateText"; 4 | var url = apiUrl + "?key=" + apiKey; 5 | var headers = { 6 | "Content-Type": "application/json" 7 | }; 8 | var requestBody = { 9 | "prompt": { 10 | "text": prompt 11 | } 12 | }; 13 | var options = { 14 | "method": "POST", 15 | "headers": headers, 16 | "payload": JSON.stringify(requestBody) 17 | }; 18 | var response = UrlFetchApp.fetch(url, options); 19 | var data = JSON.parse(response.getContentText()); 20 | var output = data.candidates[0].output; 21 | //return generatedText; 22 | Logger.log(output); 23 | return output; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /GetClassroomData.js: -------------------------------------------------------------------------------- 1 | //This code is written in Google Apps Script 2 | // This code gets all the data in the Google Classroom into your Google Sheet using Google Apps Script and the Google Classroom API. 3 | 4 | function getData(){ 5 | 6 | 7 | const CLASS_DATA = Classroom.Courses.list().courses; 8 | 9 | //console.log(CLASS_DATA); 10 | 11 | 12 | const DATA = CLASS_DATA.map(c => { 13 | 14 | return [c.name, c.section,c.room,c.description,c.enrollmentCode]; 15 | }); 16 | 17 | 18 | const ss = SpreadsheetApp.getActiveSpreadsheet(); 19 | const sheet = ss.getSheetByName("Sheet1"); 20 | 21 | const START_ROW = 2; 22 | const START_COLUMN = 1 23 | 24 | sheet.getRange(START_ROW,START_COLUMN,DATA.length,DATA[0].length).setValues(DATA); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /PaLM-GAPS.gs: -------------------------------------------------------------------------------- 1 | function getPalmData() { 2 | var apiKey = "your_api_key"; 3 | var apiUrl = "https://generativelanguage.googleapis.com/v1beta2/models/text-bison-001:generateText"; 4 | 5 | var url = apiUrl + "?key=" + apiKey; 6 | 7 | var headers = { 8 | "Content-Type": "application/json" 9 | }; 10 | 11 | var prompt = { 12 | "text": "Write a story about a magic backpack" 13 | }; 14 | 15 | var requestBody = { 16 | "prompt": prompt 17 | }; 18 | 19 | var options = { 20 | "method": "POST", 21 | "headers": headers, 22 | "payload": JSON.stringify(requestBody) 23 | }; 24 | 25 | var response = UrlFetchApp.fetch(url, options); 26 | var data = JSON.parse(response.getContentText()); 27 | 28 | Logger.log(data); 29 | } 30 | 31 | -------------------------------------------------------------------------------- /ExtractCellNotes.js: -------------------------------------------------------------------------------- 1 | // This code is written in Google Apps Script 2 | // This code extract cell notes and pastes them in the adjacent cell 3 | // This makes it easier for the user to read the cell notes 4 | 5 | function getNote() 6 | { 7 | // Get the sheet 8 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 9 | var sheet = ss.getActiveSheet(); 10 | // Get the data from the sheet 11 | var range = sheet.getDataRange(); 12 | // Get the notes from the sheet 13 | var notes = range.getNotes(); 14 | 15 | for(var i = 0;i < notes.length;i++){ 16 | for (var j = 0; j < notes[0].length; j++){ 17 | // If note is not empty 18 | if(notes[i][j]){ 19 | var note = notes[i][j]; 20 | var cell = range.getCell(i+1,j+1); 21 | cell.offset(0,1).setValue(note); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ExtractURL.js: -------------------------------------------------------------------------------- 1 | function extractAndPasteLinks() { 2 | 3 | var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); 4 | 5 | // Get the number of rows with data in column B 6 | var lastRow = sheet.getLastRow(); 7 | 8 | for (var i = 2; i <= lastRow; i++) { 9 | var cell = sheet.getRange(i, 2); 10 | var richTextValue = cell.getRichTextValue(); // Get the rich text value 11 | 12 | if (richTextValue) { 13 | var runs = richTextValue.getRuns(); // Get the runs (segments of text) 14 | for (var j = 0; j < runs.length; j++) { 15 | var url = runs[j].getLinkUrl(); // Get the URL from the text run 16 | if (url) { 17 | sheet.getRange(i, 3).setValue(url); // Paste the URL in Column C 18 | break; // Stop after the first URL is found 19 | } 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Timestamp.js: -------------------------------------------------------------------------------- 1 | // This code is wriiten in Google Apps Script(JavaScript) 2 | // This code adds timestamp to Google Sheets as soon as a change is made to a specified column 3 | function onEdit(e){ 4 | 5 | const row = e.range.getRow(); 6 | const col = e.range.getColumn(); 7 | 8 | 9 | const sheetName = "Sheet1"; 10 | if (col === 1 && row > 1 && e.source.getActiveSheet().getName() === "Sheet1"){ 11 | 12 | const currentDate = new Date(); 13 | e.source.getActiveSheet().getRange(row,4).setValue(currentDate); 14 | const name = Session.getActiveUser().getEmail(); 15 | 16 | e.source.getActiveSheet().getRange(row,5).setValue(name) 17 | 18 | if(e.source.getActiveSheet().getRange(row,3).getValue() == ""){ 19 | 20 | e.source.getActiveSheet().getRange(row,3).setValue(currentDate); 21 | 22 | } 23 | 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Bard_Lang.js: -------------------------------------------------------------------------------- 1 | function BARD(sentence,origin_language,target_lanugage) { 2 | var apiKey = "your_api_key"; 3 | var apiUrl = "https://generativelanguage.googleapis.com/v1beta2/models/text-bison-001:generateText"; 4 | var url = apiUrl + "?key=" + apiKey; 5 | var headers = { 6 | "Content-Type": "application/json" 7 | }; 8 | 9 | var prompt = { 10 | 11 | 'text': "Convert this sentence"+ sentence + "from"+origin_language + "to"+target_lanugage 12 | 13 | } 14 | 15 | var requestBody = { 16 | 17 | "prompt": prompt 18 | } 19 | 20 | 21 | var options = { 22 | "method": "POST", 23 | "headers": headers, 24 | "payload": JSON.stringify(requestBody) 25 | }; 26 | var response = UrlFetchApp.fetch(url, options); 27 | var data = JSON.parse(response.getContentText()); 28 | var output = data.candidates[0].output; 29 | Logger.log(output); 30 | return output; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /GetEvents_CalendarAPI.js: -------------------------------------------------------------------------------- 1 | function getEvents(){ 2 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 3 | var sheet = ss.getSheetByName("GetEvents"); 4 | 5 | var cal = CalendarApp.getCalendarById("aryanirani123@gmail.com"); 6 | var events = cal.getEvents(new Date("6/27/2021 12:00 AM"), new Date("6/30/2021 11:59 PM")); 7 | 8 | for(var i = 0;i{ 11 | 12 | all_labels.push(label.getName()); 13 | 14 | }) 15 | 16 | Logger.log(all_labels); 17 | 18 | let tempLabel; 19 | 20 | if(all_labels.includes(myLabel)){ 21 | tempLabel = GmailApp.getUserLabelByName(myLabel); 22 | } 23 | 24 | else{ 25 | GmailApp.createLabel(myLabel) 26 | } 27 | 28 | Logger.log(tempLabel) 29 | 30 | threads.forEach(function(message){ 31 | 32 | //Logger.log(message); 33 | const first_message = message.getFirstMessageSubject(); 34 | if(first_message.includes("Stats for your stories")){ 35 | 36 | message.addLabel(tempLabel); 37 | //message.moveToArchive(); 38 | } 39 | }) 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /DriveGAPS.js: -------------------------------------------------------------------------------- 1 | const fileId = "your_file_id"; 2 | 3 | function getUsers() { 4 | const file = DriveApp.getFileById(fileId); 5 | const editors = file.getEditors(); 6 | const viewers = file.getViewers(); 7 | 8 | const userEmails = []; 9 | 10 | // Extract emails from editors 11 | if (editors.length > 0) { 12 | editors.forEach(editor => { 13 | const email = editor.getEmail(); 14 | if (email) { 15 | userEmails.push(email); 16 | } 17 | }); 18 | } 19 | 20 | // Extract emails from viewers 21 | if (viewers.length > 0) { 22 | viewers.forEach(viewer => { 23 | const email = viewer.getEmail(); 24 | if (email) { 25 | userEmails.push(email); 26 | } 27 | }); 28 | } 29 | 30 | if (userEmails.length > 0) { 31 | Logger.log("Users with access:"); 32 | userEmails.forEach(email => Logger.log(email)); 33 | } else { 34 | Logger.log("No editors or viewers found for this file."); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Create_Events.js: -------------------------------------------------------------------------------- 1 | function create_Events(){ 2 | 3 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 4 | var sheet = ss.getSheetByName("Calendar_Events"); 5 | var last_row = sheet.getLastRow(); 6 | var data = sheet.getRange("A1:E" + last_row).getValues(); 7 | var cal = CalendarApp.getCalendarById("aryanirani123@gmail.com"); 8 | //Logger.log(data); 9 | 10 | for(var i = 0;i< data.length;i++){ 11 | // index 0 = Name of the Event 12 | // index 1 = Start Time of the Event 13 | // index 2 = End Time of the Event 14 | // index 3 = Location of the Event 15 | // index 4 = Description of the Event 16 | // index 5 = Attendess of the Event 17 | 18 | var Events = cal.createEvent(data[i][0], 19 | new Date(data[i][1]), 20 | new Date(data[i][2]), 21 | {location: data[i][3], description : data[i][4]}); 22 | //Logger.log('Event ID: ' + event.getId()); 23 | 24 | } 25 | Logger.log("Events have been added to the calendar"); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /ChangeCellColour.js: -------------------------------------------------------------------------------- 1 | //This code is written in Google Apps Script(JavaScript). 2 | //This code changes the colours of cells in Google Sheets on meeting the given criteria. 3 | //In this example, if the number in the cell is greater than 3, the cell colour to green. 4 | 5 | function ChangeColor(){ 6 | 7 | const sheet = SpreadsheetApp.getActiveSheet(); 8 | const range = sheet.getRange("D1:D10"); 9 | const rule = SpreadsheetApp.newConditionalFormatRule() 10 | .whenNumberLessThan(4) 11 | .setBackground("red") 12 | .setRanges([range]) 13 | .build(); 14 | var rules = sheet.getConditionalFormatRules(); 15 | rules.push(rule); 16 | sheet.setConditionalFormatRules(rules); 17 | 18 | var rule = SpreadsheetApp.newConditionalFormatRule() 19 | .whenNumberGreaterThan(3) 20 | .setBackground("green") 21 | .setRanges([range]) 22 | .build(); 23 | var rules = sheet.getConditionalFormatRules(); 24 | rules.push(rule); 25 | sheet.setConditionalFormatRules(rules); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /ChatGPT.gs: -------------------------------------------------------------------------------- 1 | / Replace with your OpenAI API key 2 | var API_KEY = "your_api_key"; 3 | 4 | 5 | function GPT(prompt) { 6 | 7 | var Model_ID = "text-davinci-002"; 8 | var maxTokens = 64; 9 | var temperature = 0.5; 10 | // Build the API payload 11 | var payload = { 12 | 'model': 'text-davinci-002', // or your desired model 13 | 'prompt': prompt, 14 | 'temperature': temperature, 15 | 'max_tokens': maxTokens, 16 | }; 17 | 18 | // Build the API options 19 | var options = { 20 | "method": "post", 21 | "headers": { 22 | "Content-Type": "application/json", 23 | "Authorization": "Bearer " + API_KEY 24 | }, 25 | "payload": JSON.stringify(payload) 26 | }; 27 | 28 | // Make the API request 29 | var response = UrlFetchApp.fetch("https://api.openai.com/v1/completions", options); 30 | 31 | // Parse the response and return the generated text 32 | var responseData = JSON.parse(response.getContentText()); 33 | return responseData.choices[0].text.trim(); 34 | } 35 | -------------------------------------------------------------------------------- /FiltersGoogleSheets.js: -------------------------------------------------------------------------------- 1 | // This code is written in Google Apps Script(JavaScript) 2 | // This code creates Filters in Google Sheets\ 3 | // The filter criterias are specified in the code 4 | 5 | 6 | function create_filter(){ 7 | 8 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 9 | var sheet1 = ss.getSheetByName("Filter_Sheet"); 10 | var range = sheet1.getRange("A:D"); 11 | 12 | var filter = range.createFilter(); 13 | 14 | var Filter_Criteria1 = SpreadsheetApp.newFilterCriteria().whenNumberGreaterThan(500); 15 | var Filter_Criteria2 = SpreadsheetApp.newFilterCriteria().whenTextContains(['India/Mumbai']); 16 | 17 | var add_filter1 =filter.setColumnFilterCriteria(3,Filter_Criteria1); 18 | var add_filter2 = filter.setColumnFilterCriteria(4,Filter_Criteria2); 19 | 20 | Logger.log("Filter has been added."); 21 | 22 | var range = sheet1.getDataRange(); 23 | 24 | var new_sheet = ss.insertSheet(); 25 | new_sheet.setName("India/Mumbai Data"); 26 | 27 | range.copyTo(new_sheet.getRange(1,1)); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /SendCharts.js: -------------------------------------------------------------------------------- 1 | // This code is written in Google Apps Script(JavaScript) 2 | // This code sends out charts from Google Sheets 3 | // Charts are created in Google Sheets,using Google Apps Script the charts will be emailed to the specified email. 4 | 5 | function emailChartSourceImage(){ 6 | const sheet = SpreadsheetApp.getActiveSheet(); 7 | const charts = sheet.getCharts(); 8 | 9 | // setup some variables for our email 10 | const chartBlobs = new Array(); 11 | const emailImages = {}; 12 | let emailBody = "Charts
"; 13 | 14 | charts.forEach(function(chart, i){ 15 | chartBlobs[i] = chart.getAs("image/png"); 16 | emailBody += "

"; // Alligning the chart to the center of the body in the email 17 | emailImages["chart"+i] = chartBlobs[i]; 18 | }); 19 | 20 | MailApp.sendEmail({ 21 | to: "aryanirani123@gmail.com", 22 | subject: "Chart for average marks per subject", 23 | htmlBody: emailBody, 24 | inlineImages:emailImages}); 25 | } 26 | -------------------------------------------------------------------------------- /YouTube_DataAPI.js: -------------------------------------------------------------------------------- 1 | function getYoutube(){ 2 | 3 | var sheet = SpreadsheetApp.getActiveSheet(); 4 | var range = sheet.getRange(2,1, sheet.getLastRow() - 1); 5 | var vids = range.getValues(); 6 | //Logger.log(vids); 7 | 8 | var links = vids.map(function(res){ return res[0];}).join(","); 9 | //Logger.log(links); 10 | 11 | var stats_vids = YouTube.Videos.list("snippet, statistics",{id: links}); 12 | //Logger.log(stats_vids.items); 13 | 14 | var details = stats_vids.items.map(function(s){return [s.snippet.title];}); 15 | sheet.getRange(2,2, details.length,details[0].length).setValues(details); 16 | //Logger.log(details); 17 | 18 | Logger.log("Title has been added"); 19 | 20 | var final_stats = stats_vids.items.map(function(res){ return [res.statistics.viewCount,res.statistics.likeCount,res.statistics.dislikeCount]}); 21 | //Logger.log(final_stats); 22 | 23 | sheet.getRange(2,3, final_stats.length,final_stats[0].length).setValues(final_stats); 24 | Logger.log("Stats have been added"); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /GoogleMapsDistance.gs: -------------------------------------------------------------------------------- 1 | function calculateDistances() { 2 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 3 | var sheet = ss.getSheetByName("Sheet1"); 4 | var numRows = sheet.getDataRange().getNumRows(); 5 | var data = sheet.getRange("A2:B" + numRows).getValues(); 6 | var status_column = 5; 7 | 8 | for (var i = 0; i < data.length; i++) { 9 | var start = data[i][0]; 10 | var end = data[i][1]; 11 | var flag = data[i][4]; 12 | 13 | 14 | if (start && end && flag !== "done") { 15 | var directions = Maps.newDirectionFinder() 16 | .setOrigin(start) 17 | .setDestination(end) 18 | .setMode(Maps.DirectionFinder.Mode.DRIVING) 19 | .getDirections(); 20 | 21 | var distance = directions.routes[0].legs[0].distance.text; 22 | var duration = directions.routes[0].legs[0].duration.text; 23 | 24 | sheet.getRange(i+2, 3).setValue(distance); 25 | sheet.getRange(i+2, 4).setValue(duration); 26 | sheet.getRange(i+2, 5).setValue("done"); 27 | 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GeminiAPI.js: -------------------------------------------------------------------------------- 1 | function onFormSubmit(e){ 2 | 3 | var reviewtext = e.namedValues["Describe your Experience "]; 4 | var apiKey = "your_api_key"; 5 | var apiUrl = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent" 6 | var url = apiUrl +"?key="+apiKey; 7 | 8 | var headers = { 9 | "Content-Type": "application/json" 10 | 11 | }; 12 | 13 | var requestBody = { 14 | "contents": [ 15 | { 16 | "parts": [ 17 | { 18 | "text": "Identify the items that the reviewer liked and did not like. Please categorise them in different lists, nicely formatted"+reviewtext 19 | } 20 | ] 21 | } 22 | ] 23 | }; 24 | var options = { 25 | 26 | "method": "POST", 27 | "headers": headers, 28 | "payload": JSON.stringify(requestBody) 29 | }; 30 | 31 | var response = UrlFetchApp.fetch(url,options); 32 | var data = JSON.parse(response.getContentText()); 33 | var output = data.candidates[0].content.parts[0].text 34 | Logger.log(output); 35 | 36 | 37 | 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /SendBulkEmails.js: -------------------------------------------------------------------------------- 1 | // This Code is written in Google Apps Script 2 | // This code sends out Bulk Emails 3 | // The Data for Bulk emails is being extracted from two Google Sheets. 4 | // The First Google Sheet contains the data to be sent, and the second Google Sheets contains the email addresses of the recipients. 5 | 6 | 7 | 8 | function sendEmail() { 9 | 10 | // Get handle to active spreadsheet and sheet 11 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 12 | var responses1 = ss.getSheetByName("Sheet1"); 13 | var responses2 = ss.getSheetByName("Sheet2"); 14 | 15 | // Getting data from the sheets 16 | var data1 = responses1.getRange(2,1,2,3).getValues(); // Getting the subject, body and the footer from the sheet 17 | var data2 = responses2.getRange(2,3,responses2.getLastRow() - 1,4).getValues(); // Getting the email address of the residents 18 | 19 | //Row 1 and Col 1 20 | var EmailSubject = data1[0][0]; 21 | var EmailBody = data1[0][1]; 22 | var EmailFooter = data1[0][2]; 23 | 24 | data2.forEach(function (row,i) { 25 | MailApp.sendEmail(row[1],EmailSubject,'Dear ' + row[0] + ',\n\n' + EmailBody + '\n\n' + EmailFooter); 26 | }); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /BulkEmails.js: -------------------------------------------------------------------------------- 1 | function SendEmail(){ 2 | 3 | const ss = SpreadsheetApp.getActiveSpreadsheet(); 4 | const sheet = ss.getSheetByName("Student Details"); 5 | 6 | var data = sheet.getRange(2,1,sheet.getLastRow() - 1,2).getValues(); 7 | //Logger.log(data); 8 | 9 | 10 | data.forEach(function(row,i){ 11 | 12 | var name = row[0]; 13 | var email = row[1]; 14 | Logger.log(name) 15 | Logger.log(email) 16 | 17 | 18 | 19 | var body = "Dear "+ name + "

"+ 20 | 21 | " It was noted with serious concern that quite a few students were detained last semester on account of attendance default. Despite monthly attendance information" + "

"+ 22 | "being provided to students and regular counselling by faculty members, it is found that students do not take the attendance rules seriously. This results in losing an academic year affecting career" + "

"+ 23 | "prospects after graduation. It is also noted that students do not read the SRB and are not aware of the various rules and regulations and the repercussions of non-compliance."+ ",

" + 24 | "Regards,"+ "

"+ 25 | " XYZ University " + "

"; 26 | 27 | 28 | var subject = "Student Attendance"; 29 | //GmailApp.sendEmail(email, subject, "", { htmlBody: body } ); 30 | 31 | }); 32 | 33 | 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /BlogSummaryPaLM.js: -------------------------------------------------------------------------------- 1 | function onOpen(){ 2 | 3 | var ui = DocumentApp.getUi(); 4 | ui.createMenu('Custom Menu') 5 | .addItem('Summarize Selected Paragraph', 'summarizeSelectedParagraph') 6 | .addToUi(); 7 | 8 | } 9 | 10 | function DocSummary(paragraph){ 11 | 12 | var apiKey = "your_api_key"; 13 | var apiUrl = "https://generativelanguage.googleapis.com/v1beta2/models/text-bison-001:generateText"; 14 | 15 | var url = apiUrl + "?key=" + apiKey; 16 | 17 | var headers = { 18 | 19 | "Content-Type": "application/json" 20 | } 21 | 22 | var prompt = { 23 | 24 | 'text': "Please generate a short summary for :\n" + paragraph 25 | } 26 | 27 | var requestBody = { 28 | "prompt": prompt 29 | } 30 | 31 | var options = { 32 | 33 | "method": "POST", 34 | "headers": headers, 35 | "payload": JSON.stringify(requestBody) 36 | 37 | } 38 | 39 | var response = UrlFetchApp.fetch(url,options); 40 | var data = JSON.parse(response.getContentText()); 41 | return data.candidates[0].output; 42 | 43 | 44 | } 45 | 46 | function summarizeSelectedParagraph(){ 47 | 48 | var selection = DocumentApp.getActiveDocument().getSelection(); 49 | var text = selection.getRangeElements()[0].getElement().getText(); 50 | var summary = DocSummary(text); 51 | DocumentApp.getActiveDocument().getBody().appendParagraph("Summary"); 52 | DocumentApp.getActiveDocument().getBody().appendParagraph(summary) 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /BlogSummaryAI.gs: -------------------------------------------------------------------------------- 1 | var API_key = "your_api_key"; 2 | 3 | function BlogSummary(){ 4 | 5 | var Model_ID = "text-davinci-002"; 6 | var maxtokens = 200; 7 | var temperature = 0.7; 8 | 9 | 10 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 11 | var sheet = ss.getSheetByName("Sheet1"); 12 | var urls = sheet.getRange("A2:A6").getValues() 13 | 14 | for(var i=0; i 2 | 3 | 4 | Countdown Timer 5 | 20 | 21 | 22 |
23 | 53 | 54 | -------------------------------------------------------------------------------- /SendPDF.js: -------------------------------------------------------------------------------- 1 | // This code is written in Google Apps Script(JavaScript) 2 | // This code sends out automated pdfs on email to the people who have registered for the Event. 3 | // The Google Sheet contains details on each of the student for whom we have to create ID cards 4 | // On running the code the ID cards will automatically get created and send it to the email address of the student. 5 | 6 | 7 | 8 | function After_Submit(e){ 9 | 10 | const info = e.namedValues; 11 | const pdfFile = Create_PDF(info); 12 | 13 | console.log(info); 14 | 15 | sendEmail(e.namedValues['Email Address'][0],pdfFile); 16 | } 17 | 18 | function sendEmail(email,pdfFile){ 19 | 20 | GmailApp.sendEmail(email, "ID CARD", "This is your ID Card.", { 21 | attachments: [pdfFile], 22 | name: "HEllO" 23 | 24 | }); 25 | 26 | } 27 | function Create_PDF(info) { 28 | 29 | const PDF_folder = DriveApp.getFolderById("1zx7rnI2M3p2U7RGTJugM_0G5aMINYTyh"); 30 | const TEMP_FOLDER = DriveApp.getFolderById("1jO1BHwhwkKbGFcyT8DAJsew2v0gjCI4W"); 31 | const PDF_Template = DriveApp.getFileById("1qHOMwuq2X_5LhUCfPLWcpUSh2n7pVRvHZ_kE-hsGmwg"); 32 | 33 | const newTempFile = PDF_Template.makeCopy(TEMP_FOLDER); 34 | const OpenDoc = DocumentApp.openById(newTempFile.getId()); 35 | const body = OpenDoc.getBody(); 36 | 37 | console.log(body); 38 | 39 | body.replaceText("{email}", info['Email Address'][0]) 40 | body.replaceText("{name}", info['Enter your name'][0]); 41 | body.replaceText("{roll}", info['Enter your Roll number'][0]); 42 | body.replaceText("{number}", info['Enter your phone number '][0]); 43 | body.replaceText("{DOB}", info['Enter you Date of Birth '][0]); 44 | body.replaceText("{BLOOD}", info['Enter your Blood Group [Eg: O negative]'][0]); 45 | 46 | OpenDoc.saveAndClose(); 47 | 48 | 49 | const BLOBPDF = newTempFile.getAs(MimeType.PDF); 50 | const pdfFile = PDF_folder.createFile(BLOBPDF).setName(info['Enter your name'][0] + " " + info['Enter your Roll number'][0]); 51 | console.log("The file has been created "); 52 | 53 | return pdfFile; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /CreateBulkClassroom.js: -------------------------------------------------------------------------------- 1 | /* 2 | This code is wriiten in Google Apps Script(JavaScript) 3 | This code creates nulk Google Classroom using Google Apps Script 4 | The Classes are created provided the following data is present in the Google Sheet 5 | 6 | (1) Name of the Class 7 | (2) Section of the Class 8 | (3) Room 9 | (4) Description of the Class 10 | 11 | */ 12 | OWNER_EMAIL = "aryanirani123@gmail.com"; //Don't forget to update this value with your email address :) 13 | 14 | function onOpen(){ 15 | const menu = SpreadsheetApp.getUi().createMenu('Custom'); 16 | menu.addItem('Create Classroom','createClasses'); 17 | menu.addToUi(); 18 | } 19 | function classroomData(properties){ 20 | 21 | const crs = Classroom.newCourse(); 22 | Object.keys(properties).forEach(key => { 23 | Crs[key] = properties[key]; 24 | }) 25 | const createdCourse = Classroom.Courses.create(crs); 26 | return createdCourse.enrollmentCode; 27 | } 28 | function createClasses(){ 29 | 30 | const ss = SpreadsheetApp.getActiveSpreadsheet(); 31 | const sheet = ss.getActiveSheet(); 32 | const START_ROW= 2; 33 | const START_COLUMN = 1; 34 | const LAST_COLUMN= 4; 35 | const data = sheet.getRange( 36 | START_ROW, 37 | START_COLUMN, 38 | sheet.getLastRow()-1, 39 | LAST_COLUMN).getValues(); 40 | const enrollmentCodes = []; 41 | const nameIndex = 0; 42 | const sectionIndex = 1; 43 | const roomIndex = 2 44 | const descriptionIndex = 3; 45 | const START_COLUMN = 5; 46 | const LAST_COLUMN = 1; 47 | data.forEach(row => { 48 | const eCode = classroomData({ 49 | name: row[nameIndex], 50 | section: row[sectionIndex], 51 | room: row[roomIndex], 52 | description: row[descriptionIndex], 53 | ownerId: OWNER_EMAIL 54 | }); 55 | enrollmentCodes.push([eCode]); 56 | sheet.getRange( 57 | START_ROW, 58 | COLUMN_START, 59 | enrollmentCodes.length, 60 | COLUMN_LAST).setValues(enrollmentCodes); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /DocsAgentADK/code.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adds a custom menu in Google Docs to run the AI Audit directly. 3 | */ 4 | function onOpen() { 5 | DocumentApp.getUi() 6 | .createMenu('AI Audit Tools') 7 | .addItem('Run AI Audit', 'runAuditAndInsert') 8 | .addToUi(); 9 | } 10 | 11 | /** 12 | * Returns the entire text content of the document. 13 | */ 14 | function getDocumentText() { 15 | const doc = DocumentApp.getActiveDocument(); 16 | const body = doc.getBody(); 17 | let text = body.getText(); 18 | return text.trim(); 19 | } 20 | 21 | /** 22 | * Runs the AI Audit and inserts the result as text at the end of the document. 23 | */ 24 | function runAuditAndInsert() { 25 | const docText = getDocumentText(); 26 | const result = runAudit(docText); 27 | 28 | if (result) { 29 | const doc = DocumentApp.getActiveDocument(); 30 | const body = doc.getBody(); 31 | // Append the audit result as a new paragraph at the end of the document 32 | body.appendParagraph('AI Audit Result: ' + result); 33 | } 34 | } 35 | 36 | /** 37 | * Runs the AI Audit using ADK Agent and Gemini formatting. 38 | */ 39 | function runAudit(docText) { 40 | console.log('[INFO] Starting AI audit process...'); 41 | if (!docText) { 42 | console.log('[WARN] No text in document.'); 43 | return '⚠️ The document is empty. Please add some text to audit.'; 44 | } 45 | 46 | // Check for excessive document length to avoid token limits 47 | if (docText.length > 10000) { 48 | console.log('[WARN] Document too long.'); 49 | return '⚠️ Document exceeds 10,000 characters. Please shorten the text.'; 50 | } 51 | 52 | console.log('[STEP] Sending text to ADK Agent...'); 53 | const rawAudit = requestLlmAuditorAdkAiAgent(docText); 54 | 55 | // Check if rawAudit is an error message 56 | if (rawAudit.startsWith('ERROR:')) { 57 | console.error('[ERROR] ADK Agent returned error:', rawAudit); 58 | return rawAudit; 59 | } 60 | 61 | console.log('[STEP] Formatting AI response...'); 62 | let formatted; 63 | try { 64 | formatted = requestOutputFormatting( 65 | `Here is a fact-checking result: ${rawAudit}. 66 | Summarize it. Keep the main verdict and reasoning. Remove markdown and make it concise.` 67 | ); 68 | } catch (error) { 69 | console.error('[ERROR] Formatting failed:', error.toString()); 70 | return `ERROR: Failed to format audit result - ${error.toString()}`; 71 | } 72 | 73 | console.log('[SUCCESS] Audit completed successfully.'); 74 | console.log('[RESULT] Final Output:', formatted); 75 | return formatted; 76 | } 77 | -------------------------------------------------------------------------------- /SendEmail.js: -------------------------------------------------------------------------------- 1 | // This code is written in Google Apps Script 2 | // This code takes data from a Google Sheet and send Automated Emails to the students. 3 | // Each email depends on the students grade. If a student recieves a bad grade, he will be sent a special email. 4 | // To know more, check out the blog link: https://aryanirani123.medium.com/google-sheets-automation-using-google-apps-script-60c107ff6bcd 5 | 6 | 7 | 8 | function sendEmail() { 9 | 10 | //Get the handle to the active spreadsheet & sheet. 11 | var ss = SpreadsheetApp.getActiveSpreadsheet(); 12 | var responses = ss.getSheetByName("Form Responses 1"); 13 | 14 | //Get the data starting from values (i.e. skip the header row) 15 | var data = responses.getRange(2,1,responses.getLastRow() - 1,6).getValues(); 16 | 17 | Logger.log(data); 18 | 19 | //Iterate each row of data 20 | data.forEach(function(row, i) { 21 | 22 | // variables 23 | var Marks = row[1]; // marks secured by the students 24 | var Name = row[2]; // Name of the student 25 | var Email_ID = row[3];// Email address of the student 26 | var Roll_no = row[4]; // Roll number of the student 27 | 28 | // only send reply if the replied column is still blank 29 | if (Marks <= 10){ 30 | // send an email for marks less than equal to 10 31 | var body = formatEmailBody("Bad",Name, Marks,Email_ID,Roll_no); 32 | } 33 | 34 | else if (Marks <10) { 35 | // send an email for marks greater than 10 36 | 37 | var body = formatEmailBody("Moderate",Name, Marks,Email_ID,Roll_no); 38 | } 39 | else if (Marks <= 15) { 40 | // send an email for marks greater than equal to 15 41 | var body = formatEmailBody("good",Name, Marks,Email_ID,Roll_no); 42 | } 43 | 44 | else if (Marks <= 20) { 45 | // send an email marks greater than equal to 20 46 | var body = formatEmailBody("Very Good",Name, Marks,Email_ID,Roll_no); 47 | } 48 | 49 | else if (Marks <= 25) { 50 | // send an email for marks greater than equal to 25 51 | var body = formatEmailBody("Great",Name, Marks,Email_ID,Roll_no); 52 | } 53 | 54 | var subject = "Report for Maths Test"; 55 | GmailApp.sendEmail(Email_ID, subject, "", { htmlBody: body } ); 56 | 57 | }); 58 | 59 | } 60 | 61 | function formatEmailBody(Rating, Name, Marks,Email_ID,Roll_no) { 62 | 63 | var emailTxt = "Teachers remarks: " + Rating + "

"+ 64 | 65 | " Name : " + Name + ",

" + 66 | " Score : " + Marks + "

" + 67 | " Email Address : " + Email_ID + "

" + 68 | " Roll Number : " + Roll_no + "

"; 69 | 70 | return emailTxt; 71 | 72 | } 73 | -------------------------------------------------------------------------------- /ServiceAccount.js: -------------------------------------------------------------------------------- 1 | // Save in Apps Script Properties or Secret Manager 2 | 3 | var CREDENTIALS = { 4 | private_key: "your_private_key" 5 | client_email: 'examplename@project2-283514.iam.gserviceaccount.com', 6 | }; 7 | 8 | function getOAuthService(user) { 9 | return OAuth2.createService("Service Account") 10 | .setTokenUrl('https://oauth2.googleapis.com/token') 11 | .setPrivateKey(CREDENTIALS.private_key) 12 | .setIssuer(CREDENTIALS.client_email) 13 | .setSubject(user) 14 | .setPropertyStore(PropertiesService.getScriptProperties()) 15 | .setParam('access_type', 'offline') 16 | .setScope('https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/spreadsheets') 17 | } 18 | 19 | function reset() { 20 | var service = getOAuthService(); 21 | service.reset(); 22 | } 23 | 24 | 25 | // Function to execute BigQuery query 26 | function executeQuery(query) { 27 | const service = getOAuthService(CREDENTIALS.client_email); 28 | if (!service.hasAccess()) { 29 | throw new Error('Authorization required. Check script permissions.'); 30 | } 31 | 32 | const request = { 33 | query: query, 34 | useLegacySql: false 35 | }; 36 | const projectId = 'project2-283514'; 37 | return BigQuery.Jobs.query(request, projectId); 38 | } 39 | 40 | // Function to process and log results 41 | function processQueryResults(queryResults, sheet, headers, startRow) { 42 | if (queryResults.rows && queryResults.rows.length > 0) { 43 | const data = queryResults.rows.map(row => row.f.map(cell => cell.v)); 44 | const lastRow = sheet.getLastRow(); 45 | 46 | // Clear previous data 47 | if (lastRow > startRow - 1) { 48 | sheet.getRange(startRow, 2, lastRow - startRow + 1, headers.length).clear(); 49 | } 50 | 51 | // Write headers and data 52 | sheet.getRange(1, 2, 1, headers.length).setValues([headers]); 53 | sheet.getRange(startRow, 2, data.length, headers.length).setValues(data); 54 | } else { 55 | Logger.log('No data found.'); 56 | } 57 | } 58 | 59 | // Fetch results for multiple IDs 60 | function FetchResults() { 61 | const spreadsheetId = 'your_spreadsheet_id'; 62 | const sheetName = 'your_sheet_name'; 63 | const timestampCell = 'your_timestamp_cell'; 64 | 65 | const spreadsheet = SpreadsheetApp.openById(spreadsheetId); 66 | const sheet = spreadsheet.getSheetByName(sheetName); 67 | const ids = sheet.getRange(2, 1, sheet.getLastRow() - 1, 1).getValues().flat(); 68 | 69 | if (ids.length === 0) { 70 | Logger.log('No IDs found in the sheet.'); 71 | return; 72 | } 73 | // This is an example query you can always customise it to your needs. 74 | const query = ` 75 | SELECT 76 | \`Test Name\`, 77 | \`Participants\`, 78 | \`Test Focus\` 79 | FROM \`project2-283514.invoices_dataset.FInalTry\` 80 | WHERE \`id\` IN (${ids.map(id => `'${id}'`).join(", ")}) 81 | ORDER BY \`Test Name\`; 82 | `; 83 | 84 | const queryResults = executeQuery(query); 85 | processQueryResults(queryResults, sheet, ['Test Name', 'Participants', 'Test Focus'], 2); 86 | sheet.getRange(timestampCell).setValue(`Last Updated: ${new Date()}`); 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google-Apps-Script 2 | Collection of Google Apps Script Automation scripts written and compiled by Aryan Irani. 3 | 4 | AddGoogleMeet.js - Create Calendar Events with a Google Meet link using Google Apps Script. The Calendar Events contain data from a Google Sheet. 5 | 6 | 7 | Blog Link : https://bit.ly/3bw8KsI 8 | Video Tutorial : https://www.youtube.com/watch?v=_pY9eK1V_JA 9 | 10 | ChangeCellColour.js - Changes colour of cells in Google Sheets using Google Apps Script.If a change is made in the Google Sheet, that meets the conditions 11 | mentioned in the script, the colour of the cell will be changed. 12 | 13 | 14 | Blog Link : https://bit.ly/3jSV5Rk 15 | Video Tutorial : will be out soon :) 16 | 17 | CreateBulkClassroom.js - Bulk create Google Classrooms, with custom data present in Google Sheets using Google Apps Script. After the classrooms are created, the 18 | class codes are pasted in the Google Sheet for further use. 19 | 20 | 21 | Blog Link : https://bit.ly/3nIkUVk 22 | Video tutorial : https://www.youtube.com/watch?v=YZQdkrJSVgQ&t=2s 23 | 24 | Create_Events.js - Create calendar events with data from Google Sheets using Google Apps Script. Bulk create calendar events wtih custom details for each event. 25 | 26 | 27 | Blog Link : https://bit.ly/3q0VwN4 28 | Video Tutorial : https://www.youtube.com/watch?v=DKYM8CYwSUE 29 | 30 | ExtractCellNotes.js - Extract cell notes from Google Sheets using Google Apps Script. Extract cell notes into the adjacent column using Google Apps Script. 31 | 32 | 33 | Blog Link : https://bit.ly/3bqgWuy 34 | Video Tutorial : https://www.youtube.com/watch?v=z0k0nNieL2o 35 | 36 | FiltersGoogleSheets.js - Create filters in Google Sheets using Google Apps Script. Instead of manually creating filters, automate the process using Google Apps Script, 37 | by just specifying the filter criteria. 38 | 39 | 40 | Blog Link : https://bit.ly/2ZB7gem 41 | Video Tutorial : https://www.youtube.com/watch?v=0dKQy0yakvI&t=139s 42 | 43 | GetEvents_CalendarAPI.js - Get calendar events with all the details from your Google Calendar into your Google Sheet using Google Apps Script. 44 | 45 | 46 | Blog Link : https://bit.ly/3pRoUW8 47 | Video tutorial : https://www.youtube.com/watch?v=YDhln3ukH7Y&t=2s 48 | 49 | LimitResponses.js - Limit responses in Google Forms, using Google Apps Script. 50 | 51 | 52 | Blog Link : https://bit.ly/317vQ78 53 | Video Tutorial :https://www.youtube.com/watch?v=Uee_7cZW0p0&t=2s 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Renewal.js: -------------------------------------------------------------------------------- 1 | function checkAndSendRenewalEmails() { 2 | const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1'); 3 | const data = sheet.getDataRange().getValues(); 4 | 5 | // Assuming 'Subscription End Date' is in column 6 (F), 'Name' is in column 2 (B), and 'User ID' is in column 0 (A) 6 | const EmailRange = sheet.getRange(3, 2, sheet.getLastRow() - 2, 1); 7 | const EndDateRange = sheet.getRange(3, 7, sheet.getLastRow() - 2, 1); 8 | const NameRange = sheet.getRange(3, 3, sheet.getLastRow() - 2, 1); 9 | const StatusRange = sheet.getRange(3, 10, sheet.getLastRow() - 2, 1); // Assuming we add 'Status' column 10 | const LogsRange = sheet.getRange(3, 11, sheet.getLastRow() - 2, 1); // Assuming we add 'Logs' column 11 | 12 | const EmailValues = EmailRange.getValues(); 13 | const EndDateValues = EndDateRange.getValues(); 14 | const NameValues = NameRange.getValues(); 15 | const StatusValues = StatusRange.getValues(); 16 | const LogsValues = LogsRange.getValues(); 17 | 18 | const today = new Date(); 19 | const currentmonth = today.getMonth(); 20 | const currentyear = today.getFullYear(); 21 | const dayOfMonth = today.getDate(); 22 | 23 | if (dayOfMonth !== 30) { // Adjust this check as per your requirement 24 | Logger.log("Today is not the 6th of the month. No emails will be sent."); 25 | return; 26 | } 27 | 28 | Logger.log(`Today's date is ${today}`); 29 | Logger.log(`Processing ${EmailValues.length} rows`); 30 | 31 | for (let i = 0; i < EmailValues.length; i++) { 32 | const email = EmailValues[i][0]; 33 | const endDateStr = EndDateValues[i][0]; 34 | const name = NameValues[i][0]; 35 | 36 | Logger.log(`Row ${i + 3}: email=${email}, endDateStr=${endDateStr}`); 37 | 38 | if (email !== "" && endDateStr !== "") { 39 | const endDate = new Date(endDateStr); 40 | if (isNaN(endDate)) { 41 | Logger.log(`Invalid date in row ${i + 3}: ${endDateStr}`); 42 | LogsValues[i][0] = `Invalid date format: ${endDateStr}`; 43 | continue; 44 | } 45 | 46 | const monthend = endDate.getMonth(); 47 | const dayend = endDate.getDate(); 48 | 49 | Logger.log(`Row ${i + 3}: endDate=${endDate}, monthend=${monthend}, dayend=${dayend}, name=${name}`); 50 | 51 | if (monthend === currentmonth && endDate.getFullYear() === currentyear) { 52 | try { 53 | const emailBody = ` 54 | Dear ${name}, 55 | 56 | We hope you're enjoying your fitness journey with us! We wanted to remind you that your gym membership is set to expire on ${endDateStr}. 57 | 58 | To keep your momentum going and continue achieving your fitness goals, we encourage you to renew your membership before it expires. By renewing now, you'll ensure that you have uninterrupted access to all our facilities, classes, and expert trainers. 59 | 60 | If you have any questions about your renewal options or need assistance, our team is here to help. Just give us a call or drop by the front desk on your next visit. 61 | 62 | Thank you for being a valued member of our fitness community. We look forward to supporting you in your ongoing pursuit of health and wellness. 63 | 64 | Stay strong, 65 | 66 | 24/7 Fitness 67 | Member Support Team 68 | Rachel Grae 69 | 70 | P.S. Renew your membership this month and enjoy exclusive offers, including discounted personal training sessions! 71 | `; 72 | 73 | MailApp.sendEmail(email, "Please upgrade your subscription", emailBody); 74 | Logger.log(`Email sent to ${email} for renewal date ${endDateStr}`); 75 | StatusValues[i][0] = 'Pending'; 76 | LogsValues[i][0] = 'EMAIL SENT'; 77 | } catch (e) { 78 | Logger.log(`Failed to send email to ${email}: ${e.message}`); 79 | LogsValues[i][0] = `Failed to send email: ${e.message}`; 80 | } 81 | } 82 | } else { 83 | Logger.log(`Skipping row ${i + 3} due to missing email or end date.`); 84 | LogsValues[i][0] = 'Missing email or end date'; 85 | } 86 | } 87 | 88 | // Update the Status and Logs columns 89 | StatusRange.setValues(StatusValues); 90 | LogsRange.setValues(LogsValues); 91 | } 92 | -------------------------------------------------------------------------------- /DocsAgentADK/AIVertex.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Google LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const LOCATION = PropertiesService.getScriptProperties().getProperty('LOCATION'); 18 | const GEMINI_MODEL_ID = PropertiesService.getScriptProperties().getProperty('GEMINI_MODEL_ID'); 19 | const REASONING_ENGINE_ID = PropertiesService.getScriptProperties().getProperty('REASONING_ENGINE_ID'); 20 | const SERVICE_ACCOUNT_KEY = PropertiesService.getScriptProperties().getProperty('SERVICE_ACCOUNT_KEY'); 21 | 22 | const credentials = credentialsForVertexAI(); 23 | 24 | /** 25 | * @param {string} statement The statement to fact-check. 26 | */ 27 | function requestLlmAuditorAdkAiAgent(statement) { 28 | return UrlFetchApp.fetch( 29 | `https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${credentials.projectId}/locations/${LOCATION}/reasoningEngines/${REASONING_ENGINE_ID}:streamQuery?alt=sse`, 30 | { 31 | method: 'post', 32 | headers: { 'Authorization': `Bearer ${credentials.accessToken}` }, 33 | contentType: 'application/json', 34 | muteHttpExceptions: true, 35 | payload: JSON.stringify({ 36 | "class_method": "async_stream_query", 37 | "input": { 38 | "user_id": "google_sheets_custom_function_fact_check", 39 | "message": statement, 40 | } 41 | }) 42 | } 43 | ).getContentText(); 44 | } 45 | 46 | /** 47 | * @param {string} prompt The Gemini prompt to use. 48 | */ 49 | function requestOutputFormatting(prompt) { 50 | const response = UrlFetchApp.fetch( 51 | `https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${credentials.projectId}/locations/${LOCATION}/publishers/google/models/${GEMINI_MODEL_ID}:generateContent`, 52 | { 53 | method: 'post', 54 | headers: { 'Authorization': `Bearer ${credentials.accessToken}` }, 55 | contentType: 'application/json', 56 | muteHttpExceptions: true, 57 | payload: JSON.stringify({ 58 | "contents": [{ 59 | "role": "user", 60 | "parts": [{ "text": prompt }] 61 | }], 62 | "generationConfig": { "temperature": 0.1, "maxOutputTokens": 2048 }, 63 | "safetySettings": [ 64 | { 65 | "category": "HARM_CATEGORY_HARASSMENT", 66 | "threshold": "BLOCK_NONE" 67 | }, 68 | { 69 | "category": "HARM_CATEGORY_HATE_SPEECH", 70 | "threshold": "BLOCK_NONE" 71 | }, 72 | { 73 | "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", 74 | "threshold": "BLOCK_NONE" 75 | }, 76 | { 77 | "category": "HARM_CATEGORY_DANGEROUS_CONTENT", 78 | "threshold": "BLOCK_NONE" 79 | } 80 | ] 81 | }) 82 | } 83 | ); 84 | return JSON.parse(response).candidates[0].content.parts[0].text 85 | } 86 | 87 | /** 88 | * Gets credentials required to call Vertex API using a Service Account. 89 | * Requires use of Service Account Key stored with project. 90 | * 91 | * @return {!Object} Containing the Google Cloud project ID and the access token. 92 | */ 93 | function credentialsForVertexAI() { 94 | const credentials = SERVICE_ACCOUNT_KEY; 95 | if (!credentials) { 96 | throw new Error("service_account_key script property must be set."); 97 | } 98 | 99 | const parsedCredentials = JSON.parse(credentials); 100 | 101 | const service = OAuth2.createService("Vertex") 102 | .setTokenUrl('https://oauth2.googleapis.com/token') 103 | .setPrivateKey(parsedCredentials['private_key']) 104 | .setIssuer(parsedCredentials['client_email']) 105 | .setPropertyStore(PropertiesService.getScriptProperties()) 106 | .setScope("https://www.googleapis.com/auth/cloud-platform"); 107 | return { 108 | projectId: parsedCredentials['project_id'], 109 | accessToken: service.getAccessToken(), 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /PodcastGenAI/Script.js: -------------------------------------------------------------------------------- 1 | function onOpen() { 2 | var ui = DocumentApp.getUi(); 3 | ui.createMenu('PodcastGenPro') 4 | .addItem('Launch Sidebar', 'showSidebar') 5 | .addToUi(); 6 | } 7 | 8 | function showSidebar() { 9 | var html = HtmlService.createHtmlOutputFromFile('Sidebar') 10 | .setTitle('PodcastGeneratorPro') 11 | .setWidth(800); 12 | DocumentApp.getUi().showSidebar(html); 13 | } 14 | 15 | function getSelectedText() { 16 | var doc = DocumentApp.getActiveDocument(); 17 | var selection = doc.getSelection(); 18 | if (selection) { 19 | var elements = selection.getRangeElements(); 20 | var selectedText = elements.map(function (element) { 21 | return element.getElement().asText().getText(); 22 | }).join("\n"); 23 | return selectedText; 24 | } 25 | return ""; 26 | } 27 | function sendToGemini(selectedText) { 28 | const GEMINI_KEY = 'YOUR_API_KEY'; 29 | const GEMINI_ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GEMINI_KEY}`; 30 | 31 | var headers = { 32 | "Content-Type": "application/json", 33 | }; 34 | 35 | var requestBody = { 36 | "contents": [ 37 | { 38 | "parts": [ 39 | { 40 | "text": `You are an AI scriptwriter for a podcast called *The Machine Learning Engineer*. Your task is to generate a natural, engaging, and dynamic podcast script in a conversational format between two speakers: Marina and Sascha. The discussion should feel spontaneous and lively, with realistic interactions. The output should be in Hindi langauge 41 | Use the following format: 42 | [ 43 | { 44 | "speaker": "Marina", 45 | "text": "" 46 | }, 47 | { 48 | "speaker": "Sascha", 49 | "text": "" 50 | }, 51 | ] 52 | ### Topic: 53 | ${selectedText} 54 | 55 | Use the information provided above as the core discussion material for this episode. Marina will introduce the topic, and Sascha will explain it in detail, with back-and-forth interactions that make the conversation engaging. 56 | 57 | ### Tone & Style: 58 | - Friendly, engaging, and conversational. 59 | - Natural flow, like two experts chatting informally. 60 | - Keep it dynamic with occasional expressions of surprise, humor, or curiosity. 61 | 62 | ### Instructions for Response: 63 | - The response should be a structured JSON list of dialogue exchanges. 64 | - Maintain a lively discussion with a smooth flow of ideas. 65 | - Ensure Sascha provides clear and engaging explanations while Marina asks insightful follow-up questions. 66 | 67 | Return the response in the specified JSON format, ensuring it stays structured correctly while keeping the dialogue engaging and informative. 68 | ` 69 | } 70 | ] 71 | } 72 | ] 73 | }; 74 | 75 | var options = { 76 | "method": "POST", 77 | "headers": headers, 78 | "payload": JSON.stringify(requestBody), 79 | "muteHttpExceptions": true 80 | 81 | }; 82 | 83 | try { 84 | var response = UrlFetchApp.fetch(GEMINI_ENDPOINT, options); 85 | var data = JSON.parse(response.getContentText()); 86 | 87 | Logger.log("Gemini API Response: " + JSON.stringify(data)); 88 | 89 | // Extract the JSON part from the response 90 | if (data.candidates && data.candidates[0].content.parts[0].text) { 91 | var scriptText = data.candidates[0].content.parts[0].text; 92 | 93 | // Remove code block markers and parse JSON 94 | var startIndex = scriptText.indexOf('['); 95 | var endIndex = scriptText.lastIndexOf(']'); 96 | var cleanScriptText = scriptText.substring(startIndex, endIndex + 1); 97 | var conversationScript = JSON.parse(cleanScriptText); 98 | 99 | Logger.log(conversationScript) 100 | 101 | // Cloud Run Call 102 | const url1 = 'YOUR_CLOUD_RUN_ENDPOINT'; 103 | var payload1 = { 104 | 'variable': JSON.stringify(conversationScript) 105 | }; 106 | var options1 = { 107 | 'method': 'post', 108 | 'contentType': 'application/json', 109 | 'payload': JSON.stringify(payload1), 110 | 'muteHttpExceptions': true 111 | }; 112 | 113 | var cloudFunctionResponse = UrlFetchApp.fetch(url1, options1); 114 | Logger.log("Script sent to Cloud Run"); 115 | Logger.log("Cloud Run Response: " + cloudFunctionResponse.getContentText()); 116 | } else { 117 | Logger.log("Unexpected Gemini API response format: " + JSON.stringify(data)); 118 | } 119 | } catch (e) { 120 | Logger.log("Error during Gemini API or Cloud Run call: " + e.message); 121 | } 122 | } 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /PodcastGenAI/audio.py: -------------------------------------------------------------------------------- 1 | import os 2 | import flask 3 | import datetime 4 | import re 5 | import json 6 | from google.cloud import storage, texttospeech 7 | from pydub import AudioSegment 8 | 9 | # Directory to store files (Cloud Run allows writing to /tmp) 10 | FILE_DIR = '/tmp/' 11 | BUCKET_NAME = 'aryan_bucket' 12 | 13 | # Google TTS Client 14 | client = texttospeech.TextToSpeechClient() 15 | 16 | # Speaker-to-voice mapping 17 | speaker_voice_map = { 18 | "Sascha": "en-US-Wavenet-D", 19 | "Marina": "en-US-Journey-O" 20 | } 21 | 22 | # Google TTS function 23 | def synthesize_speech_google(text, speaker, index): 24 | synthesis_input = texttospeech.SynthesisInput(text=text) 25 | voice = texttospeech.VoiceSelectionParams( 26 | language_code="en-US", 27 | name=speaker_voice_map[speaker] 28 | ) 29 | audio_config = texttospeech.AudioConfig( 30 | audio_encoding=texttospeech.AudioEncoding.LINEAR16 31 | ) 32 | response = client.synthesize_speech( 33 | input=synthesis_input, voice=voice, audio_config=audio_config 34 | ) 35 | filename = f"{FILE_DIR}{index}_{speaker}.wav" 36 | with open(filename, "wb") as out: 37 | out.write(response.audio_content) 38 | print(f'Audio content written to file "{filename}"') 39 | 40 | def synthesize_speech(text, speaker, index): 41 | synthesize_speech_google(text, speaker, index) 42 | 43 | # Function to sort filenames naturally 44 | def natural_sort_key(filename): 45 | return [int(text) if text.isdigit() else text for text in re.split(r'(\d+)', filename)] 46 | 47 | # Function to merge audio files 48 | def merge_audios(audio_folder, output_file): 49 | combined = AudioSegment.empty() 50 | audio_files = sorted( 51 | [f for f in os.listdir(audio_folder) if f.endswith(".wav")], 52 | key=natural_sort_key 53 | ) 54 | for filename in audio_files: 55 | audio_path = os.path.join(audio_folder, filename) 56 | print(f"Processing: {audio_path}") 57 | audio = AudioSegment.from_file(audio_path) 58 | combined += audio 59 | combined.export(output_file, format="mp3") 60 | print(f"Merged audio saved as {output_file}") 61 | 62 | # Function to upload file to GCS 63 | def upload_to_gcs(file_path, file_name): 64 | client = storage.Client() 65 | try: 66 | blob = client.bucket(BUCKET_NAME).blob(file_name) 67 | blob.upload_from_filename(file_path) 68 | print(f"File uploaded to GCS: {BUCKET_NAME}/{file_name}") 69 | except Exception as e: 70 | print(f"Error uploading to GCS: {e}") 71 | raise 72 | 73 | # Function to generate the podcast audio from the provided conversation 74 | def generate_audio(conversation, file_name_prefix): 75 | os.makedirs(FILE_DIR, exist_ok=True) 76 | for index, part in enumerate(conversation): 77 | speaker = part['speaker'] 78 | text = part['text'] 79 | synthesize_speech(text, speaker, index) 80 | 81 | audio_folder = FILE_DIR 82 | final_file_name = f"{file_name_prefix}_podcast.mp3" 83 | output_file_path = os.path.join(FILE_DIR, final_file_name) 84 | merge_audios(audio_folder, output_file_path) 85 | 86 | # Upload the final file to GCS 87 | try: 88 | upload_to_gcs(output_file_path, final_file_name) 89 | print(f"Final podcast uploaded successfully: {final_file_name}") 90 | except Exception as e: 91 | print(f"Error during final upload: {e}") 92 | raise 93 | 94 | # Flask handler to accept the script through 'variable' 95 | def hello_world(request): 96 | # Parse the JSON request body 97 | request_json = request.get_json(silent=True) 98 | 99 | # Extract the 'variable' parameter from the request body 100 | if request_json and 'variable' in request_json: 101 | try: 102 | # Parse the conversation script from the 'variable' field 103 | conversation = json.loads(request_json['variable']) 104 | except json.JSONDecodeError as e: 105 | return flask.make_response(f"Invalid input: 'variable' must be a valid JSON. Error: {e}", 400) 106 | else: 107 | return flask.make_response("Invalid input: Please provide a 'variable'.", 400) 108 | 109 | # Print the conversation to the console (Cloud Function logs) 110 | print(f"Received conversation script: {conversation}") 111 | 112 | # Create a unique file name based on the current timestamp 113 | timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") 114 | file_name_prefix = f"podcast_{timestamp}" 115 | 116 | # Generate audio based on the provided conversation 117 | try: 118 | generate_audio(conversation, file_name_prefix) 119 | except Exception as e: 120 | return flask.make_response(f"Error during podcast generation: {e}", 500) 121 | 122 | response_message = f"Podcast generated and uploaded to GCS with prefix: {file_name_prefix}_podcast.mp3" 123 | return flask.make_response(response_message, 200) 124 | -------------------------------------------------------------------------------- /GeminiDataExtract.js: -------------------------------------------------------------------------------- 1 | function processPdfToSheet() { 2 | var archiveFolderId = "YOUR_ARCHIVE_FOLDER_ID"; 3 | const folderId1 = "YOUR_FOLDER_ID"; 4 | var folder = DriveApp.getFolderById(folderId1); 5 | var files = folder.getFiles(); 6 | 7 | while (files.hasNext()) { 8 | var file = files.next(); 9 | if (file.getMimeType() === MimeType.PDF) { // Filter PDF files 10 | var fileId = file.getId(); 11 | var pdfContent = convertPdfToGoogleDoc(fileId, folder); 12 | var responseData = sendToGemini(pdfContent); 13 | var details = extractFields(responseData); 14 | 15 | // Update Google Sheet with extracted details 16 | updateSheet(details); 17 | 18 | // Move the original PDF and the converted Google Doc to the archive folder 19 | var archiveFolder = DriveApp.getFolderById(archiveFolderId); 20 | moveFileToArchive(file, archiveFolder); 21 | } 22 | } 23 | } 24 | 25 | 26 | function convertPdfToGoogleDoc(fileId, folder) { 27 | var file = DriveApp.getFileById(fileId); 28 | var blob = file.getBlob(); 29 | var newFileName = file.getName().replace(/\.pdf$/, '') + ' converted'; 30 | var resource = { 31 | title: newFileName, 32 | mimeType: MimeType.GOOGLE_DOCS 33 | }; 34 | var options = { 35 | ocr: true, 36 | ocrLanguage: 'en' 37 | }; 38 | var convertedFile = Drive.Files.create(resource, blob, options); 39 | var doc = DocumentApp.openById(convertedFile.id); 40 | var pdfContent = doc.getBody().getText(); 41 | var convertedFileObj = DriveApp.getFileById(convertedFile.id); 42 | convertedFileObj.setTrashed(true); // Move to trash 43 | return pdfContent; 44 | } 45 | 46 | function sendToGemini(pdfData) { 47 | const GEMINI_KEY = 'YOUR_API_KEY'; 48 | const GEMINI_ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GEMINI_KEY}`; 49 | var headers = { 50 | "Content-Type": "application/json", 51 | "Accept": "application/json" 52 | }; 53 | var requestBody = { 54 | "contents": [ 55 | { 56 | "parts": [ 57 | { 58 | "text": `extract the following details: Vendor Name: Invoice Number: Amount Due: Due Date: Description Tax: \n${pdfData}` 59 | } 60 | ] 61 | } 62 | ] 63 | }; 64 | var options = { 65 | "method": "POST", 66 | "headers": headers, 67 | "payload": JSON.stringify(requestBody) 68 | }; 69 | try { 70 | var response = UrlFetchApp.fetch(GEMINI_ENDPOINT, options); 71 | var datanew = JSON.parse(response.getContentText()); 72 | return datanew; 73 | } catch (error) { 74 | Logger.log('Error calling Gemini API: ' + error); 75 | return null; 76 | } 77 | } 78 | 79 | function extractFields(datanew) { 80 | if (!datanew || !datanew.candidates || !datanew.candidates.length) { 81 | Logger.log('No valid data returned from Gemini.'); 82 | return {}; 83 | } 84 | 85 | var textContent = datanew.candidates[0].content.parts[0].text; 86 | textContent = textContent.replace(/- /g, '').trim(); 87 | var lines = textContent.split('\n'); 88 | 89 | var details = {}; 90 | lines.forEach(function (line) { 91 | var parts = line.split(':'); 92 | if (parts.length === 2) { 93 | var key = parts[0].replace(/\*\*/g, '').trim(); 94 | var value = parts[1].replace(/\*\*/g, '').trim(); 95 | details[key] = value; 96 | } 97 | }); 98 | 99 | return details; 100 | } 101 | 102 | function updateSheet(details) { 103 | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Invoices"); 104 | var range = sheet.getDataRange(); 105 | var values = range.getValues(); 106 | 107 | var vendorName = details['Vendor Name']; 108 | var nameFound = false; 109 | 110 | var currentDate = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'MM/dd/yy'); 111 | var formattedDateTime = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "yyyy-MM-dd HH:mm:ss"); 112 | 113 | for (var i = 1; i < values.length; i++) { 114 | if (values[i][2].toLowerCase() === vendorName.toLowerCase()) { // Compare by Vendor Name 115 | nameFound = true; 116 | sheet.getRange(i + 1, 1).setValue(details['Invoice Number']); // Column A 117 | sheet.getRange(i + 1, 6).setValue(details['Amount Due']); // Column F 118 | sheet.getRange(i + 1, 7).setValue(details['Due Date']); // Column G 119 | sheet.getRange(i + 1, 9).setValue("Last updated at: " + formattedDateTime); // Column I 120 | Logger.log("Updated Row " + (i + 1)); 121 | break; 122 | } 123 | } 124 | 125 | if (!nameFound) { 126 | Logger.log("Vendor not found: " + vendorName); 127 | var newRow = values.length + 1; 128 | 129 | sheet.getRange(newRow, 1).setValue(details['Invoice Number']); // Column A 130 | sheet.getRange(newRow, 3).setValue(vendorName); // Column C 131 | sheet.getRange(newRow, 4).setValue(details['Description']); // Column D 132 | sheet.getRange(newRow, 5).setValue(vendorName); // Column E 133 | sheet.getRange(newRow, 6).setValue(details['Amount Due']); // Column F 134 | sheet.getRange(newRow, 7).setValue(details['Due Date']); // Column G 135 | sheet.getRange(i + 1, 9).setValue("Last updated at: " + formattedDateTime); // Column I 136 | Logger.log("New Row Added"); 137 | } 138 | } 139 | 140 | function moveFileToArchive(file, archiveFolder) { 141 | file.moveTo(archiveFolder); 142 | } 143 | -------------------------------------------------------------------------------- /PDF.js: -------------------------------------------------------------------------------- 1 | function CreateBulkPDF(PDFContentBlob){ 2 | 3 | // Getting all the Files to store the PDF's 4 | const docfile = DriveApp.getFileById("1UCwUYnp94yYwQp8ZK30ks3UtRX3id5cX5wz97E55CRQ"); 5 | const TempFolder = DriveApp.getFolderById("1N0ayWXIEU1oHmhg02wx7FkBNliEturZV"); 6 | const PDFFolder = DriveApp.getFolderById("1NJlToP7fsLIO4T9NpXC4FmJY4S-SMHMR"); 7 | 8 | const ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("marks"); 9 | var data = ss.getRange(4,1, ss.getLastRow() -3,19).getValues(); 10 | //Logger.log(data); 11 | 12 | // Getting the Name of the Subjects 13 | var sub1 = ss.getRange("E3").getValue(); // Subject 1 14 | //console.log(sub1); 15 | 16 | var sub2 = ss.getRange("G3").getValue(); // Subject 3 17 | //console.log(sub3); 18 | 19 | var sub3 = ss.getRange("I3").getValue(); // Subject 2 20 | //console.log(sub5); 21 | 22 | var sub4 = ss.getRange("K3").getValue(); // Subject 1 23 | //console.log(sub7); 24 | 25 | var sub5 = ss.getRange("M3").getValue(); // Subject 3 26 | //console.log(sub9); 27 | 28 | var sub6 = ss.getRange("O3").getValue(); // Subject 3 29 | //console.log(sub11); 30 | // Getting all the Details of the Students using ForEach 31 | 32 | data.forEach(function(row,i){ 33 | 34 | var SapId = row[1]; // Sap Id of the student 35 | //console.log(sap); 36 | 37 | var rollnum = row[2]; // Roll number of the student. 38 | // console.log(roll); 39 | 40 | var name = row[3]; // Name of the student 41 | // console.log(name); 42 | 43 | var SOM1 = row[4]; 44 | // console.log(SOM1); // Marks Strength of materials test1 45 | 46 | var SOM2 = row[5]; 47 | // console.log(SOM2); // Marks of Strength of materials test2 48 | 49 | var Surv1 = row[6]; 50 | // console.log(Surveying1); // Marks of Surveying Test1 51 | 52 | var Surv2 = row[7]; 53 | // console.log(Surveying2) // Marks of Surveying Test2 54 | 55 | var Geo1 = row[8]; 56 | //console.log(Eng_Geo1); // Marks of Engineering Geology Test1 57 | 58 | var Geo2 = row[9]; 59 | // console.log(Eng_Geo2); // Marks of Engineering Geology Test2 60 | 61 | var Math1 = row[10]; 62 | //console.log(Eng_Maths1); // Marks of Engineering Maths Test 1 63 | 64 | var Math2 = row[11]; 65 | //console.log(Eng_Maths2); // Marks of Engineering Maths Test 2 66 | 67 | var Phy1 = row[12]; 68 | // console.log(Eng_Phy1); // Marks of Engineering Physics Test 1 69 | 70 | var Phy2 = row[13]; 71 | // console.log(Eng_Phy2); // Marks of Engineering Physics Test 2 72 | 73 | var Num1 = row[14]; 74 | // console.log(Num_Tech1); // Marks of Numerical Techniques Test 1 75 | 76 | var Num2 = row[15]; 77 | // console.log(NumTech2); // Marks of Numerical Techniques Test 2 78 | var email = row[18]; 79 | // console.log(email); // Email of the Parent 80 | 81 | var pdfName = name+" " + rollnum; 82 | 83 | 84 | createPDF(name,rollnum,SapId,SOM1,SOM2,Surv1,Surv2,Geo1, Geo2,Math1,Math2,Phy1,Phy2,Num1,Num2,sub1,sub2,sub3,sub4,sub5,sub6,email,docfile,TempFolder,PDFFolder,pdfName); 85 | 86 | }); 87 | 88 | } 89 | 90 | 91 | 92 | 93 | // Function that creates the PDF 94 | function createPDF(name,rollnum,SapId,SOM1,SOM2,Surv1,Surv2,Geo1, Geo2,Math1,Math2,Phy1,Phy2,Num1,Num2,sub1,sub2,sub3,sub4,sub5,sub6,email, docfile,TempFolder,PDFFolder,pdfName){ 95 | // Getting all the Files like Docs, Temp Folder , Pdf Folder 96 | 97 | const tempFile = docfile.makeCopy(TempFolder); 98 | const TempDocFile = DocumentApp.openById(tempFile.getId()); 99 | const body = TempDocFile.getBody(); 100 | 101 | // Searching and replacing the text into the Doc 102 | TempDocFile.getBody().replaceText("{name}", name); // Name of the Student 103 | TempDocFile.getBody().replaceText("{roll}", rollnum); // Roll Number of the Student 104 | TempDocFile.getBody().replaceText("{sap}", SapId); // Sap ID of the Student 105 | TempDocFile.getBody().replaceText("{som1}", SOM1); // Strength of Materials M1 106 | TempDocFile.getBody().replaceText("{som2}", SOM2); // Strength of Materials M2 107 | TempDocFile.getBody().replaceText("{surv1}", Surv1); // Surveying M1 108 | TempDocFile.getBody().replaceText("{surv2}", Surv2); // Surveying M2 109 | TempDocFile.getBody().replaceText("{geo1}", Geo1); // Engineering Geology M1 110 | TempDocFile.getBody().replaceText("{geo2}", Geo2); // Engineering Geology M2 111 | TempDocFile.getBody().replaceText("{maths1}", Math1); // Engineering Mathematics M1 112 | TempDocFile.getBody().replaceText("{maths2}", Math2); // Engineering Mathematics M2 113 | TempDocFile.getBody().replaceText("{phy1}", Phy1); // Engineering Physics M1 114 | TempDocFile.getBody().replaceText("{phy2}", Phy2); // Engineering Physics M2 115 | TempDocFile.getBody().replaceText("{num1}", Num1); // Numerical Techniques M1 116 | TempDocFile.getBody().replaceText("{num2}", Num2); // Numerical Techniques M2 117 | 118 | TempDocFile.getBody().replaceText("{Som}", sub1); // Strength of Materials 119 | TempDocFile.getBody().replaceText("{Surv}", sub2 ); // Surveying 120 | TempDocFile.getBody().replaceText("{geo}", sub3); // Engineering Geology 121 | TempDocFile.getBody().replaceText("{maths}", sub4); // Engineering Mathematics 122 | TempDocFile.getBody().replaceText("{phy}", sub5); // Engineering Physics 123 | TempDocFile.getBody().replaceText("{num}", sub6); // Numerical Techniques 124 | 125 | var pdfName = name+" " + rollnum; 126 | 127 | // Saving all the changes made in the doc 128 | TempDocFile.saveAndClose(); 129 | const PDFContentBlob = tempFile.getAs(MimeType.PDF); 130 | const PDF_File = PDFFolder.createFile(PDFContentBlob).setName(pdfName); 131 | TempFolder.removeFile(tempFile); 132 | 133 | MailApp.sendEmail(email,name + " " + rollnum,"These are your marks for M1 and M2",{ 134 | 135 | attachments: [PDF_File.getAs(MimeType.PDF)] 136 | 137 | }); 138 | 139 | 140 | } 141 | -------------------------------------------------------------------------------- /Fetch&SummariseDocComments.js: -------------------------------------------------------------------------------- 1 | const GEMINI_API_KEY = "your_api_key"; 2 | 3 | function onOpen() { 4 | const ui = DocumentApp.getUi(); 5 | ui.createMenu('AI Comment Tools') 6 | .addItem('Summarize All Comments (Gemini)', 'summarizeDocComments') 7 | .addToUi(); 8 | } 9 | 10 | /** 11 | * Fetch comments and their context from the Google Doc 12 | */ 13 | function listComments() { 14 | const documentId = DocumentApp.getActiveDocument().getId(); 15 | const documentBodyText = DocumentApp.getActiveDocument().getBody().getText(); 16 | const options = { 17 | fields: 'comments(id, content, createdTime, author(displayName), resolved, replies(id, content, createdTime, author(displayName)), anchor)' 18 | }; 19 | 20 | let allCommentsAndContext = ''; 21 | let commentCount = 0; 22 | 23 | try { 24 | const response = Drive.Comments.list(documentId, options); 25 | const comments = response.comments; 26 | 27 | if (!comments || comments.length === 0) { 28 | return { commentsString: '', count: 0 }; 29 | } 30 | 31 | commentCount = comments.length; 32 | comments.forEach(comment => { 33 | const author = comment.author ? comment.author.displayName : 'Unknown Author'; 34 | const commentContent = comment.content || '[No Content]'; 35 | let contextualText = ''; 36 | 37 | if (comment.anchor && comment.anchor.range) { 38 | const range = comment.anchor.range; 39 | const startIndex = range.startIndex || 0; 40 | const endIndex = range.endIndex || documentBodyText.length; 41 | contextualText = documentBodyText.substring(startIndex, endIndex).trim(); 42 | } 43 | 44 | allCommentsAndContext += `--- Context for Comment by ${author} ---\n`; 45 | allCommentsAndContext += `${contextualText || '[No context found]'}\n`; 46 | allCommentsAndContext += `--- Comment: ---\n${commentContent}\n`; 47 | 48 | if (comment.replies && comment.replies.length > 0) { 49 | comment.replies.forEach(reply => { 50 | const replyAuthor = reply.author ? reply.author.displayName : 'Unknown Author'; 51 | const replyContent = reply.content || '[No Content]'; 52 | allCommentsAndContext += ` ↳ Reply from ${replyAuthor}: ${replyContent}\n`; 53 | }); 54 | } 55 | allCommentsAndContext += `------------------------\n\n`; 56 | }); 57 | } catch (e) { 58 | Logger.log(`Error fetching comments: ${e.message}`); 59 | return { commentsString: '', count: 0 }; 60 | } 61 | 62 | return { commentsString: allCommentsAndContext, count: commentCount }; 63 | } 64 | 65 | /** 66 | * Generate a summary using Gemini API 67 | */ 68 | function generateSummary(commentsAndContext) { 69 | const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${GEMINI_API_KEY}`; 70 | const promptText = `Summarize the following Google Docs comments (with context). Identify key feedback, action items, and recurring themes. Format as clear bullet points. Be concise and omit quotation marks.\n\n${commentsAndContext}`; 71 | 72 | const headers = { "Content-Type": "application/json" }; 73 | const payload = JSON.stringify({ 74 | contents: [{ parts: [{ text: promptText }] }] 75 | }); 76 | 77 | const response = UrlFetchApp.fetch(endpoint, { 78 | method: "POST", 79 | headers: headers, 80 | payload: payload, 81 | muteHttpExceptions: true 82 | }); 83 | 84 | const json = JSON.parse(response.getContentText()); 85 | if (json.candidates && json.candidates.length > 0) { 86 | return json.candidates[0].content.parts[0].text.trim(); 87 | } else if (json.error) { 88 | throw new Error(json.error.message); 89 | } else { 90 | throw new Error("Unexpected API response format."); 91 | } 92 | } 93 | 94 | /** 95 | * Insert a styled summary at the top of the Google Doc 96 | */ 97 | function insertStyledSummary(summary) { 98 | const doc = DocumentApp.getActiveDocument(); 99 | const body = doc.getBody(); 100 | 101 | // Insert header 102 | const header = body.insertParagraph(0, "🧠 AI Comment Summary (Powered by Gemini AI)"); 103 | header.setHeading(DocumentApp.ParagraphHeading.HEADING2); 104 | 105 | // Insert timestamp 106 | const timestamp = new Date().toLocaleString(); 107 | const timestampPara = body.insertParagraph(1, `Generated on: ${timestamp}`); 108 | timestampPara.setItalic(true); 109 | 110 | // Insert summary content 111 | const summaryPara = body.insertParagraph(2, summary); 112 | summaryPara.setSpacingAfter(12); 113 | } 114 | 115 | /** 116 | * Main function that runs the summarization 117 | */ 118 | function summarizeDocComments() { 119 | const ui = DocumentApp.getUi(); 120 | 121 | if (!GEMINI_API_KEY || GEMINI_API_KEY === "YOUR_GEMINI_API_KEY") { 122 | ui.alert("API Key Missing", "Please add your Gemini API key at the top of the script.", ui.ButtonSet.OK); 123 | return; 124 | } 125 | 126 | ui.alert("Fetching all comments from your document..."); 127 | const commentsData = listComments(); 128 | 129 | if (!commentsData.count) { 130 | ui.alert("No Comments Found", "This document has no comments to summarize.", ui.ButtonSet.OK); 131 | return; 132 | } 133 | 134 | const confirmMessage = `Found ${commentsData.count} comments. Do you want to summarize them using Gemini AI?`; 135 | const userResponse = ui.alert("Confirm Summarization", confirmMessage, ui.ButtonSet.YES_NO); 136 | if (userResponse !== ui.Button.YES) { 137 | ui.alert("Action Cancelled", "Comment summarization was cancelled.", ui.ButtonSet.OK); 138 | return; 139 | } 140 | 141 | ui.alert("Processing...", "Sending comments to Gemini AI for summarization.", ui.ButtonSet.OK); 142 | 143 | let summary; 144 | try { 145 | summary = generateSummary(commentsData.commentsString); 146 | } catch (e) { 147 | ui.alert("Gemini API Error", `An error occurred: ${e.message}`, ui.ButtonSet.OK); 148 | return; 149 | } 150 | 151 | insertStyledSummary(summary); 152 | ui.alert("✅ Summary Inserted", "Your AI summary has been added to the top of the document.", ui.ButtonSet.OK); 153 | } 154 | -------------------------------------------------------------------------------- /LabelConfig.js: -------------------------------------------------------------------------------- 1 | function analyzeAndApplyDynamicLabel66556() { 2 | const fileID = "1hK8E-7z-XgpvtWJ7MTxz7d4giGZULjAjG6VssDMPHrs"; 3 | const driveFileID = '1hK8E-7z-XgpvtWJ7MTxz7d4giGZULjAjG6VssDMPHrs'; 4 | const apiKey = "AIzaSyDHWFWH9IVgnUGftBown8OBJiRwL-rLOx8"; // Replace with your actual API key 5 | 6 | try { 7 | // Step 1: Fetch labels dynamically 8 | const labelsResponse = DriveLabels.Labels.list({ view: "LABEL_VIEW_FULL" }); 9 | const labels = labelsResponse.labels; 10 | if (!labels || labels.length === 0) { 11 | Logger.log("No labels found."); 12 | return; 13 | } 14 | 15 | const labelMap = {}; 16 | labels.forEach(label => { 17 | if (label.properties?.title) { 18 | labelMap[label.properties.title] = { 19 | id: label.id, 20 | fields: label.fields 21 | }; 22 | } 23 | }); 24 | Logger.log(`Available Labels: ${JSON.stringify(labelMap)}`); 25 | 26 | // Step 2: Get document content 27 | const doc = DocumentApp.openById(fileID); 28 | const documentContent = doc.getBody().getText(); 29 | Logger.log(`Document Content: ${documentContent}`); 30 | 31 | // Step 3: Gemini API call for label suggestion 32 | const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${apiKey}`; 33 | const labelSuggestionPrompt = `Analyze the document and suggest an appropriate label. Just return the label. Available labels are: ${Object.keys(labelMap).join(", ")}.\nDocument:\n${documentContent}`; 34 | 35 | const labelResponse = makeGeminiApiRequest(apiUrl, labelSuggestionPrompt); 36 | if (!labelResponse) return; 37 | 38 | const suggestedLabel = labelResponse.candidates[0].content.parts[0].text.trim(); 39 | Logger.log(`Suggested Label: ${suggestedLabel}`); 40 | 41 | // Step 4: Apply the label and populate fields 42 | if (labelMap[suggestedLabel]) { 43 | const labelData = labelMap[suggestedLabel]; 44 | const labelId = labelData.id; 45 | 46 | const addLabelRequest = Drive.newModifyLabelsRequest() 47 | .setLabelModifications([Drive.newLabelModification().setLabelId(labelId)]); 48 | const addLabelResponse = Drive.Files.modifyLabels(addLabelRequest, driveFileID); 49 | Logger.log(`Labels applied: ${JSON.stringify(addLabelResponse.modifiedLabels)}`); 50 | 51 | if (labelData.fields && labelData.fields.length > 0) { 52 | const fieldPrompt = generateFieldPrompt(labelData.fields, documentContent); 53 | const fieldResponse = makeGeminiApiRequest(apiUrl, fieldPrompt); 54 | 55 | if (fieldResponse) { 56 | const fieldValues = parseFieldData(fieldResponse.candidates[0].content.parts[0].text.trim()); 57 | applyLabelAndPopulateFields(driveFileID, labelId, labelData.fields, fieldValues); 58 | } else { 59 | Logger.log('Failed to fetch field data for label.'); 60 | } 61 | } 62 | } else { 63 | Logger.log(`Suggested label "${suggestedLabel}" not found among available labels.`); 64 | } 65 | } catch (error) { 66 | Logger.log(`Failed to analyze and apply label: ${error.message}`); 67 | } 68 | } 69 | 70 | function makeGeminiApiRequest(apiUrl, prompt) { 71 | const requestBody = { contents: [{ parts: [{ text: prompt }] }] }; 72 | const options = { 73 | method: 'post', 74 | contentType: 'application/json', 75 | payload: JSON.stringify(requestBody), 76 | muteHttpExceptions: true 77 | }; 78 | 79 | const response = UrlFetchApp.fetch(apiUrl, options); 80 | const responseCode = response.getResponseCode(); 81 | const responseText = response.getContentText(); 82 | 83 | Logger.log(`Gemini API Response Code: ${responseCode}`); 84 | Logger.log(`Gemini API Response Text: ${responseText}`); 85 | 86 | if (responseCode !== 200) { 87 | Logger.log(`Error calling Gemini API: ${responseText}`); 88 | return null; 89 | } 90 | 91 | try { 92 | return JSON.parse(responseText); 93 | } catch (e) { 94 | Logger.log(`Error parsing Gemini API response: ${e.message}`); 95 | return null; 96 | } 97 | } 98 | 99 | function generateFieldPrompt(fields, documentContent) { 100 | const fieldNames = fields.map(field => field.properties.displayName).join(", "); 101 | return `Analyze the document and extract the following details: ${fieldNames}. Document Content:\n${documentContent}`; 102 | } 103 | 104 | function parseFieldData(fieldData) { 105 | const fieldValues = {}; 106 | const lines = fieldData.split('\n'); 107 | 108 | lines.forEach(line => { 109 | const [key, value] = line.split(':').map(part => part.trim()); 110 | if (key && value) { 111 | fieldValues[key] = value; 112 | } 113 | }); 114 | 115 | Logger.log(`Parsed field values: ${JSON.stringify(fieldValues)}`); 116 | return fieldValues; 117 | } 118 | 119 | function applyLabelAndPopulateFields(fileId, labelId, fields, fieldValues) { 120 | try { 121 | // Step 1: Apply the label 122 | const labelModification = Drive.newLabelModification().setLabelId(labelId); 123 | const applyLabelRequest = Drive.newModifyLabelsRequest().setLabelModifications([labelModification]); 124 | 125 | const applyLabelResponse = Drive.Files.modifyLabels(applyLabelRequest, fileId); 126 | Logger.log(`Label applied successfully: ${JSON.stringify(applyLabelResponse.modifiedLabels)}`); 127 | 128 | // Step 2: Populate label fields 129 | const labelModifications = fields.map(field => { 130 | const fieldValue = fieldValues[field.properties.displayName]; 131 | 132 | if (field.selectionOptions) { 133 | const choice = field.selectionOptions.choices.find(choice => 134 | choice.properties.displayName.toLowerCase() === fieldValue.toLowerCase() 135 | ); 136 | if (choice) { 137 | return Drive.newFieldModification() 138 | .setFieldId(field.id) 139 | .setChoiceId(choice.id); 140 | } 141 | } else if (fieldValue) { 142 | return Drive.newFieldModification() 143 | .setFieldId(field.id) 144 | .setText(fieldValue); 145 | } 146 | 147 | Logger.log(`No valid value for field: ${field.properties.displayName}`); 148 | return null; 149 | }).filter(mod => mod !== null); 150 | 151 | if (labelModifications.length === 0) { 152 | Logger.log('No valid modifications for fields.'); 153 | return; 154 | } 155 | 156 | // Send modifications in a single request 157 | const populateFieldsRequest = Drive.newModifyLabelsRequest() 158 | .setLabelModifications([ 159 | Drive.newLabelModification() 160 | .setLabelId(labelId) 161 | .setFieldModifications(labelModifications) 162 | ]); 163 | 164 | const populateFieldsResponse = Drive.Files.modifyLabels(populateFieldsRequest, fileId); 165 | Logger.log(`Fields populated successfully: ${JSON.stringify(populateFieldsResponse.modifiedLabels)}`); 166 | 167 | } catch (error) { 168 | Logger.log(`Error while applying label and populating fields: ${error.message}`); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /DriveLabelAutomation.js: -------------------------------------------------------------------------------- 1 | function analyzeAndApplyDynamicLabel66556() { 2 | const fileID = "your_file_id"; 3 | const driveFileID = 'your_file_id'; 4 | const apiKey = "your_api_key"; // Replace with your actual API key 5 | try { 6 | // Step 1: Fetch labels dynamically 7 | const labelsResponse = DriveLabels.Labels.list({ view: "LABEL_VIEW_FULL" }); 8 | const labels = labelsResponse.labels; 9 | if (!labels || labels.length === 0) { 10 | Logger.log("No labels found."); 11 | return; 12 | } 13 | const labelMap = {}; 14 | labels.forEach(label => { 15 | if (label.properties?.title) { 16 | labelMap[label.properties.title] = { 17 | id: label.id, 18 | fields: label.fields 19 | }; 20 | } 21 | }); 22 | Logger.log(`Available Labels: ${JSON.stringify(labelMap)}`); 23 | // Step 2: Get document content 24 | const doc = DocumentApp.openById(fileID); 25 | const documentContent = doc.getBody().getText(); 26 | Logger.log(`Document Content: ${documentContent}`); 27 | // Step 3: Gemini API call for label suggestion 28 | const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${apiKey}`; 29 | const labelSuggestionPrompt = `Analyze the document and suggest an appropriate label. Just return the label. Available labels are: ${Object.keys(labelMap).join(", ")}.\nDocument:\n${documentContent}`; 30 | const labelResponse = makeGeminiApiRequest(apiUrl, labelSuggestionPrompt); 31 | if (!labelResponse) return; 32 | const suggestedLabel = labelResponse.candidates[0].content.parts[0].text.trim(); 33 | Logger.log(`Suggested Label: ${suggestedLabel}`); 34 | // Step 4: Apply the label and populate fields 35 | if (labelMap[suggestedLabel]) { 36 | const labelData = labelMap[suggestedLabel]; 37 | const labelId = labelData.id; 38 | if (labelData.fields && labelData.fields.length > 0) { 39 | const fieldPrompt = generateFieldPrompt(labelData.fields, documentContent); 40 | const fieldResponse = makeGeminiApiRequest(apiUrl, fieldPrompt); 41 | if (fieldResponse) { 42 | const fieldValues = parseFieldData(fieldResponse.candidates[0].content.parts[0].text.trim()); 43 | applyLabelAndPopulateFields(driveFileID, labelId, labelData.fields, fieldValues); 44 | } else { 45 | Logger.log('Failed to fetch field data for label.'); 46 | } 47 | } else { 48 | // Apply label without fields 49 | const addLabelRequest = Drive.newModifyLabelsRequest() 50 | .setLabelModifications([Drive.newLabelModification().setLabelId(labelId)]); 51 | const addLabelResponse = Drive.Files.modifyLabels(addLabelRequest, driveFileID); 52 | Logger.log(`Labels applied: ${JSON.stringify(addLabelResponse.modifiedLabels)}`); 53 | } 54 | } else { 55 | Logger.log(`Suggested label "${suggestedLabel}" not found among available labels.`); 56 | } 57 | } catch (error) { 58 | Logger.log(`Failed to analyze and apply label: ${error.message}`); 59 | } 60 | } 61 | 62 | function makeGeminiApiRequest(apiUrl, prompt) { 63 | const requestBody = { contents: [{ parts: [{ text: prompt }] }] }; 64 | const options = { 65 | method: 'post', 66 | contentType: 'application/json', 67 | payload: JSON.stringify(requestBody), 68 | muteHttpExceptions: true 69 | }; 70 | const response = UrlFetchApp.fetch(apiUrl, options); 71 | const responseCode = response.getResponseCode(); 72 | const responseText = response.getContentText(); 73 | Logger.log(`Gemini API Response Code: ${responseCode}`); 74 | Logger.log(`Gemini API Response Text: ${responseText}`); 75 | if (responseCode !== 200) { 76 | Logger.log(`Error calling Gemini API: ${responseText}`); 77 | return null; 78 | } 79 | try { 80 | return JSON.parse(responseText); 81 | } catch (e) { 82 | Logger.log(`Error parsing Gemini API response: ${e.message}`); 83 | return null; 84 | } 85 | } 86 | 87 | function generateFieldPrompt(fields, documentContent) { 88 | const fieldNames = fields.map(field => field.properties.displayName).join(", "); 89 | return `Analyze the document and return ONLY a JSON object with the following keys: ${fieldNames}. 90 | No markdown. No additional text. Just valid JSON. 91 | Document Content: 92 | ${documentContent}`; 93 | } 94 | 95 | function parseFieldData(fieldData) { 96 | try { 97 | // Strip common markdown code blocks (e.g., ```json ... ```) 98 | let cleanData = fieldData 99 | .replace(/```json\s*/g, '') // Remove ```json 100 | .replace(/```\s*/g, '') // Remove ``` 101 | .replace(/^\s*[\n\r]+|[\n\r]+\s*$/g, '') // Trim leading/trailing newlines 102 | .trim(); 103 | 104 | // Fallback: If still invalid, try to extract JSON substring (between { and }) 105 | if (!cleanData.startsWith('{') || !cleanData.endsWith('}')) { 106 | const jsonMatch = cleanData.match(/\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/); 107 | if (jsonMatch) { 108 | cleanData = jsonMatch[0]; 109 | } else { 110 | throw new Error('No valid JSON object found'); 111 | } 112 | } 113 | 114 | const fieldValues = JSON.parse(cleanData); 115 | Logger.log(`Parsed field values: ${JSON.stringify(fieldValues)}`); 116 | return fieldValues; 117 | } catch (e) { 118 | Logger.log(`Error parsing JSON field data: ${e.message}. Raw data: ${fieldData}`); 119 | return {}; 120 | } 121 | } 122 | 123 | function applyLabelAndPopulateFields(fileId, labelId, fields, fieldValues) { 124 | try { 125 | // Build field modifications as plain objects based on type 126 | const fieldModifications = fields.map(field => { 127 | const displayName = field.properties.displayName; 128 | const fieldValue = fieldValues[displayName]; 129 | if (!fieldValue) { 130 | Logger.log(`No value for field: ${displayName}`); 131 | return null; 132 | } 133 | 134 | let fieldMod = { 135 | kind: "drive#labelFieldModification", 136 | fieldId: field.id 137 | }; 138 | 139 | if (field.selectionOptions) { 140 | const choice = field.selectionOptions.choices.find(choice => 141 | choice.properties.displayName.toLowerCase() === fieldValue.toLowerCase() 142 | ); 143 | if (choice) { 144 | fieldMod.setSelectionValues = [choice.id]; 145 | } else { 146 | Logger.log(`No matching choice for ${displayName}: ${fieldValue}`); 147 | return null; 148 | } 149 | } else if (field.integerOptions) { 150 | const intValue = parseInt(fieldValue); 151 | if (!isNaN(intValue)) { 152 | fieldMod.setIntegerValues = [intValue.toString()]; 153 | } else { 154 | Logger.log(`Invalid integer for ${displayName}: ${fieldValue}`); 155 | return null; 156 | } 157 | } else if (field.dateOptions) { 158 | // Parse to ISO "2025-11-28" 159 | const date = new Date(fieldValue); 160 | if (!isNaN(date.getTime())) { 161 | const isoDate = date.toISOString().split('T')[0]; 162 | fieldMod.setDateValues = [isoDate]; 163 | } else { 164 | Logger.log(`Invalid date for ${displayName}: ${fieldValue}`); 165 | return null; 166 | } 167 | } else if (field.textOptions) { 168 | fieldMod.setTextValues = [fieldValue]; 169 | } else { 170 | Logger.log(`Unknown field type for ${displayName}`); 171 | return null; 172 | } 173 | 174 | return fieldMod; 175 | }).filter(mod => mod !== null); 176 | 177 | if (fieldModifications.length === 0) { 178 | Logger.log('No valid modifications for fields.'); 179 | return; 180 | } 181 | 182 | // Build request as plain object (applies label + fields atomically) 183 | const request = { 184 | kind: "drive#modifyLabelsRequest", 185 | labelModifications: [ 186 | { 187 | kind: "drive#labelModification", 188 | labelId: labelId, 189 | fieldModifications: fieldModifications, 190 | removeLabel: false // Ensure label is kept/applied 191 | } 192 | ] 193 | }; 194 | Logger.log(`Request body: ${JSON.stringify(request)}`); 195 | 196 | const response = Drive.Files.modifyLabels(request, fileId); 197 | Logger.log(`Label and fields applied: ${JSON.stringify(response.modifiedLabels)}`); 198 | } catch (error) { 199 | Logger.log(`Error while applying label and populating fields: ${error.message}`); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /DriveLabelsAutomation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tutorial: Complete Google Drive Labels Automation in Apps Script 3 | * 4 | * Copy-paste this entire code into a new Google Apps Script project (script.google.com). 5 | * Prerequisites: 6 | * 1. Enable Drive API v3 and Drive Labels API v2 (Editor > Services > + > Search/Add each). 7 | * 2. Re-authorize permissions (Run > Review permissions). 8 | * 3. Replace fileId in fullLabelAutomation() with your actual file ID. 9 | * 4. Ensure "Invoice" label exists and is published in Google Workspace Admin > Drive > Labels. 10 | * 11 | * 12 | * This script: 13 | * - Fetches available labels. 14 | * - Mocks suggestion as "Invoice" (replace with Gemini for dynamic). 15 | * - Fetches fields for the label. 16 | * - Applies label + populates fields with mock values (from your invoice example). 17 | * - Logs everything; check file in Drive > Details > Labels for results. 18 | */ 19 | 20 | // 1. Fetch Currently Available Labels and Their IDs 21 | function fetchAvailableLabels() { 22 | try { 23 | const response = DriveLabels.Labels.list({ view: "LABEL_VIEW_FULL" }); 24 | const labels = response.labels || []; 25 | if (labels.length === 0) { 26 | Logger.log("No labels found. Check Workspace admin for published labels."); 27 | return {}; 28 | } 29 | 30 | const labelMap = {}; 31 | labels.forEach(label => { 32 | if (label.properties?.title) { 33 | labelMap[label.properties.title] = label.id; 34 | } 35 | }); 36 | 37 | Logger.log(`Fetched ${labels.length} labels: ${JSON.stringify(labelMap)}`); 38 | return labelMap; 39 | } catch (error) { 40 | Logger.log(`Error fetching labels: ${error.message}`); 41 | return {}; 42 | } 43 | } 44 | 45 | // 2. Fetch Label Fields and Their IDs (list-based to avoid 404) 46 | function fetchLabelFields(labelId) { 47 | try { 48 | if (!labelId) { 49 | throw new Error("labelId is required. Run fetchAvailableLabels() first and copy an ID."); 50 | } 51 | 52 | const response = DriveLabels.Labels.list({ view: "LABEL_VIEW_FULL" }); 53 | const labels = response.labels || []; 54 | if (labels.length === 0) { 55 | Logger.log("No labels found. Check Workspace admin for published labels."); 56 | return {}; 57 | } 58 | 59 | const targetLabel = labels.find(label => label.id === labelId); 60 | if (!targetLabel) { 61 | Logger.log(`Label ID "${labelId}" not found in available labels. Run fetchAvailableLabels() to verify.`); 62 | return {}; 63 | } 64 | 65 | const fields = targetLabel.fields || []; 66 | if (fields.length === 0) { 67 | Logger.log(`No fields found for label ${labelId}.`); 68 | return {}; 69 | } 70 | 71 | const fieldMap = {}; 72 | fields.forEach(field => { 73 | const displayName = field.properties?.displayName; 74 | if (displayName) { 75 | let type = 'text'; // Default 76 | if (field.integerOptions) type = 'integer'; 77 | else if (field.dateOptions) type = 'date'; 78 | else if (field.selectionOptions) type = 'selection'; 79 | 80 | fieldMap[displayName] = { 81 | id: field.id, 82 | type: type 83 | }; 84 | } 85 | }); 86 | 87 | Logger.log(`Fetched ${fields.length} fields for label ${labelId}: ${JSON.stringify(fieldMap)}`); 88 | return fieldMap; 89 | } catch (error) { 90 | Logger.log(`Error fetching fields for label ${labelId}: ${error.message}`); 91 | return {}; 92 | } 93 | } 94 | 95 | // 3. Apply Label to File 96 | function applyLabelToFile(fileId, labelId) { 97 | try { 98 | if (!fileId || !labelId) { 99 | throw new Error("fileId and labelId are required."); 100 | } 101 | 102 | const request = { 103 | kind: "drive#modifyLabelsRequest", 104 | labelModifications: [ 105 | { 106 | kind: "drive#labelModification", 107 | labelId: labelId, 108 | removeLabel: false 109 | } 110 | ] 111 | }; 112 | 113 | const response = Drive.Files.modifyLabels(request, fileId); 114 | Logger.log(`Label ${labelId} applied to file ${fileId}: ${JSON.stringify(response.modifiedLabels)}`); 115 | return response.modifiedLabels || []; 116 | } catch (error) { 117 | Logger.log(`Error applying label ${labelId} to file ${fileId}: ${error.message}`); 118 | return []; 119 | } 120 | } 121 | 122 | // 4. Apply Label and Update Fields 123 | function applyLabelAndUpdateFields(fileId, labelId, fields) { 124 | try { 125 | if (!fileId || !labelId || !fields || fields.length === 0) { 126 | throw new Error("fileId, labelId, and non-empty fields array are required."); 127 | } 128 | 129 | const fieldModifications = fields.map(field => { 130 | let fieldMod = { 131 | kind: "drive#labelFieldModification", 132 | fieldId: field.id 133 | }; 134 | 135 | const value = field.value; 136 | if (!value) { 137 | Logger.log(`Skipping field ${field.id}: No value provided.`); 138 | return null; 139 | } 140 | 141 | switch (field.type) { 142 | case 'selection': 143 | fieldMod.setSelectionValues = [value]; 144 | break; 145 | case 'integer': 146 | const intVal = parseInt(value); 147 | if (!isNaN(intVal)) { 148 | fieldMod.setIntegerValues = [intVal.toString()]; 149 | } else { 150 | Logger.log(`Invalid integer for field ${field.id}: ${value}`); 151 | return null; 152 | } 153 | break; 154 | case 'date': 155 | const date = new Date(value); 156 | if (!isNaN(date.getTime())) { 157 | const isoDate = date.toISOString().split('T')[0]; 158 | fieldMod.setDateValues = [isoDate]; 159 | } else { 160 | Logger.log(`Invalid date for field ${field.id}: ${value}`); 161 | return null; 162 | } 163 | break; 164 | case 'text': 165 | default: 166 | fieldMod.setTextValues = [value]; 167 | break; 168 | } 169 | 170 | return fieldMod; 171 | }).filter(mod => mod !== null); 172 | 173 | if (fieldModifications.length === 0) { 174 | Logger.log('No valid field modifications; applying label only.'); 175 | return applyLabelToFile(fileId, labelId); 176 | } 177 | 178 | const request = { 179 | kind: "drive#modifyLabelsRequest", 180 | labelModifications: [ 181 | { 182 | kind: "drive#labelModification", 183 | labelId: labelId, 184 | fieldModifications: fieldModifications, 185 | removeLabel: false 186 | } 187 | ] 188 | }; 189 | 190 | const response = Drive.Files.modifyLabels(request, fileId); 191 | Logger.log(`Label ${labelId} and fields applied to file ${fileId}: ${JSON.stringify(response.modifiedLabels)}`); 192 | return response.modifiedLabels || []; 193 | } catch (error) { 194 | Logger.log(`Error applying label and fields to file ${fileId}: ${error.message}`); 195 | return []; 196 | } 197 | } 198 | 199 | // Full Label Automation: Chains all steps (mock suggestion/values for demo) 200 | function fullLabelAutomation(fileId) { 201 | try { 202 | Logger.log(`Starting automation for file: ${fileId}`); 203 | 204 | // Step 1: Fetch labels 205 | const labelMap = fetchAvailableLabels(); 206 | if (Object.keys(labelMap).length === 0) { 207 | Logger.log("No labels available. Exiting."); 208 | return; 209 | } 210 | 211 | // Step 2: Mock suggestion (replace with Gemini for dynamic AI) 212 | const suggestedLabel = "Invoice"; // Hardcoded for demo; integrate Gemini here 213 | const labelId = labelMap[suggestedLabel]; 214 | if (!labelId) { 215 | Logger.log(`Suggested label "${suggestedLabel}" not found. Available: ${Object.keys(labelMap).join(", ")}. Exiting.`); 216 | return; 217 | } 218 | Logger.log(`Suggested and found label: ${suggestedLabel} (ID: ${labelId})`); 219 | 220 | // Step 3: Fetch fields 221 | const fieldMap = fetchLabelFields(labelId); 222 | if (Object.keys(fieldMap).length === 0) { 223 | Logger.log("No fields for label. Applying label only."); 224 | applyLabelToFile(fileId, labelId); 225 | return; 226 | } 227 | 228 | // Step 4: Build fields array with mock values (from your invoice doc; extract dynamically in prod) 229 | const fields = Object.keys(fieldMap).map(key => ({ 230 | id: fieldMap[key].id, 231 | type: fieldMap[key].type, 232 | value: key === "Invoice Number" ? "100556" : 233 | key === "Invoice Date" ? "November 28, 2025" : 234 | key === "Vendor Name" ? "Spark Electrical Services" : "Default Value" 235 | })); 236 | 237 | // Apply label + fields 238 | applyLabelAndUpdateFields(fileId, labelId, fields); 239 | Logger.log("Automation complete! Check file labels in Drive."); 240 | } catch (error) { 241 | Logger.log(`Automation failed: ${error.message}`); 242 | } 243 | } 244 | 245 | function runDemo() { 246 | // Replace this with your actual File ID 247 | const myFileId = "your_file_id"; 248 | 249 | fullLabelAutomation(myFileId); 250 | } 251 | --------------------------------------------------------------------------------