// Constants for the pair arrays const AGGRESSIVE_PAIRS = [ ['wood+', 'fire-'], ['wood-', 'earth+'], ['fire+', 'earth-'], ['fire-', 'metal+'], ['earth+', 'metal-'], ['earth-', 'water+'], ['metal+', 'water-'], ['metal-', 'wood+'], ['water+', 'wood-'], ['water-', 'fire+'] ]; const PROTECTIVE_PAIRS = [ ['wood+', 'metal-'], ['wood-', 'water+'], ['fire+', 'water-'], ['fire-', 'wood+'], ['earth+', 'wood-'], ['earth-', 'fire+'], ['metal+', 'fire-'], ['metal-', 'earth+'], ['water+', 'earth-'], ['water-', 'metal+'] ]; const SUBTLE_PAIRS = [ ['wood+', 'wood+'], ['wood-', 'wood-'], ['fire+', 'fire+'], ['fire-', 'fire-'], ['earth+', 'earth+'], ['earth-', 'earth-'], ['metal+', 'metal+'], ['metal-', 'metal-'], ['water+', 'water+'], ['water-', 'water-'] ]; const OVERT_PAIRS = [ ['wood+', 'metal+'], ['wood-', 'metal-'], ['fire+', 'water+'], ['fire-', 'water-'], ['earth+', 'wood+'], ['earth-', 'wood-'], ['metal+', 'fire+'], ['metal-', 'fire-'], ['water+', 'earth+'], ['water-', 'earth-'] ]; // The main function that handles GET requests function doGet(e) { var callback = e.parameter.callback; // Get the date parameter from the request var userDateStr = e.parameter.date; if (!userDateStr) { var errorResponse = 'No date provided'; if (callback) { return ContentService.createTextOutput(callback + "('" + errorResponse + "')") .setMimeType(ContentService.MimeType.JAVASCRIPT); } else { return ContentService.createTextOutput(errorResponse) .setMimeType(ContentService.MimeType.TEXT); } } // Get the base date type parameter (default to 'user') var baseDateType = e.parameter.baseType || 'user'; // 'user' or 'today' // Get the year range parameters var startYear = parseInt(e.parameter.startYear) || -9; // Default to 9 years before var endYear = parseInt(e.parameter.endYear) || -2; // Default to 9 years after // Ensure startYear is less than or equal to endYear if (startYear > endYear) { var temp = startYear; startYear = endYear; endYear = temp; } // Determine the base date for filtering var baseDate; if (baseDateType === 'today') { baseDate = new Date(); } else { // Parse the user date for filtering var filterDateStr = e.parameter.filterDate; if (filterDateStr) { baseDate = parseDateValue(filterDateStr); if (isNaN(baseDate.getTime())) { var errorResponse = 'Invalid filter date format'; if (callback) { return ContentService.createTextOutput(callback + "('" + errorResponse + "')") .setMimeType(ContentService.MimeType.JAVASCRIPT); } else { return ContentService.createTextOutput(errorResponse) .setMimeType(ContentService.MimeType.TEXT); } } } else { // If no filter date provided, use the user date baseDate = parseDateValue(userDateStr); } } // Parse the user date for element calculation var userDate = parseDateValue(userDateStr); if (isNaN(userDate.getTime())) { var errorResponse = 'Invalid date format'; if (callback) { return ContentService.createTextOutput(callback + "('" + errorResponse + "')") .setMimeType(ContentService.MimeType.JAVASCRIPT); } else { return ContentService.createTextOutput(errorResponse) .setMimeType(ContentService.MimeType.TEXT); } } // List of planets to process var planets = ['moon', 'mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn', 'marsjupiter', 'jupitersaturn']; // Read the data from the spreadsheet var ss = SpreadsheetApp.getActiveSpreadsheet(); var sheet = ss.getSheetByName('Sheet1'); // Ensure the sheet name is correct if (!sheet) { var errorResponse = 'Sheet not found'; if (callback) { return ContentService.createTextOutput(callback + "('" + errorResponse + "')") .setMimeType(ContentService.MimeType.JAVASCRIPT); } else { return ContentService.createTextOutput(errorResponse) .setMimeType(ContentService.MimeType.TEXT); } } var dataRange = sheet.getDataRange(); var dataValues = dataRange.getValues(); // Data structure: an array of objects [{planet, date, element}] var data = []; for (var i = 0; i < dataValues.length; i++) { var row = dataValues[i]; var planet = row[0]; var dateValue = row[1]; var element = row[2]; // Parse date var date = parseDateValue(dateValue); if (isNaN(date.getTime())) { // Invalid date, skip continue; } data.push({ planet: planet, date: date, element: element }); } Logger.log('Total data entries: ' + data.length); // For each planet, find the user element var results = []; for (var i = 0; i < planets.length; i++) { var planet = planets[i]; // Get all data for the planet, sorted ascendingly var allPlanetData = data .filter(function(item) { return item.planet.toLowerCase() === planet.toLowerCase(); }) .sort(function(a, b) { return a.date.getTime() - b.date.getTime(); // ascending order }); Logger.log('Planet: ' + planet + ', Total entries: ' + allPlanetData.length); if (allPlanetData.length === 0) { // No data for this planet continue; } // Find the latest entry where date <= userDate var latestEntry = null; for (var j = allPlanetData.length - 1; j >= 0; j--) { if (allPlanetData[j].date.getTime() <= userDate.getTime()) { latestEntry = allPlanetData[j]; break; } } if (!latestEntry) { // No entries before or on userDate continue; } var userelement = latestEntry.element; Logger.log('Latest entry for ' + planet + ': ' + JSON.stringify(latestEntry)); // Find the index of latestEntry in allPlanetData var latestIndex = allPlanetData.findIndex(function(d) { return d.date.getTime() === latestEntry.date.getTime() && d.element === latestEntry.element; }); // Determine the nextDate var nextDate = null; if (latestIndex >= 0 && latestIndex < allPlanetData.length - 1) { nextDate = allPlanetData[latestIndex + 1].date.toISOString().split('T')[0]; } // Now, for the user element, find matching dates from allPlanetData var userResults = []; // Process AGGRESSIVE_PAIRS var aggressivePairelements = AGGRESSIVE_PAIRS .filter(function(pair) { return pair[0] === userelement; }) .map(function(pair) { return pair[1]; }); var aggressiveDates = getDatesForElements(allPlanetData, aggressivePairelements); aggressiveDates = labelDates(aggressiveDates, 'Aggressively Superempowering Dates'); userResults = userResults.concat(aggressiveDates); // Process PROTECTIVE_PAIRS var protectivePairelements = PROTECTIVE_PAIRS .filter(function(pair) { return pair[0] === userelement; }) .map(function(pair) { return pair[1]; }); var protectiveDates = getDatesForElements(allPlanetData, protectivePairelements); protectiveDates = labelDates(protectiveDates, 'Protectively Superempowered Dates'); userResults = userResults.concat(protectiveDates); // Process SUBTLE_PAIRS var subtlePairelements = SUBTLE_PAIRS .filter(function(pair) { return pair[0] === userelement; }) .map(function(pair) { return pair[1]; }); var subtleDates = getDatesForElements(allPlanetData, subtlePairelements); subtleDates = labelDates(subtleDates, 'Subtle Destructive Betrayal Dates'); userResults = userResults.concat(subtleDates); // Process OVERT_PAIRS var overtPairelements = OVERT_PAIRS .filter(function(pair) { return pair[0] === userelement; }) .map(function(pair) { return pair[1]; }); var overtDates = getDatesForElements(allPlanetData, overtPairelements); overtDates = labelDates(overtDates, 'Overtly and Aggressively Destructive Dates'); userResults = userResults.concat(overtDates); Logger.log('Total paired dates before filtering: ' + userResults.length); // Filter dates within specified year range of baseDate var filteredResults = userResults.filter(function(item) { var itemDate = item.date; var baseTimestamp = baseDate.getTime(); var itemTimestamp = itemDate.getTime(); // Calculate year differences var yearDiff = (itemTimestamp - baseTimestamp) / (1000 * 60 * 60 * 24 * 365.25); // Check if the date falls within the specified range return yearDiff >= startYear && yearDiff <= endYear; }); Logger.log('Filtered dates between ' + startYear + ' and ' + endYear + ' years: ' + filteredResults.length); // For each date, get the next date from allPlanetData for (var j = 0; j < filteredResults.length; j++) { var currentDate = filteredResults[j].date; // Find the index of currentDate in allPlanetData var index = allPlanetData.findIndex(function(d) { return d.date.getTime() === currentDate.getTime() && d.element === filteredResults[j].element; }); if (index >= 0 && index < allPlanetData.length - 1) { var pairedNextDate = allPlanetData[index + 1].date.toISOString().split('T')[0]; filteredResults[j].nextDate = pairedNextDate; } else { filteredResults[j].nextDate = null; } } // Add to results results.push({ planet: planet, userelement: userelement, userelementDate: latestEntry.date.toISOString().split('T')[0], userelementNextDate: nextDate, baseDate: baseDate.toISOString().split('T')[0], startYear: startYear, endYear: endYear, dates: filteredResults.map(function(item) { return { date: item.date.toISOString().split('T')[0], element: item.element, label: item.label, nextDate: item.nextDate }; }) }); } Logger.log('Final results: ' + JSON.stringify(results)); // Return the results as JSON or JSONP var jsonString = JSON.stringify(results); if (callback) { // Wrap the JSON in the callback function var response = callback + '(' + jsonString + ')'; return ContentService .createTextOutput(response) .setMimeType(ContentService.MimeType.JAVASCRIPT); } else { // Return JSON normally return ContentService .createTextOutput(jsonString) .setMimeType(ContentService.MimeType.JSON); } } // Helper function to parse date values function parseDateValue(dateValue) { if (dateValue instanceof Date) { return dateValue; } else if (typeof dateValue === 'string') { // Check for 'YYYY-MM-DD' format var dashParts = dateValue.split('-'); if (dashParts.length === 3) { var year = parseInt(dashParts[0], 10); var month = parseInt(dashParts[1], 10) - 1; var day = parseInt(dashParts[2], 10); return new Date(year, month, day); } // Check for 'MM/DD/YYYY' format var slashParts = dateValue.split('/'); if (slashParts.length === 3) { var month = parseInt(slashParts[0], 10) - 1; var day = parseInt(slashParts[1], 10); var year = parseInt(slashParts[2], 10); return new Date(year, month, day); } } return new Date('Invalid Date'); } // Helper function to get dates for specific elements function getDatesForElements(allPlanetData, elements) { var dates = []; for (var i = 0; i < allPlanetData.length; i++) { if (elements.indexOf(allPlanetData[i].element) >= 0) { dates.push({ date: allPlanetData[i].date, element: allPlanetData[i].element }); } } return dates; } // Helper function to label dates function labelDates(dates, label) { for (var i = 0; i < dates.length; i++) { dates[i].label = label; } return dates; }