How to automatically fill table based on certain rules (with JS) - javascript

I have shift planning table created by JS and shown to user as html. This table is currently filled by user. I have certain set of rules and when user breaks them, he gets warning and he have to fix them.
I want to create script which fills this table automatically. I know how to do this, but i don't know how to do it so that script obeys all that rules and preferences.
For example. I cant give to all users all morning shifts. I need to have it so that one person have no more that 5 shifts in on week, have at least one free weekend, have no more than 20 shifts per month and have them layed out equaly all over the month (not all shifts at the start)
Is this even possible with javascript? If yes could you please give me some idea how?
My current idea is to use 'IF' to obey the rules, but i need to have some rules on same level, so if i use 'IF' and logical operators, but i am kinda stuck on it for days and i am not certain that this is right way to go.
Fiddle with layout of table
Basic draft of my idea to fill table by rules:
function autoFill() {
var numberOfPeople = people.length;
var days = daysInThisMonth();
var ruleOne = ruleOneFunction(); //boolean
var ruleTwo = ruleTwoFunction(); //boolean
for(var i = 0; i < numberOfPeople; i++) {
for(var j = 1; j <= days; j++) {
if(ruleOne === true && ruleTwo === true) {
//fill table here
}
}
}
};

Related

How to create a scripted button in Google Sheets that randomly selects from a cells drop-down menu?

I'm working on a grossly complex and embarrassingly ungraceful spreadsheet that is a crudely crafted prompt-builder for AI image creation tools such as Stable Diffusion. The prompt builder is simply made up of about 40 data-validated drop-down menus that each pull from their respective categorical lists of words and phrases that I have listed elsewhere in the same sheet.
I would like to:
Create a scripted button that would automatically choose random options from the drop-down menus and
Add a probability percentage to each of the drop-down menus so that when the randomize button is pressed, there's a, for example, 100% chance that one option is randomly selected, and a 50% chance that another option will be randomly selected.
The Problem: I created a button, scripted it, and it does not function.
Simplified Editable DEMO of my Prompt Builder
I'm very aware that I'm likely doing this the hard way, and would love to avoid scripting if possible, but am at the end of my limited spreadsheet knowledge. I honestly have no business scripting. Using the script I built currently requires permission for anyone to use, which seems unnecessary to me, but it's the only way I knew to create it. I'm wondering if there's some way around that, maybe with triggers? I suppose that's a separate question...
I'm embarrassed to show this, but this is the script that I cobbled together. Before I added the if statements in trying to add probability, I had it kind of working. It would randomize entries, but some more than others. It was bizarre. Once I added in what I thought was probability code, it completely stopped working. Also, again, this is a simplified version of my actual spreadsheet and script, which had 40 individual data-validated dropdown menus.
My Non-Functioning Script:
function randomSelection() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Prompt Tool Demo");
var cell1 = sheet.getRange("C4");
var cell2 = sheet.getRange("C5");
var cell3 = sheet.getRange("C6");
var values1 = sheet.getRange("K3:K24").getValues();
var values2 = sheet.getRange("L3:L24").getValues();
var values3 = sheet.getRange("M3:M24").getValues();
var randomIndex1 = Math.random();
var randomIndex2 = Math.random();
var randomIndex3 = Math.random();
if (randomIndex1 < 1.0) {
cell1.setValue(values1[Math.floor(Math.random() * values1.length)][0]);
}
if (randomIndex2 < 0.5) {
cell2.setValue(values2[Math.floor(Math.random() * values2.length)][0]);
}
if (randomIndex3 < 0.5) {
cell3.setValue(values3[Math.floor(Math.random() * values3.length)][0]);
}
}

Add value to each cell in range (Google apps script)

Im building a macro for google sheets, in which i iterate through a range and add a value to each cell. I've been trying a few different ways to do it, but they are all really slow. For 30 cells, this takes about a minute. Is there any way to make it faster/write it in a different way? This is a macro that will be run a lot of times in a day, for ranges of about 30 columns max.
for (var j = 1; j <= range.getNumColumns(); j++) {
if(range.getCell(1, j).getValue() == "") {
range.getCell(1,j).setValue(valueToAdd/(colEnd-colStart));
} else range.getCell(1,j).setValue(valueToAdd/(colEnd-colStart)+(parseFloat(range.getCell(1,j).getValue())));
}
Try it this way:
function iterateThroughARange() {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
const rg=sh.getDataRange();
const vs=rg.getValues();
vs.forEach(function(r,i){
r.forEach(function(c,j){
vs[i][j]='Your Adjustment';
});
})
rg.setValues(vs);
}
Best Practices
This way you get all of the data at one time perform you adjustment and save it all at one time. It get's trickier if you want to avoid writing in certain cells. Then you may have to go back to the slower method of writing to individual cells. So Thinking about how how layout a spreads can impact the speed of your scripts quite a bit.

JS code to have multiple functions be applied to a cell on edit

I am currently trying to write some JS in script editor on a google sheet to not only produce a timestamp in conjunction with a check box being clicked but also set a timer that if the box is check for more than X minutes the cell will start to turn red.
Also when I uncheck the box to make the cell green. The idea behind this is think of a restaurant hostess paradigm when I click the box that means table is full timestamp gives me the time the box was checked and color shows its not available - IF I uncheck box it gives time stamp it was unchecked while also resetting time counter and changing the cell back to green. Currently I can get the timestamp portion to work with the code I wrote and it to change color on the first edit but it will not vacillate between colors based on the check box
Below is the code I currently have. Would a trigger function or another on edit function be used If anyone has any tips or ideas it would be greatly appreciated
For visual each column i'm referencing is a stagnate column 1 which is seat number the check boxes going down the 2nd column and the 3rd column which is populating what time the box is checked and unchecked
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == "Sheet1") { //checks that we're on the correct sheet
var r = s.getActiveCell();
if (r.getColumn() == 5) { //checks the column
var nextCell = r.offset(0, 1);
//if( nextCell.getValue() !== '' ) //is empty?
nextCell.setValue(new Date().setFontColor('green');
}
The code below should work:
function onEdit(e) {
var s = SpreadsheetApp.getActiveSheet(); // the active sheet (no need to check if the sheet == sheet1 as the active sheet will always be the one that's being edited)
var r = e.range; // the range of the edited cell
var c = r.getColumn(); // the column of the range
var timeDelay = 5; // number in seconds
var checkbox = r.getValue(); // the value of the checkbox after being edited
var date = new Date(); // the date for the timestamp
if (c == 5 && checkbox == true) { // if the checkbox has been checked, change the color to red
var nextCell = r.offset(0,1);
Utilities.sleep(timeDelay * 1000); // Utilities.sleep takes a number in milliseconds
nextCell.setValue(date).setBackground("red");
} else if (c == 5 && checkbox == false) { // if the checkbox has been unchecked, change the color to green
var nextCell = r.offset(0,1);
nextCell.setValue(date).setBackground("green");
}
}
I made a few changes to yours, some are simply personal preference of what I think is more readable in the long run if you were to add more to the code (i.e. declaring the date beforehand, setting all your variables up at the start, etc.).
However, this is definitely superfluous as active sheet will return the right thing anyway:
if (s.getName() == "Sheet1")
I also changed setFontColor() to setBackground() as it sounded like this is what you wanted from your description and thought you might not know of the function, although you can of course change it back to setFontColor() without any issues.
I also added in the time delay which you asked about, although I'm not sure why you'd need it in this context. If this isn't what you were talking about, feel free to remove these two lines and the rest of the code will still work fine:
var timeDelay = 5; // number in seconds
and
Utilities.sleep(timeDelay * 1000);

Get all elements in viewport Javascript

I wonder if it is possible (in javascript or jquery but without any plugins) to get all elements (for example table rows tr) in current viewport without looping through each of them? I found a lot of examples how to check if specified element is in current viewport, but what I need is a function returns a list of all elements in current viewport. I need this for virtualization because this table should have an infinite capacity and looping through each row from two millions rows is quite inefficient :)
Is there any reasonable way to do this?
Assuming you're not doing anything fancy with positioning, table rows in the viewport can be found with a binary search. For example, for 200000 rows, about 18 lookups are required to locate the first row on the page (jsfiddle, warning: slow to load). This can be extended to find the last element as well, or you could just loop through the elements starting from the first until you find one that is no longer visible.
var rows = table.children().children();
var start = 0;
var end = rows.length;
var count = 0;
while(start != end) {
var mid = start + Math.floor((end - start) / 2);
if($(rows[mid]).offset().top < document.documentElement.scrollTop)
start = mid + 1;
else
end = mid;
}
Obviously this does not work well if anything is floated, absolutely positioned, etc. In short: The nodes being searched must be in order such that rows[N-1].top <= rows[N].top. For something such as a table, this should be true if no styling is applied and no multi-row cells exist.

How to write javascript to reorder pages of a pdf document?

I have a double-sided document as two separate pdf files — front-facing pages in one document and rear-facing pages in the second.
front.pdf
rear.pdf
I have also combined them into a single document with all the pages but with all the front-facing pages before the rear-facing pages. The page ordering is of the form, {1,3,5,7,...,[n],2,4,6,8,...,[n-1 OR n+1]}
all.pdf
I wish to write a simple javascript that can be run from inside Adobe Abrobat X Pro. Ideally, it would count the pages of the document all.pdf, handle both occasion when there are either an odd or even number of total pages and then reorder them such that they are in their original order:
page [1>3>4>2] => page [1>2>3>4]
The tiny leading code snippet above is from the answer by user171577 on SuperUser in this question: https://superuser.com/questions/181596/software-that-merges-pdf-every-other-page
I was able to accomplish this following advice from NullUserException :
This script requires a document composed of all the odd pages followed by all the even pages. It will cope with cases where there are n even pages and n+1 odd pages.
I entered a 'Document JavaScript' called InterleavePages, with the following code:
function InterleavePages() {
var n = this.numPages;
var nOdd = Math.floor(n / 2);
var nEven = n - nOdd;
var x;
var y;
var i;
for(i = 0; i < nEven; i++) {
// movePage x, toAfterPage y
// note page numbers are 0-indexed
x = nOdd + (i); //
y = i * 2 ; //
this.movePage(x,y);
}
}
InterleavePages();
Thanks, this was a great help. Just wanted to point out the limitation I found is it only works as written with an even number of pages. Although it's probably possible to write a more sophisticated calculation script, I took the easy way out and just added a blank page to the end of my 17-page test document. I did, however, add an alert so I wouldn't forget to add an extra page when necessary...
function InterleavePages() {
var n = this.numPages;
var nOdd = Math.floor(n / 2);
var nEven = n - nOdd;
var x;
var y;
var i;
app.alert({
cMsg: "Total pages must be an even number",
cTitle: "Even Number of Pages Required!",
nIcon: 1, nType: 1
});
this.pageNum = 0;
for(i = 0; i < nEven; i++) {
// movePage x, toAfterPage y
// note page numbers are 0-indexed
x = nOdd + (i); //
y = i * 2 ; //
this.movePage(x,y);
}
}
InterleavePages();
As mentioned in some other exchanges, to interweave the pages two pdfs, you can use the Java console.
The first step is to combine the pdfs into a single document. I would do this by highlighting both files, and then right-clicking on one of them. There should be an option to "Combine supported files in Acrobat".
Then, once they are combined, open the combined file, where you want to run the code
for (i = 0; i <= this.numPages/2-1; i++) this.movePage(this.numPages-1,this.numPages/2-i-1);
The step-by-step details for running such code are:
1) Open the pdf.
2) Go to the second page. Doing this way will allow you to notice whether the change has taken place. You don't have to do this step, but it helps.
2) Press Control + J
3) In the window that pops up, I always go to the "View" Dropdown menu, and set it to "Script and Console".
4) In the bottom window, replace the text that should read something like
"Acrobat EScript Built-in Functions Version 10.0
Acrobat SOAP 10.0"
with
for (i = 0; i <= this.numPages/2-1; i++) this.movePage(this.numPages-1,this.numPages/2-i-1);
5) Press enter once. Pressing twice might run the code again (which you do not want).
6) Check you pdf to see if the pages have been interlaced. If not, try step 5 again.
7) You are now a Java wizard. Congratulations.
I had the same issue, my scanner was single sided only, and the scanner software, after being done with the auto-feeder, asked if there's more to scan. If you grab the stack, turn it over and feed it again, you'll end up with a single PDF where the n-page document is arranged as 1f, 2f, 3f ... nb, (n-1)b, (n-2)b ... 1b (f=front, b=back, 1-based page numbers). By definition you'd have an even number of scanned pages, the javascript for re-arranging the whole thing (careful, only works with even number of pages in this context!) is:
// rearrange the pages from single-side scanner, 0-based page# where
// pages 0 .. n/2-1 are front, pages n/2 .. n-1 are back
function Rearrange() {
var tpn=0; // target page number
for (count = 0; count < this.numPages/2; count++) {
this.movePage(this.numPages-1,tpn);
tpn=tpn+2;
}
}
Rearrange();

Categories

Resources