Autocompletion based on large sheet (2000+ rows) - google-apps-script

When using Google Spreadsheets, and you want to use Validation on a cell based on a range of values, you get a pretty nice autocompletion feature that makes data entry much nicer.
My most common application is in an inventory-like situation, where I reference inventory items through some kind of hash code or part number. My frequently used hashes are committed to my brain, but when i need a new part or I need a variation on an old one, I want a little help making sure I have the correct part# selected.
I always find that I want additional row context with my autocompletion, so now I think I want to make a sidebar addon that has smarter searching rules and also includes more contextual data to ensure that I have the part# I meant. Once I am sure of the part#, one button can push the selected result over to the currently active row.
This solution is a bit "heavier" than data validation, but it does exactly what I want.
Assuming that my inventory source is another spreadsheet, what is a good way to set up my Addon-Script Project?
I was thinking that my sidebar would call an HtmlService function that utilizes Cache Service to hold my "hash list" and a few bits of context in memory. I don't think I am looking at a heavy jQuery solution (only to build the autocomplete dialog as I type), but that is really the whole purpose of this question!
Any thoughts on high level project overview? I am fairly new to Apps Scripts in general, especially since the newer API's have been coming out since 2013.

I did exactly that with my Budget Sheets, moved from Data Validation to Jquery's Autocomplete in a sidebar when the number of compositions jumped from 500 to 2.500, and it is a LOT faster, the search is faster than Autovalidation with 100 itens, the logic I use:
Database:
It's base data is in a Spreadsheet, each time it is updated, there's an OnEdit function that will trigger in several minutes a DB update, this is so that the function won't run unecesserary on several times consectively for the same edit.
The DB is then stored in simple text in JSON format on Google Drive, it is a 2MB file generated from the Spreadsheet data, using DriveApp.getFileById(id).setContet(JSON.stringify(myJsonDataFromSpreadsheet)), the file generation and saving takes up to 30 seconds, the file reading is around 4segs.
Sidebar:
Build a normal HTML - remember to use IFRAME option - and serve it, this is all in the docs, you'll have to send data from the HTML to GoogleScript (eg. the part# to insert) via google.script.run, and get back data (eg. the file with all the part numbers) with SuccessHandler in conjunction with google.script.run.
Main function references:
https://developers.google.com/apps-script/guides/html/reference/run
https://developers.google.com/apps-script/guides/dialogs#custom_sidebars
https://developers.google.com/apps-script/reference/drive/file -> First get the file With DriveApp.getFileById(id).

Related

Looking For Solution to Automate Calendar Event Creation from Google Sheets

Background:
At my (new) job, we have a number of Sales Reps who book gigs for our organization. Until now, they handle all of their sales tasks in our CRM (Pipedrive [PD]). They then use that information to create an "activity" (event) on PD's calendar, which is set up for two-way sync with our general work Calendar (Outlook [OL]).
Problem:
Between the number of different Reps who edit the PD Calendar, the variety in bookings for our org, and just general human error, our Calendar system is a NIGHTMARE. Formatting inconsistencies, typos, lack of necessary information, and even straight up wrong details make my job (creating a program around, and then organizing the talent for each booking) nearly impossible.
"Genius" "Solution":
Even though it's a mess, I hate the type of person who comes into a new position and instantly tries to make changes to a system that has (somehow) worked before their arrival. So instead I sat down with the reps, learned what their needs were for the calendar, as well as their process, and built a system that not only gives me what I need, but also cuts the busy work on their end in half.
Previously, they not only entered into into PD, and then manually created the events, they also created three separate supplementary Word files that documented the details, created a detailed schedule of events, and outlined contractual needs based on the type of event.
So to solve all of this, I created a Spreadsheet (SS) Template that had all the fluff prefilled, was able to fill in all the contact info from an external contact database, and fill in the entire event schedule based on "type" and "start time". So basically they just need to enter in "Where", When", and "What", and the SS would auto populate the rest.
How that benefits me, is I then painstakingly scoured YouTube, Reddit, and Stack overflow for information on how to build Scripts for Google. And managed to make a function that can pull all the information from their new fancy form, and automatically create a 100% accurate and consistent Event for me.
Everybody wins, right?
New Problem:
Due to things well beyond my current knowledge, I am unable to create a dummy proof way for the event to "Trigger". I know it has to do with user permissions, and the limitations of Simple Triggers yada yada, but I'm incredibly annoyed that I managed to eliminate so much busy work, entirely solve my problem in the process, and yet the thing blocking me in the end is that I have to actively go to my Script and hit "Run" for it to properly function.
(For any "Expedition Force" readers out there, this feels a lot like how for so long Skippy could program intricate FTL Jumps across Spacetime, but still needed a "filthy monkey" to push the "jump" button)
Relevant Additional Details:
As I said, we use Office, but we're likely switching to Google down the road (and as someone who works in the arts and has been a poor student/struggling artist for the last two decades, I am much more versed with Google than Microsoft. I've also rarely found anything of value within my needs that one could do that the other couldn't, so I've built this whole system in Google Apps Script. However, if someone finds a potential solution where this will work in Excel with VBA instead, then I'd learn to translate what I've written so far.
Also, the Script works. I can make it work 100% of the time via the Apps Dashboard (where they don't worry as much about permissions), so it's not a problem with the code itself, which is why I haven't posted it here (but I can if anyone has a reason to think it's relevant).
It is a frequent need for our Reps to go in and edit the gig (people get sick, availabilities change, or mistakes were made the first time, etc.), so to combat this, I've actually written two Scripts. The first is a "Create" script which takes all the information, creates an event, and then pulls the EventID # which it pastes in a safe cell on a different tab. The second is an "Edit" script which searches for the previously created event by ID, and then makes the changes as needed.
In addition to PD and Office, we also use Monday.com and have a Zappier account. But I'd rather avoid either of those if possible since they have apparently changed software packages every other year before I got here, and we are in the middle of a search for a new Executive Director, so I'd rather not have this whole thing come crashing down because one minor part of it depended on Zappier, which our next ED cut from the budget, ya know?
Potential Solutions:
I can make the whole thing run via their Installable Triggers. However, I find them limiting:
I can do it by "Open" which creates the event every single time the SS is opened (which is both obnoxious AND useless, since the edits happen AFTER it is opened).
I can do it by "Edit" or by "Change", but again this just creates a whole new Event every time. I even tested it and made three random edits on empty cells in a useless tab, and it made three identical events to match the first (although this could be a solution for my 2nd Script...). I wish they could do onEdit of specific Cell, because THAT would be useful.
I can do it by form submission.. which means I can instead make a form that they fill out that creates the whole doc, and after they submit, it creates the doc AND event, but this limits the idea of having a different SS for every contract.
And lastly, which I really want to avoid, since I don't trust the Reps who manage to spell the same name wrong three different ways in three different places with having to open Scripts and hit "run", I could create a macro that notifies ME whenever changes are made, and then I could go in and manually hit the button. But..... Is that REALLY the best solution?
I know that's a lot, but I'm more looking for creative coding ways to solve a general problem, rather than a specific fix to a single string. Anything that could make what I want happen, without making me do annoying extra steps, or without requiring me to trust others with a keyboard, I am open to suggestions!
You would benefit from more research on triggers gerenally, onEdit in particular. It is broadly true that onEdit is triggered "when anything was edited" but the script can be written so that it evaluates specific rows &/or columns &/or cells &/or sheets &/or values. It can do this by using Event Objects which involve including an argument (often the letter "e", or the word "event"). The Event objects provide a lot of information about the nature of the edit.
For example, if you had a checkbox in sheet "Sheet2", column D, and you wanted to trigger something if the checkbox value was changed to "checked", then an onEdit(e) script might include an IF statement such as:
if(e.range.getSheet().getName() == "Sheet2" && e.range.columnStart == 4 && e.value == true){//do stuff}
In the scenario described, an automated trigger might not be necessary - a user might select a row (or a cell in a row), and click the "Button" in order to execute the script. However, it might be desirable to "check" a checkbox so that there is no doubt about that row has been calendarized or not. The advantage of this is that an event is not calendarized twice.
The following script might be an appropriate example:
function buttonTrigger() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheetName = "Sheet1"
var sheet = ss.getSheetByName(sheetName)
// get the row to be calendarized
var row = sheet.getCurrentCell().getRow()
// Logger.log("DEBUG: the cursor is on row is "+row)
// check is this row is already processed
if (sheet.getRange(row,4).getValue == true){
// this row already processed
// insert abend code
return
}
else{
// this row is OK to update
// insert script to calendarize event
// update checkbox to show event is updated
// checkbox is in Column 4
sheet.getRange(row,4).setValue("true")
}
}
Example
Creating a button and assigning a script
There is a good explanation of this process in StackOverflow topic Run script only on click on button instead with open the sheets

App Script formula in Google spreadsheet cell gets invalidated and needs forced reload

I'm managing a student registration system based on Google docs. Students enter their registration data in a Google Form, the results are gathered in a Google spreadsheet ("Préinscriptions") that uses additional sheets to normalize the data. The data is then imported by another spreadsheet ("Inscriptions") to handle the registrations.
In two normalization sheets in the Google spreadsheet, I use custom Apps Script functions to normalize the data from the response sheets. The functions are used in a vertical concatenation array to gather normalized data from several response sheets into the normalization sheets. The formulas look like this:
On tab "Nouveaux":
={normalizeNouveau("FR", 'FR - Nouveau'!$A$1:$AB);normalizeNouveau("EN", 'EN - Nouveau'!$A$1:$AB); normalizeNouveau("ES", 'ES - Nouveau'!$A$1:$AB)}
On tab "Anciens":
={normalizeAncien("FR", 'FR - Ancien'!$A$1:$AB); normalizeAncien("EN", 'EN - Ancien'!$A$1:$AB); normalizeAncien("ES", 'ES - Ancien'!$A$1:$AB)}
normalizeNouveau and normalizeAncien are the custom functions that normlized data linewise. "FR - Nouveau", "EN - Nouveau" ... "ES - Ancien" are the response sheets.
Now the problem is that, while this all globally works great, the formulas that gather the normalized data, on both "Anciens" and "Nouveaux" sheets, sometimes get "disconnected" (for lack of a better word) and display error messages for each formula -- either a single one or 3, corresponding I expect to the three blocks of data produced by the various function calls. When that happens (usually several times a day), what I need to do is (simply :-| ) to cut the formulas from their original cells, validate the change, then paste them again. And then the data reappears normally.
When the problem happens:
After cut-pasting:
That is very problematic because, as I said, another file ("Inscriptions") uses that data to handle many aspects of the registration process, and when this happens, that whole files becomes empty and useless -- this can even lead to errors if the problem occurs at a time where I'm running an Apps Script function, because the data is then effectively absent. So when that happens in "Inscriptions", I need to go back to "Préinscriptions" to perform the little trick I was just describing. Doing it myself is annoying enough, but other people might need to use that file too and would simply get stuck or, worse, break something if the data simply disappears without warning.
Sometimes the connection seems to break between "Préinscriptions" and "Inscriptions", which loads the data from yet a third tab where "Nouveaux" and "Anciens" get merged. The same trick applies, although it may need several attempts to successfully show the data:
Can anyone tell me why this happen and if there's anything I can do to prevent it? I'm heavily relying on the stable dataflow between the forms, response sheets and registration spreadsheet for the system to work, and this is just breaking the workflow way too often to be negligible.

Can I use Google Apps Script to intelligently add page/column breaks based on how much space appended text will take up in a Google Doc?

I wrote a custom script that takes Google Sheet data and formats it to generate a Google Doc. The sheet data updates automatically and I have to periodically generate a new report for client use. (Overall this relatively seamless -- it has saved me a huge amount of time!)
Unfortunately, I always have to manually format the report afterward, adding column breaks to make sure individual records appear continuously without wrapping to the next column or page. Is there a way to have the script do this for me? So far as I've found, Apps Script doesn't have a way to "figure out" where on the page the cursor is or determine how much room is left before the bottom of the page.
As per Rubén's confirmation in the comments, there is no built-in feature to do what you want. But alternatively, there is a way to do what you want.
This answer shows that similar idea. You can have certain string/keywords that are placed within your source that can define where you would insert your break.
I'm not entirely sure how your data looks and it is hard to provide an exact answer. But if there is a pattern to your data in sheets and you can preemptively define where you can insert your column break, then that can be doable.
Basically, you will need to design how your data is being read (or even modify it) and format it into the document.
If you can provide a sample data and an expected output, then maybe users here can provide you a specific answer that will cater to your exact needs.

Does this risk me to exceed Google spreadsheet API calls quota?

I am using google spreadsheet to collaborate on some common data, and processing this data using spreadsheet macros and saving it there itself in cells. This approach is error prone as the macro functions which are processing the data depend on inputs apart from what is given as parameters.
Example of how the common data-looks
Content Sheet
Sections Sheet
Pages Sheet
Explanation of this common data
The three sheets are filled by various collaborators
Content sheet defines base elements of a page, they are referred (using their UUIDs) in the sections sheet (using macros), and finally all sections add together to make publishable html page.
The output html format varies depending upon the destination which are multiple - static html, Google Document, Google Slides, Word-press, MediaWiki etc. The formatting is done using GAS macros.
I tried various solutions, but nothing is working properly. Finally I have thought to keep the google spreadsheet as a data source only and move the entire computation to my local machine. This means I have to download the sheets data (3 sheets) which are some 1,000 rows, but can become voluminous with time.
Earlier when the entire computation was on google spreadsheets, I had to only fetch the final data and use it, which amounted to a lot fewer APIs calls. Referring to the example above, It means I would only fetch the output html of the "Pages sheet".
Q1) So my question is, given that I plan to move the entire computation to local machine, if I make only 3 APIs calls to bulk fetch data of three sheets, and pull the entire data, does it still counts as just 3 API calls, or big requests have a different API cost? Are there any hidden risks of over-running the quota in this approach?
Q2) And let's say i use hooks, both spreadsheet hooks and drive hooks to sync with real-time changes, what are my risks of running out of quota. Does each hook call constitute a API call. Any general advice on best practices for doing this?
Thanks
As per the documentation says by default Google Sheets API has a limit of 500 requests per 100 seconds per project, and 100 requests per 100 seconds per user unless you change it on your project.
A1) If you make a single request it counts as 1 request no matter how large the data is, for that reason I'd make a single request in order to GET the entire Spreadsheet rather than making 3 API calls and then make the whole process you mentioned before.
A2) If you've considered using Push Notifications keep in mind that you will need some extra configuration such as a domain.
As a workaround I recommend you to use Drive File Stream
Since you want move the computation to your local machine, this option would be better but will need some changes. For example, you could change the output to CSV in order to better handle the data and it still will be a Google Sheets valid file type and then host your app in the same machine or make your local machine accessible.

Is it possible for a GAS script to lock a Google Sheet so nobody else can alter it until the script is done

I am familiar with the Lock Service but that is only for locking scripts.
I have some code that will "process" a large Google Sheet. My script needs to re-order the rows. I need/want to make it so while the script is running nobody else can change the order. However, I still need another script to be able to append rows.
We use a Google Form for our team's intake. It appends rows to a sheet. I have an hourly job that will go through all the rows/records and "process them". I have a column that stores the last time a record/row was "processed". I want to sort on that column such that the "oldest" records are on top and then start processing from the top down. If the script fails or times out then the next iteration will just start over...
I know I could use getValues or getDisplayValues to get an array and then write the array back but I worry what would happen if someone sorted the rows as it would muck things up when writing the array back.
Is there some way to accomplish my goal? I want to be able to process the records, and maintain row order to avoid breaking my processing.
The way to block a spreadsheet "completely" is by changing the spreadsheet sharing settings. Remove all editors or change them to viewers, once your script finish, change them back as editors. In a extreme case, usa a second account to act as the owner of the critical files / spreadsheets and only use it for this purpose,so you could block your regular account for doing changes to the spreadsheet.
NOTE: A Google Form editResponseUrl could be used to edit the linked spreadsheet.
I'm facing a similar situation but I took a different approach, I'm using an index/key column (you could use the timestamp column) and using the index/key to save each edited row to the right position, then write the whole resulting array in a single operation (by using setValues()). In my case this is simple because I only require values, I'm not worried about notes, data validation, conditional formatting, comments, etc. and there isn't a Google Form linked to my spreadsheet.
Related
Google Spreadsheet -- get sharing permissions by script
Any way to share google docs programmatically?