ServiceNow - Data Transform Script Debug - servicenow

I am using a data transform to import a large .csv of emails into a Watchlist field. My .csv is in the following format:
"ipsum#test.com", "ipsum#test.com, lorem#test.com",,"ipsum#tests.com"
I am using the following script to link the imported emails to User records, however my data transform ignores all records despite me manually confirming the emails exist, is there something wrong in this script?
answer = (function transformEntry(source) {
var grUsers = new GlideRecord('sys_user');
var users = String(source.u__users).split(",");
var query = 'email=LIKE' + String(users[0]);
var i = 1;
while(i != users.length){
query = query + '^ORemailLIKE' + String(users[i]);
i++;
}
grUsers.addEncodedQuery(query);
grUsers.query();
return grUsers.join(',');
})(source);

Two problems:
Using split(",") is not enough if the format you are showing is correct. You need to strip the quotes as well
Using email=LIKE should be emailLIKE, no = sign
Here is an example based on your data:
var data = '"alpha#test.com","beta#test.com","lorem#test.com","ipsum#tests.com"';
var users = String(data).split(",");
for (var i = 0; i < users.length; i++){
gs.print(users[i])
}
var query = 'email=LIKE' + String(users[0]);
gs.print(query);
Running this in Background Scripts I get:
*** Script: "alpha#test.com"
*** Script: "beta#test.com"
*** Script: "lorem#test.com"
*** Script: "ipsum#tests.com"
*** Script: email=LIKE"alpha#test.com"
Here's the fixed one:
var data = '"alpha#test.com","beta#test.com","lorem#test.com","ipsum#tests.com"';
var users = String(data).split(",");
for (var i = 0; i < users.length; i++){
users[i] = users[i].replace(/^"(.*)"$/, '$1');
}
for (var i = 0; i < users.length; i++){
gs.print(users[i])
}
var query = 'emailLIKE' + String(users[0]);
gs.print(query);
Result:
*** Script: alpha#test.com
*** Script: beta#test.com
*** Script: lorem#test.com
*** Script: ipsum#tests.com
*** Script: emailLIKEalpha#test.com

Related

Add a status to each proccesed row in for loop that has an if statement with Google App Script

I've got a for loop in App script that is looking only at rows that have data in two columns. I'd like to set a status on each row that is actually processed, but the statuses get added to the wrong rows. When I add to i it adds to the whole length of the array, so I guess I shouldn't be trying to process each row, what am I doing wrong?
function auditReport() {
var sheetname = "Sheet1"; // name of data sheet ex. Form Responses 1
var colstoworkon = 10; // how many cols are filled with data f.e. by a form
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(sheetname));
var sheet = ss.getSheetByName(sheetname);
var data = sheet.getRange(3,1,sheet.getLastRow()-1,colstoworkon).getValues(); // starting with row 2 and column 1 as our upper-left most column,
//This makes it loops continuously and checks all not done rows
for (var i in data) {
if(data[i][1] && data[i][2]){//if email or copy are undefined just skip
var setStatus = sheet.getRange(i,4).setValue("done")
} // end of if
} // End of Loop
} //End of email function
Modification points:
In your script, from getRange(3,1,sheet.getLastRow()-1,colstoworkon), in this case, it is required to be getRange(3,1,sheet.getLastRow()-2,colstoworkon).
In the case of for (var i in data) {, i is the string type.
When you want to use sheet.getRange(i,4).setValue("done"), it is required to be sheet.getRange(Number(i) + 3, 4).setValue("done").
I thought that this might be the reason of your issue of but the statuses get added to the wrong rows..
In the case of if (data[i][1] && data[i][2]) {, if the value is 0, data[i][1] && data[i][2] is false.
When these points are reflected to your script, it becomes as follows.
Modified script:
function auditReport() {
var sheetname = "Sheet1";
var colstoworkon = 10;
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(sheetname));
var sheet = ss.getSheetByName(sheetname);
var data = sheet.getRange(3, 1, sheet.getLastRow() - 2, colstoworkon).getDisplayValues();
for (var i in data) {
if (data[i][1] && data[i][2]) {
var setStatus = sheet.getRange(Number(i) + 3, 4).setValue("done");
}
}
}
Or, your script can be also modified as follows. In this modification, done is put using the range list. By this, the process cost can be reduced a little.
function auditReport() {
var sheetname = "Sheet1";
var colstoworkon = 10;
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.setActiveSheet(ss.getSheetByName(sheetname));
var sheet = ss.getSheetByName(sheetname);
var data = sheet.getRange(3, 1, sheet.getLastRow() - 2, colstoworkon).getDisplayValues();
var ranges = data.map(([,b,c], i) => b && c ? `D${i + 3}` : "").filter(String);
if (ranges.length == 0) return;
sheet.getRangeList(ranges).setValue("done");
}
References:
for...in
getRangeList(a1Notations)

Google appscipt - Exporting multiple PDF with a for loop, using a sheet as a template, unexpected result

I'm trying to automate the pdf export of a sheet ('BL') which is filled depending on a cell value grabbed on a variable list on sheet 'EiBLdata' by 'i' on each loop.
It seems to work... more or less.
Instead of having 1st pdf with 1st value,
2nd pdf with 2nd value,
3rd pdf with 3rd value etc.
I get 1st pdf with 1st value,
2nd pdf with 1st value,
3rd pdf with 2nd value etc.
In the end only the 1st pdf has the right name, there is a shift in all the others and the last value isn't exported.
I'm quite a newbie with JavaScript and I admit there is a lot of copy/paste in my code, adapted to my purpose. I can't find what I'm doing wrong.
function printSelectedRange() {
var nomfeuille = "EiBLData"
var nomBL = "BL"
var cc = SpreadsheetApp.getActiveSpreadsheet();
var feuille = cc.getSheetByName(nomfeuille);
var BL = cc.getSheetByName(nomBL);
var tr = BL.getRange('B2').getValue();
var plage = feuille.getRange('A1:A15').getValues();
var cell0 = feuille.getRange(1,1).getValue();
BL.getRange('B2').setValue(cell0);
for (var i = 1; i <= 16; i++) {
var cell = feuille.getRange(i,1).getValue();
if (cell > 0) {
BL.getRange('B2').setValue(cell)
var sheetName = "BL";
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(sheetName);
var ssUrl = ss.getUrl();
var sheetId= sheet.getSheetId();
var url = ssUrl.replace(/\/edit.*$/,'')
+ '/export?exportformat=pdf&format=pdf'
+ '&size=A6'
+ '&portrait=false'
+ '&fitw=false'
+ '&scale=4'
+ '&top_margin=0.35'
+ '&bottom_margin=0.00'
+ '&left_margin=0.35'
+ '&right_margin=0.0'
+ '&sheetnames=false'
+ '&printtitle=false'
+ '&pagenum=false'
+ '&gridlines=false'
+ '&fzr=FALSE'
+ '&gid='+sheetId;
var token = ScriptApp.getOAuthToken();
var docurl = UrlFetchApp.fetch(url, { headers: { 'Authorization': 'Bearer ' + token } });
var pdf = docurl.getAs('application/pdf');
var file = DriveApp.createFile(pdf);
var docId = sheet.getRange('F13').getValue();
var clientName = sheet.getRange('E9').getValue();
var docDate = sheet.getRange('F14').getValue();
var mois = docDate.getMonth()
docDate.setMonth((mois+1) % 12);
var docDateMMYY = docDate.getMonth()+"-"+docDate.getFullYear().toString().substr(-2);
var docName = "BL-"+docId+"-"+clientName+"-"+docDateMMYY ;
var folder = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxx");
var finalFile = file.makeCopy(docName,folder);
file.setTrashed(true);
};
};
var cmdes = ss.getSheetByName('cmdes');
var raz = cmdes.getRange('S2:S501').setValue(false);
}
I think I had a good intuition and finally found the reason of my issue:
how-to-pause-app-scripts-until-spreadsheet-finishes-calculation
It looks like the loop runs faster than the spreadsheet calculation.
Sorry for inconvenience...

InDesign Text Modification Script Skips Content

This InDesign Javascript iterates over textStyleRanges and converts text with a few specific appliedFont's and later assigns a new appliedFont:-
var textStyleRanges = [];
for (var j = app.activeDocument.stories.length-1; j >= 0 ; j--)
for (var k = app.activeDocument.stories.item(j).textStyleRanges.length-1; k >= 0; k--)
textStyleRanges.push(app.activeDocument.stories.item(j).textStyleRanges.item(k));
for (var i = textStyleRanges.length-1; i >= 0; i--) {
var myText = textStyleRanges[i];
var converted = C2Unic(myText.contents, myText.appliedFont.fontFamily);
if (myText.contents != converted)
myText.contents = converted;
if (myText.appliedFont.fontFamily == 'Chanakya'
|| myText.appliedFont.fontFamily == 'DevLys 010'
|| myText.appliedFont.fontFamily == 'Walkman-Chanakya-905') {
myText.appliedFont = app.fonts.item("Utsaah");
myText.composer="Adobe World-Ready Paragraph Composer";
}
}
But there are always some ranges where this doesn't happen. I tried iterating in the forward direction OR in the backward direction OR putting the elements in an array before conversion OR updating the appliedFont in the same iteration OR updating it a different one. Some ranges are still not converted completely.
I am doing this to convert the Devanagari text encoded in glyph based non-Unicode encoding to Unicode. Some of this involves repositioning vowel signs etc and changing the code to work with find/replace mechanism may be possible but is a lot of rework.
What is happening?
See also: http://cssdk.s3-website-us-east-1.amazonaws.com/sdk/1.0/docs/WebHelp/app_notes/indesign_text_frames.htm#Finding_and_changing_text
Sample here: https://www.dropbox.com/sh/7y10i6cyx5m5k3c/AAB74PXtavO5_0dD4_6sNn8ka?dl=0
This is untested since I'm not able to test against your document, but try using getElements() like below:
var doc = app.activeDocument;
var stories = doc.stories;
var textStyleRanges = stories.everyItem().textStyleRanges.everyItem().getElements();
for (var i = textStyleRanges.length-1; i >= 0; i--) {
var myText = textStyleRanges[i];
var converted = C2Unic(myText.contents, myText.appliedFont.fontFamily);
if (myText.contents != converted)
myText.contents = converted;
if (myText.appliedFont.fontFamily == 'Chanakya'
|| myText.appliedFont.fontFamily == 'DevLys 010'
|| myText.appliedFont.fontFamily == 'Walkman-Chanakya-905') {
myText.appliedFont = app.fonts.item("Utsaah");
myText.composer="Adobe World-Ready Paragraph Composer";
}
}
A valid approach is to use hyperlink text sources as they stick to the genuine text object. Then you can edit those source texts even if they were actually moved elsewhere in the flow.
//Main routine
var main = function() {
//VARS
var doc = app.properties.activeDocument,
fgp = app.findGrepPreferences.properties,
cgp = app.changeGrepPreferences.properties,
fcgo = app.findChangeGrepOptions.properties,
text, str,
found = [], srcs = [], n = 0;
//Exit if no documents
if ( !doc ) return;
app.findChangeGrepOptions = app.findGrepPreferences = app.changeGrepPreferences = null;
//Settings props
app.findChangeGrepOptions.properties = {
includeHiddenLayers:true,
includeLockedLayersForFind:true,
includeLockedStoriesForFind:true,
includeMasterPages:true,
}
app.findGrepPreferences.properties = {
findWhat:"\\w",
}
//Finding text instances
found = doc.findGrep();
n = found.length;
//Looping through instances and adding hyperlink text sources
//That's all we do at this stage
while ( n-- ) {
srcs.push ( doc.hyperlinkTextSources.add(found[n] ) );
}
//Then we edit the stored hyperlinks text sources 's texts objects contents
n = srcs.length;
while ( n-- ) {
text = srcs[n].sourceText;
str = text.contents;
text.contents = str+str+str+str;
}
//Eventually we remove the added hyperlinks text sources
n = srcs.length;
while ( n-- ) srcs[n].remove();
//And reset initial properties
app.findGrepPreferences.properties = fgp;
app.changeGrepPreferences.properties = cgp;
app.findChangeGrepOptions.properties =fcgo;
}
//Running script in a easily cancelable mode
var u;
app.doScript ( "main()",u,u,UndoModes.ENTIRE_SCRIPT, "The Script" );

"for" loop for Google Spreadsheet values not looping

I'm currently writing a statistic spreadsheet script for my guild, which reads out the class of one person and counts it on the statistic sheet.
For some reason the for loops aren't working. When I execute the script, it does nothing. Everything before the for loop seems to work. I have used the debugger, and set a debug point from the point of the for loop and the window is opening and closing after like 1 second.
This is my code as of now:
function addToStatistik() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = spreadsheet.getSheetByName("Raid Montag");
var source_range_names = source_sheet.getRange("C4:C13");
var source_range_setup_boss1 = source_sheet.getRange("M4:M13");
var target_sheet = spreadsheet.getSheetByName("Statistik");
var target_range_names = target_sheet.getRange("A4:A31");
var target_range_boss1 = target_sheet.getRange("K4:S31");
target_sheet.getRange(2,1).setValue("Debug1"); //testing stuff
for (var i=0; i < source_range_names.length; i++) {
for (var j=0; j < target_range_names.length; j++) {
if (source_range_names[i][0] == target_range_names[j][0]) {
if (source_range_setup_boss1[i][0].indexOf("War") > -1) {
target_sheet.getRange(9,5).setValue("TEST");
}
}
}
}
}
Someone can find any errors in there? I can't find anything and google also isnt helping me.
You are getting the range, but not the values. This line:
var source_range_names = source_sheet.getRange("C4:C13");
gets a range, but not any values.
Should be:
var source_range_names = source_sheet.getRange("C4:C13").getValues();
The outer loop never loops. There is no length of a range.
for (var i=0; i < source_range_names.length; i++) {
You don't need to change the above line, but currently the variable source_range_names is a range, and not a 2D array of values.
Before you iterate you need to get the values of the range, to achieve this you need to use the method getValues() or getDisplayValues():
function leFunction() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = spreadsheet.getSheetByName("Raid Montag");
var source_range_names = source_sheet.getRange("A1:C13");
var values_range_names = source_range_names.getDisplayValues();
Logger.log(values_range_names);
for (var i=0; i < values_range_names.length; i++) {
// Do Something
}
}

Microsoft Outlook Interop (extract attachments) very slow

I'm using Microsoft.Office.Interop.Outlook to extract e-mail attachments:
var MAPI = new Application().GetNamespace("MAPI");
var ExampleFolder = MAPI.GetDefaultFolder(OlDefaultFolders.olFolderSentMail)
foreach (dynamic i in ExampleFolder.Items)
if (i.Attachments.Count > 0)
; // DoSomething();
Unfortunately this is extremely slow.
Is there any faster way to check for attachments?
Is it possible to filter/sort e-mails by date: loop through the last n items only?
sure, you can sort the collection using Items.Sort.
You can also use Items.Find/FindNext or Items.Restrict to look for items with attachments only. The property you need is PR_HASATTACH (DASL name http://schemas.microsoft.com/mapi/proptag/0x0E1B000B)
#Kiquenet (I can't add a comment below yours), here is the code to get items with attachments from Items.Restrict:
//fanti's code
var MAPI = new Application().GetNamespace("MAPI");
var ExampleFolder = MAPI.GetDefaultFolder(OlDefaultFolders.olFolderSentMail)
Urn way (tested, ok -> source https://social.msdn.microsoft.com/Forums/windowsapps/en-US/b6fef244-756c-4ab0-a22b-78137cfb4349/datereceived-filter-nor-happeinig?forum=outlookdev):
var itemsWithAttachment = ExampleFolder.Items.Restrict("#SQL= urn:schemas:httpmail:hasattachment = True");
DASL way (tested, ko -> 'should work' source https://learn.microsoft.com/en-us/office/client-developer/outlook/pia/how-to-filter-and-efficiently-enumerate-items-in-a-folder):
const string PR_HAS_ATTACH = "https://schemas.microsoft.com/mapi/proptag/0x0E1B000B";
var itemsWithAttachment = ExampleFolder.Items.Restrict("#SQL=\"" + PR_HAS_ATTACH + "\" = 1");
To filter by a date, just add "AND"s or "OR"s like this (Urn way):
var itemsWithAttachmentAndDate = ExampleFolder.Items.Restrict("#SQL= urn:schemas:httpmail:hasattachment = True"
+ " AND urn:schemas:httpmail:datereceived <= '" + DateTime.Now.AddMonths(-3) + "'");
To loop through the last n items only:
int n = 3;
for (int i = itemsWithAttachmentAndDate.Count - 1; i > n; i--)
{
//current item: itemsWithAttachmentAndDate[i] //Beware: "dynamic" typed!
; //DoSomething();
}

Resources