Executing function in DotNetNuke - dotnetnuke

I'm not at all a software developer and I need to know how can I average a set of values of one row in a DotNetNuke table inside of Form and List Module. I have tried using this for the expression:
'AVG('+[Num1]+','+[Num2]+','+[Num3]+')'
but this only returns concatenated: AVG(10,20,30) after I set the values of 10,20,30.
Anyone ideas?
$(document).ready(function()
{ $("#budgetWorksheet tbody tr").change(rowAverage); });
function rowAverage() { var totalAvg = 0; $("tbody tr").slice(0,-1).each(function()
{ var row_total = 0; var i = "td:not(.subtotal) input:text",
this).each(function() { row_total += parseInt(this.value || 0, 10);
}).length; if (row_total > 0) { var avg = Math.floor(parseInt(row_total, 10) /
i); $(".subtotal input:text", this).val(avg); totalAvg += avg; } });
$(.totalAvg input").val(totalAvg);

You will likely need to use XSLT to do this, FnL supports XSL templates, you can create them and apply them in the configuration for the module.
Doing it with the standard Grid layout would likely be a little more difficult, but could probably be done through jQuery. Here's a SO question that does some jQuery to figure out averages https://stackoverflow.com/questions/8812548/sum-table-row-text-fields-with-running-overall-total-in-text-field

Related

Add columns to an existing source of data

I would like to add several columns to an existing source of data displayed in a sheet. But as a data source, the number of rows can increase. I don't want to update the sheet once it is set up, it should be automatic. In those new columns, I would like formulas.
Please find below the result expected.
in blue, columns from data source
in red, my new columns with a formula
How can I do ?
Thank for you help 🙏
use in E1:
={"new col 1"; ARRAYFORMULA(IF(C2:C="";;IF((ISNUMBER(C2:C))*(C2:C>=2); 1; 0)))}
use in F1:
={"new col 2"; ARRAYFORMULA(IF(D2:D="";;IF((ISNUMBER(D2:D))*(D2:D>=2); 10; 0)))}
I tried with a custom function and to personalize it with another project, it doesn't work...
In this simple case, it works. If not empty, it returns G cells in uppercase :
={"Feature"; ARRAYFORMULA(SI(G2:G="";;IF((NOT(ISEMPTY(G2:G))); UPPERCASE(G2:G); "-")))}
In other case with a custom function, not. It is named "getFeature"
={"Feature"; ARRAYFORMULA(SI(G2:G="";;IF((NOT(ISEMPTY(G2:G))); getFeature(G2:G); "-")))}
When I use it in a classic way, my function works : =getFeature(G7)
Here my custom function :
function getFeature(searchString) {
if (searchString === "") {
return ""
}
var sFeature = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("raw feature")
var rFeatureCol = sFeature.getRange(2, 1, sFeature.getLastRow(), 1)
array = searchString.split(";")
for (let i = 0; i < array.length; i++) {
try {
var textFinder = rFeatureCol.createTextFinder(array[i])
var search_row = textFinder.findNext().getValue()
return array[i]
}
catch {
// erreur détectée
}
}
return ""
}

Loop over a couple of columns

very beginning with Google sheet.
I managed to create the function I need:
function happy() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('C5').activate();
spreadsheet.getCurrentCell().setFormula('=G4');
spreadsheet.getRange('H4').activate();
var current_e15=spreadsheet.getRange('E15').getValue();
spreadsheet.getCurrentCell().setValue(current_e15);
spreadsheet.getRange('C5').activate();
spreadsheet.getCurrentCell().setFormula('=G5');
spreadsheet.getRange('H5').activate();
var current_e15=spreadsheet.getRange('E15').getValue();
spreadsheet.getCurrentCell().setValue(current_e15);
...
...
...
The function is reading from the G[i] column, assign it to C5, read the result produced by the new value in E15 and put that value in the cell H[i].
Obviously this can be done over a loop over i instead of copy past this same block and changing G4 by G5 and H4 by H5 etc..
I just don't know how to say it in this langage.
I tried this that is obviously not working.
function happyloop() {
var spreadsheet = SpreadsheetApp.getActive();
var CellsG = spreadsheet.getRange('G4:G15');
var CellsH = spreadsheet.getRange('H4:H15');
for (var i = 0; i < CellsG.length; i++) {
spreadsheet.getRange('C5').activate();
spreadsheet.getCurrentCell().setFormula('=CellsG[i]');
spreadsheet.getRange(CellsH[i]).activate();
var current_e15=spreadsheet.getRange('E15').getValue();
spreadsheet.getCurrentCell().setValue(current_e15);
}
};
I'd be glad to get help with how to properly this function in google sheet language.
Thanks if you can help :)
I finally get to this that is working.
If there is a more elegant way to do it please correct me.
Thank you
function happyloop() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheets()[0];
for (var i = 4; i < 16; i++) {
sheet.getRange('C5').activate();
var current_Gi=sheet.getRange(i,7).getValue();
sheet.getCurrentCell().setValue(current_Gi);
sheet.getRange(i,8).activate();
var current_e15=sheet.getRange('E15').getValue();
sheet.getCurrentCell().setValue(current_e15);
}
}
I took your working answer and made minor changes in it to achieve a more elegant way as you requested. The original scripts runs in 4.5 seconds, while this modified version runs in 2.5 seconds. This is the optimized version:
function happyLoop() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheets()[0];
var C5 = sheet.getRange('C5');
var E15 = sheet.getRange('E15').getValue();
for (var i = 4; i < 16; i++) {
C5.setValue(sheet.getRange(i, 7).getValue());
sheet.getRange(i, 8).setValue(E15);
}
}
I changed the variable names to make it clearer to me, but that isn't a speed improvement. To achieve more speed I took out of the for loop everything that doesn't depend on the i iterator. Notice also how I nested each method call.
To improve this code even more I'll have to understand your original scenario. A big possible improvement is to use the .setValues just once in the full code, because it is the slowest method and here it gets called in every iteration. Finally, I want to share the Apps Script best practices with you. Please, don't hesitate to ask me for deeper clarification.

Google Sheets custom function displays "Loading..." forever in mobile app

I have written a custom function for Google Sheets in Apps Script. The goal is to have a sheet which automatically calculates who owes how much money to whom (e.g. to split a bill).
My sheet looks like this:
The first bill (Restaurant) is to be split among all 5 and the second bill is to be split among all 5 except Peter, because there is no 0 in B3.
The input for my Apps Script function will be cells B1 to F3 (thus, values AND names). The function works fine - it calculates the correct results. I open that spreadsheet via browser (sheets.google.com) AND via my phone app (Google Sheets). However, on my phone it often happens that the result cell (with the formula =calc_debt(B1:F3)) only displays "Loading ...". What's the problem?
For the sake of completeness, here is custom function's code:
function calc_debt(input) {
var credit = [0, 0, 0, 0, 0]; // credit[0] = Peter, credit[1] = Mark ...
for (var i = 1; i < input.length; i++) { // starting at i = 1 to skip the first row, which is the names!
// first: calculate how much everybody has to pay
var sum = 0;
var people = 0;
for (var j = 0; j <= 4; j++) {
if (input[i][j] !== "") {
sum += input[i][j];
people += 1;
}
}
var avg_payment = sum / people;
// second: calculate who has payed too much or too little
for (var j = 0; j <= 4; j++) {
if (input[i][j] !== "") {
credit[j] += input[i][j] - avg_payment;
}
}
}
// this function is needed later
function test_zero (value) {
return value < 0.00001;
};
var res = ""; // this variable will contain the result string, something like "Peter to Mark: 13,8 | Katy to ..."
while (!credit.every(test_zero)) {
var lowest = credit.indexOf(Math.min.apply(null, credit)); // find the person with the lowest credit balance (will be minus!)
var highest = credit.indexOf(Math.max.apply(null, credit)); // find the person with the highest credit balance (will be plus!)
var exchange = Math.min(Math.abs(credit[lowest]), Math.abs(credit[highest])); // find out by how much we can equalize these two against each other
credit[lowest] += exchange;
credit[highest] -= exchange;
res += input[0][lowest] + " to " + input[0][highest] + ": " + exchange.toFixed(2) + " | "; // input[0] = the row with the names.
}
return res;
}
I'm having a similar issue in the android app that loading a custom formula sometimes just shows 'Loading...', while in the web it always works fine. I've found a workaround to load the formulas in the android app:
Menu - > Export - > Save as - > PDF.
This will take a moment and behind the modal loading indicator you will see that the formulars eventually resolve. You can wait for the export to finish or cancel it as soon as you see your formular was resolved.
Also making the document available offline via the menu toggle could resolve the formulars.
Another thing you could do is using caching in your script. So whenever you use the web version to render more complex formulars the results are being stored and immediately loaded for the mobile app. Unfortunately, the Google cache is limited in time and does invalidate after a few hours. See here for more information:
https://developers.google.com/apps-script/reference/cache/
This two things work quite well. However, I'm searching for a better solution. Let me know if you find one.
Solved follow the solution provided here ..
Menu - > Export - > Save as - > PDF
This forces the script to run on mobile and be readable by the mobile sheet

Slow Google Script - difficulties batching information into an array

I've been struggling with a little project of mine for a while now, and was looking for some assistance. The key issue I believe is simply me not being familiar with array script language and how to approach this. I've tried a few things after researching on here a bit and reading through the Best Practices section, but haven't been able to get it functioning adequately.
My script needs to be able to collect 200 rows x 200 columns of data from a spreadsheet, and depending on the number within each cell, it needs to select the corresponding number of columns next to that number and colour them in.
This was really simple to do with my basic programming knowledge, by just getting it to select each cell, check the number, select that range with an offset and then change the colour and move onto the next cell, however my code is incredibly slow because it does everything within the sheet without batching the data, and can't complete the full range within Google Script's time allowance. Any assistance on speeding it up would be greatly appreciated, as I haven't been able to get this working using arrays.
Here's the code I'm working with currently:
function CreateCalendar() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheet=ss.getSheetByName('Sheet2');
var selection=ss.getRange("Sheet2!H2:FC140");
var columns=selection.getNumColumns();
var rows=selection.getNumRows();
for (var column=1; column < columns; column++) {
for (var row=1; row < rows; row++) {
var cell=selection.getCell(row,column);
var cellvalue=cell.getValue();
if (cellvalue >= 1) {
var range=cell.offset(0,0,1,cellvalue);
range.setBackground("blue");
}
else {;}
}
}
}
Here's a public spreadsheet with confidential info removed and the sheet I'm targeting is Sheet2. Any assistance I could get on this would be greatly appreciated! Thanks
https://docs.google.com/spreadsheets/d/1Oe0aacfSBMmHpZvGPmjay5Q1bqBebnGQV4xlsK8juxk/edit#gid=0
You need to get rid of the repeated calls to range.getValue(). You can get all of the values for the range in one call & then iterate over that array in-script.
For your script it would look something like this:
function CreateCalendar() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheet=ss.getSheetByName('Sheet2');
var selection=ss.getRange("Sheet2!H1:FC140"); // <= include header, but we'll skip it when we get to stepping over the array
var values = selection.getValues(); // <= get all of the values now in one call
for (var r=1; r < values.length; r++) {
for (var c=0; c < values[r].length; c++) {
if (values[r][c] >= 1) {
var range=sheet.getRange(r+1, c+8, 1, values[r][c]); // r+1 & c+8 because of array offsets
range.setBackground("blue");
}
else {;}
}
}
}
Take a look at Google's documentation: range.GetValues() https://developers.google.com/apps-script/reference/spreadsheet/range#getValues()
How about following sample script? If this is not your expectation, I would like to modify this script.
Sample script :
function CreateCalendar() {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheet=ss.getSheetByName('Sheet2');
var data = sheet.getRange("H2:FC140").getValues();
data.forEach(function(e1, i1){
e1.forEach(function(e2, i2){
if (e2 >= 1) {
sheet.getRange(i1+2, i2+8).offset(0,0,1,e2).setBackground("blue");
}
})
})
}
Result (sample) :
If I misunderstand your question, I'm sorry.

Google Script: adding up arrays

I want to write a google spreadsheet with two main parts: an individual order form to calculate price & a system to count the total orders.
I made a simplified version here: https://docs.google.com/spreadsheets/d/1_vRnX-qT3-0puYhtBCclYIVu504A-donE4nU8rQn41k/edit?usp=sharing
After noting an individual order and the order is paid, the green button is clicked. The button activates the payment-script.
The payment script needs to add the values of the individual order to the total orders and afterwards empty the individual orders.
function payment() {
var individualOrder = SpreadsheetApp.getActiveSheet().getRange('E3:E6').getValues();
var currentTotal = SpreadsheetApp.getActiveSheet().getRange('O3:O6').getValues();
var newTotal = [0,0,0,0];
for (var i = 0; i < 4; i++) {
newTotal[i] = currentTotal[i] + individualOrder[i];
}
SpreadsheetApp.getActiveSheet().getRange('O3:O6').setValues([newTotal]);
// Reset individual order after payment
SpreadsheetApp.getActiveSheet().getRange('E3:E6').setValue(0);
}
EDIT: Now it does work to replace the new Total values by using the code below, but there is another problem. When summing the array values 1 + 0, it cocatenates them to 10 instead of using math-values. Any ideas?
function payment() {
var individualOrder = SpreadsheetApp.getActiveSheet().getRange('E3:E6').getValues();
var currentTotal = SpreadsheetApp.getActiveSheet().getRange('O3:O6').getValues();
var newTotal = currentTotal;
Logger.log(individualOrder);
Logger.log(currentTotal);
for (var i = 0; i < 4; i++) {
newTotal[i] = [currentTotal[i] + individualOrder[i]];
}
Logger.log(newTotal);
SpreadsheetApp.getActiveSheet().getRange('O3:O6').setValues(newTotal);
// Reset individual order after payment
SpreadsheetApp.getActiveSheet().getRange('E3:E6').setValue(0);
}
If all you want to do is set 'individualOrder' to 0 try:
function payment() {
var individualOrder = SpreadsheetApp.getActiveSheet().getRange('E3:E6').getValues();
for (var i = 0; i <individualOrder.length ; i++) {
individualOrder[i]=[0];
}
SpreadsheetApp.getActiveSheet().getRange('E3:E6').setValues(individualOrder);
}
I solved it by making the sum in the spreadsheet and doing a .getValues on that range and using this in .setValues. A lot easier, only 2 lines of code.

Resources