Format imported table - google-apps-script

I've been attempting to import a table of events from the following website:  https://scpgajt.bluegolf.com/bluegolf/scpgajt23/schedule/index.htm?type=2&display=champ (and others similar in structure).
I am attempting to reproduce the example website table on a Google Sheet where I would later add a check-box column and then select the events I need (which would copy the selection to another sheet for personalized planning).
So far, I have been able to use copied/pasted Apps Script coding found on Stack Overflow (see my Example Sheet HERE) and this =ImportTableHTML(A1,1) formula on the sheet to pull the table from the site into the sheet.
This Apps Script method has finally produced a complete list of events, however, the results are horribly formatted incorrectly (see Example Sheet 1 - Scrape Import / Raw). The result I am looking for should format close to the the original columns and rows as the original table, or filter and distribute the pulled data into certain specified cells (see Example Sheet 2 - Model Result).
This is the farthest I have been able to get, thanks to the scripts found on Stack Overflow, combining scripts posted in Replacing =ImportHTML with URLFetchApp) and Creating a UrlFetchApp script to replace the Google Sheet importHTML function.
Unfortunately, now I cannot figure out the options in the script to affect formatting / distributing of the results into the proper cells.
Is it possible to reproduce the table in my example sheet with proper or modifiable formatting?
The site I am attempting to capture table data from
The resulting import using =ImportTableHTML(A1,1)
The way the imported data should be parsed and distributed
App Script Code I am currently using:
function importTableHTML(url,n){
var html = '<table' + UrlFetchApp.fetch(url, {muteHttpExceptions: true}).getContentText().replace(/(\r\n|\n|\r|\t| )/gm,"").match(/(?<=\<table).*(?=\<\/table)/g) + '</table>';
var trs = [...html.matchAll(/<tr[\s\S\w]+?<\/tr>/g)];
var data = [];
for (var i=0;i<trs.length;i++){
var tds = [...trs[i][0].matchAll(/<(td|th)[\s\S\w]+?<\/(td|th)>/g)];
var prov = [];
for (var j=0;j<tds.length;j++){
donnee=tds[j][0].match(/(?<=\>).*(?=\<\/)/g)[0];
prov.push(stripTags(donnee));
}
data.push(prov);
}
return(data);
}
function stripTags(body) {
var regex = /(<([^>]+)>)/ig;
return body.replace(regex,"");
}

Related

How to remove google apps script "Exception: Conditional format rule cannot reference a different sheet."

I have several similar spreadsheets I am working with. Each spreadsheet has 3 sheets of the same name: "Plan", "Class", and "Coach". I want the conditional formatting within the sheets of my spreadsheets to be exactly the same. I was helped a little while ago in creating a script that could do so, and it worked perfectly fine in my test spreadsheets (I don't like to put something in my actual spreadsheets until I know it will work properly). The script uses getConditionalFormatRules on all the sheets from my source sheet and then uses setConditionalFormatRules and the other sheet ID's to recreate the source spreadsheet's rules in my other spreadsheets. I have now placed my code into the actual spreadsheet, but I am now experiencing a problem.
Every time I run my script, I get this error:
Exception: Conditional format rule cannot reference a different sheet.
I get this message when the script is copying the conditional formatting from the sheet called "class". The other two sheets' formatting gets copied perfectly fine. I have gone over the code many times now, but I can find no error in my code. I have tried creating new sheets and starting over, and then referencing the recreated sheets, but I still get the same message. I have tried changing the sheet name as well, but to no avail (plus, I would rather not change the sheet name from "Class" because many of my other scripts would also have to change). I am very lost.
This is my current script:
function copyConditional(){
var targetT = SpreadsheetApp.openById("Tuesday_ID").getSheetByName("Plan");
var targetW = SpreadsheetApp.openById("Wednesday_ID").getSheetByName("Plan");
var targetTh = SpreadsheetApp.openById("Thursday_ID").getSheetByName("Plan");
var targetF = SpreadsheetApp.openById("Friday_ID").getSheetByName("Plan");
var targetS = SpreadsheetApp.openById("Saturday_ID").getSheetByName("Plan");
var source = SpreadsheetApp.openById("Monday_ID").getSheetByName("Plan");
var rules = source.getConditionalFormatRules();
targetT.setConditionalFormatRules(rules);
targetW.setConditionalFormatRules(rules);
targetTh.setConditionalFormatRules(rules);
targetF.setConditionalFormatRules(rules);
targetS.setConditionalFormatRules(rules);
var targetT2 = SpreadsheetApp.openById("").getSheetByName("Coach");
var targetW2 = SpreadsheetApp.openById("").getSheetByName("Coach");
var targetTh2 = SpreadsheetApp.openById("").getSheetByName("Coach");
var targetF2 = SpreadsheetApp.openById("").getSheetByName("Coach");
var targetS2 = SpreadsheetApp.openById("").getSheetByName("Coach");
var source2 = SpreadsheetApp.openById("").getSheetByName("Coach");
var rules2 = source2.getConditionalFormatRules();
targetT2.setConditionalFormatRules(rules2);
targetW2.setConditionalFormatRules(rules2);
targetTh2.setConditionalFormatRules(rules2);
targetF2.setConditionalFormatRules(rules2);
targetS2.setConditionalFormatRules(rules2);
var targetT1 = SpreadsheetApp.openById("").getSheetByName("Class");
var targetW1 = SpreadsheetApp.openById("").getSheetByName("Class");
var targetTh1 = SpreadsheetApp.openById("").getSheetByName("Class");
var targetF1 = SpreadsheetApp.openById("").getSheetByName("Class");
var targetS1 = SpreadsheetApp.openById("").getSheetByName("Class");
var source1 = SpreadsheetApp.openById("").getSheetByName("Class");
var rules1 = source.getConditionalFormatRules();
targetT1.setConditionalFormatRules(rules1);
targetW1.setConditionalFormatRules(rules1);
targetTh1.setConditionalFormatRules(rules1);
targetF1.setConditionalFormatRules(rules1);
targetS1.setConditionalFormatRules(rules1);
}
The only thing I changed in this code was taking out my sheet ID's, but I can guarantee I had all the correct ID's in the right spots.
I would like to know what I can do to remove the exemption from my script, or if there is maybe another workaround to allow the script to function properly. Thank you!
Edit
This is an image of part of my sheet. The first two rows have rules that will change the background color and font color of a cell depending on its exact value. There is also one rule for every 6 columns (except for the first) that will change the cell's background color if any information at all is entered.
Edit 2
These are all the rules on sheet "Class" as of the moment. I do add rules pretty frequently, but following the same pattern. I am using my script because it can be very time consuming to have to add the same new rule to all 6 of my sheets individually.
My other sheet "Coach" has all the same "text is exactly" rules, except the range changes from A1:EO2 to A1:BF59. The other sheet "Plan", has all the same "text is exactly" rules, but changes the range to A1:AU59. Sheet "Plan" also has some unique rules, which I will add as well.
EDIT
I never found out why the script did not work on my original sheets. I had to recreate all of my sheets from scratch, rewrite my script in the new spreadsheets, and paste in the new spreadsheet ID's into my script to get my desired effect. At this point, I have determined there must have been some fundamental flaw in the sheet itself, and that my problem had nothing to do with my code.
I am not posting this as an answer, because creating new spreadsheets from scratch did not exactly solve the problem. I consider it a lengthy and annoying workaround more than a functioning solution. Thanks to everyone who tried to help though!

How to dumpValues & Data of a full column in google sheet via google script, in one operation?

I've been handling a big set of Data with automated scripts in google drive.
These script constantly update some cells in a column through different sheets.
Now here is the problem: Reading and writing in google scripts is super long. So I tried to get the column via getValues, dump it in a an array, do my researches and my mods there and write the whole array back to the sheet.
However, this erases the formulas that might have been in the column (getValues would then return the result of the formula). Using getFormulas wouldnt give back the values. I could write and read formulas and values, but that would add operations when I'm trying to save time.
How come google doesn't provide a way to directly dump a String content of each cell? Any work around?
A.
Well, I could find any google specified answer, but I developed a merge function and it's actually fast enough, if anyone needs it :
function dumpColumn(sheet, index){
var range = sheet.getRange(1, index, sheet.getLastRow(), 1);
var arrV = range.getValues();
var arrF = range.getFormulas();
for(i = 0; i<arrF.length; i++){
if(arrF[i][0] != "")
arrV[i][0] =arrF[i][0];
}
return arrV;
}

Split data into different Google Sheets files

I have this one main file in Google Sheets. Now, I need to split this data such that its gets sent to " X " number of new files ( not a separate sheet). The data that is being sent is a subset of the data from the main file.
Is it possible to create these new workbooks and populate them with this subset data?
I tried creating new sheets, but the script is only working on creating it within the currently active sheet, and I cant find anything that is copying a range to a new sheet.
so i did figure out how to work around this thing.
now i didnt want to create a copy of the file. i wanted it to work somewhat like importrange.
var target2 = SpreadsheetApp.openById("Sheet url");
var target_sheet2 = target2.getSheetByName("Sheet1");
var source_rangefor2 = source_sheet.getRange("A7:C11");
var target_rangefor2 = target_sheet2.getRange("A1:C5");
var values = source_rangefor2.getValues();
target_rangefor2.setValues(values);
this copies the said range from the main sheet to the given range in the target sheet.
Hope this helps other people.

Google Spreadsheets with Form Vinculated copy

I'm trying to copy a spreadsheet with the responses of a form thought drive API.
https://developers.google.com/drive/v2/reference/files/copy
All files from my drive are working fine but only this spreadsheet that persist my form answers that create a duplicate form instead of only copying the spreadsheet itself.
You can try to reproduce the problem using the given Id. After copying you will notice that both spread sheet and form will be copied. This should not be a problem if I could erase the form but in the response of the copy procedure I don't get any advice about the form that is being copied together.
File id: 0Aqq-9JjR-lUydHRKVEJ2SThGMjJlVjVqczkyWlVCWUE
Please, help me. I'm desperate.
If you are only trying to copy the spreadsheet of the form, try this:
var fromSheet = ***whatever***;//this is the sheet attached to your form
var toSheet = ***whatever***;//this will be the sheet that you are copying to
var range = **whatever***;//this is the range you are copying over. If you are using dynamic ranges (ie varying number of rows), you may want to **getDataRange()** and **getLastRow** to build a more flexible range
function myCopyCat(){
myValues = fromSheet.getRange(range).getValues();//'copies' all of your data within range from fromSheet to an array
toSheet.getRange(range).setValues(myValues);//'pastes' all of your data into cells on toSheet
}

Switch rows and columns in EmbeddedChart

I am using Google Docs and Google Apps Script to make some auto generated report for our sprint.
I would like to add a Chart to my Sheet, and everything works fine with the following code:
var lChartBuilder = SpreadsheetApp.getActiveSheet().newChart();
lChartBuilder.addRange(SpreadsheetApp.getActive().getRange("Tasks!C39:S40"));
lChartBuilder.setOption("title", "Task Burndown");
var lChart = lChartBuilder.asLineChart().build();
SpreadsheetApp.getActiveSheet().insertChart(lChart);
But my series are organized horizontally, not vertically. In the editor I have seen the option "Switch rows / columns" and the other option "Use column C for labels"
I have tried many options (like lChartBuilder.setOption("reverseCategories", true); or lChartBuilder.setOption("isStacked", true);), but they seem all related to the last tab, I fear, not the Start tab.
So, is there a way (other than transposing my data) to do that or must I fire the chart editor manually to fix this each time I generate it?
Bonus question: how do I then set (through Google Apps Script as well) that the first row/column is a header and serves as legend?
For the bonus question, I have your bonus answer.
Use this option when creating/modifying your graph:
setOption('useFirstColumnAsDomain','true')
For an embedded chart, you will need to transpose your data, because the embedded chart uses spreadsheet data, and does not support transposition itself. However, you can handle this programmatically, and the transpose operation itself is simple.
In the code below, I'm assuming existence of a sheet named "Scratch" that will be used to store the working copy of the transposed data. You can create and then hide this sheet, so that it's not in the way.
// Uses 2D Arrays Library, see https://sites.google.com/site/scriptsexamples/custom-methods/2d-arrays-library
function buildChart() {
var taskSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tasks");
var lChartBuilder = taskSheet.newChart(); taskSheet.removeChart(chart)
var srcData = taskSheet.getRange("Tasks!C39:S40").getValues();
// Transpose the table (using 2D Array Library)
var scratchData = ArrayLib.transpose(srcData);
var numRows = scratchData.length;
var numCols = scratchData[0].length; // assume all rows are same width
// Write scratch values to scratch sheet.
var scratchSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Scratch");
scratchSheet.getRange(1, 1, numRows, numCols).setValues( scratchData );
SpreadsheetApp.flush();
lChartBuilder.addRange(scratchSheet.getDataRange());
lChartBuilder.setOption("title", "Task Burndown");
var lChart = lChartBuilder.asLineChart().setPosition(1, 1, 1, 1).build();
taskSheet.insertChart(lChart);
};
For the bonus... Having the data formatted this way ensures the first row/column is a header and serves as legend.
Now, you've still got some problems to solve.
This code creates a new chart every time it runs, but you probably want to modify it instead. (Instead, you'll want to update the transposed data, and modify the chart to pick up the new range, if it changed.)
The X axis doesn't show all the task names - you may be able to control that.
You say you did not want to transpose your data. Unfortunately, there just isn't any way around that with the current incarnation of Charts - you've got to massage your data yourself. Inserting a widget with a different charting library could work, but widget support for Sheets has been deprecated. Fortunately, I had similar code already, so it was less work than it looks like. You should look at some of the other filtering capabilities in ArrayLib, you can do a lot with it.