Javascript How to SetTimeOut while getting a list of files with Scripting.FileSystemObject - javascript

This code is for internal, offline, single user use, IE only. The code looks at a folder, and lists all files including those in subfolders. It sorts through the data based on some date fields and datelastmodified. It also uses and if to throw out thumbs.db entries. All of the data is put into a table.
My issue is that this script can take a long time to get the data. I would like to add a progress bar but the progress bar cant update while the script is running. After some research it looks like SetTimeOut can allow the page elements to be updated as the script runs, therefore allowing the progress bar to work and looking overall cleaner. However I can not figure out of to implement SetTimeOut into my existing code.
<script type="text/javascript">
var fso = new ActiveXObject("Scripting.FileSystemObject");
function ShowFolderFileList(folderspec) {
var beginningdate = new Date(startdate.value);
var finishdate = new Date(enddate.value);
var s = "";
var f = fso.GetFolder(folderspec);
var subfolders = new Enumerator(f.SubFolders);
for (subfolders.moveFirst(); !subfolders.atEnd(); subfolders.moveNext()) {
s += ShowFolderFileList(subfolders.item().path);
}
// display all file path names.
var fc = new Enumerator(f.files);
for (i = 0; !fc.atEnd(); fc.moveNext()) {
if (fc.item().name != "Thumbs.db") {
var dateModified = fc.item().DatelastModified;
if (dateModified >= beginningdate && dateModified <= finishdate) {
Date.prototype.toDateString = function () {
return [this.getMonth() + 1, '/', this.getDate(), '/', this.getFullYear()].join('');
}
var dateModifiedClean = (new Date(fc.item().DatelastModified).toDateString());
s += "<table border=0 width=100% cellspacing=0><tr " + ((i % 2) ? "" : "bgcolor=#EBF1DE") + "><td width=75%><font class=find><b>" + fc.item().ParentFolder.name + "</b>" + " - " + fc.item().name + "</font></td><td width=25% align=right><font class=find>" + dateModifiedClean + "</font></td></tr>";
i++;
}
}
}
var results = s + "</table>";
return results;
}
function listFiles() {
outPut.innerHTML = ShowFolderFileList('*Path to scan*');
}
</script>
outPut is the ID of a div tag where the results table is displayed. A button calls the listfiles function.

Related

Am I using isNaN() incorrectly? Very strange (Google Ads) Javascript code - always fails in the first execution but succeeds in subsequent tries

The code below is a Google Ads script. It does a fairly simple thing: it grabs the ad group impression share values from any 2 given periods, and issues an alert for ad groups whose impression share values dropped by more than 10% between the "pre" and "post" periods. More details are in the comments in the code.
Here's the really, really weird part: everytime I preview the code (by pressing the "Preview" button in the Google Ads script console), it always fails the first time, but then in subsequent tries (2nd, 3rd, etc) it always works.
If I go back to the Google Ads main "Scripts" page and then open the script and preview it again, it seems to get "reset" and fails again, and then succeeds in the 2nd try onwards.
The exact error is: "TypeError: Cannot read property 'preImpressionShare' of undefined".
According to the Google Ads script console, the line that causes the error is the following:
if ((!isNaN(adgroup.preImpressionShare)))
My questions are:
One would think that if the code is wrong, it should always fail. Yet, it works most of the time, and only fails in the first try. Why is it unstable like that?
How do I fix the error so that it will always work? Is the isNaN() function being used incorrectly or what?
var config = {
campaignsContain: '', // The script will only look for campaigns that contain this string - leave blank to work on all campaigns
imprShareDropThreshold: 0.1, // The percent change to trigger an alert (0.1 = 10% change)
emails: 'email#myemail.com', // Comma-separated list of emails to alert
}
function main() {
/*Date settings for the "pre" period.*/
var preStartDate = new Date('January 1, 2022');
var preFormattedStartDate = Utilities.formatDate(preStartDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
preStartDate = Utilities.formatDate(preStartDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
var preEndDate = new Date('January 31, 2022');
var preFormattedEndDate = Utilities.formatDate(preEndDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
preEndDate = Utilities.formatDate(preEndDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
/*Date settings for the "post" period.*/
var postStartDate = new Date('February 1, 2022');
var postFormattedStartDate = Utilities.formatDate(postStartDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
postStartDate = Utilities.formatDate(postStartDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
var postEndDate = new Date('February 28, 2022');
var postFormattedEndDate = Utilities.formatDate(postEndDate, AdsApp.currentAccount().getTimeZone(), 'EEE, MMM d, YYYY');
postEndDate = Utilities.formatDate(postEndDate, AdsApp.currentAccount().getTimeZone(), 'YYYYMMdd');
/*GAQL setup for the "pre" period query*/
var preCampaignFilter = config.campaignsContain.length > 0 ? ' AND CampaignName CONTAINS "' + config.campaignsContain + '" ' : '';
var preQuery = 'SELECT AdGroupId, CampaignName, AdGroupName, SearchImpressionShare FROM ADGROUP_PERFORMANCE_REPORT WHERE AdGroupStatus = ENABLED ' + 'DURING ' + preStartDate + ',' + preEndDate;
var preReport = AdsApp.report(preQuery);
var preAdgroups = [];
var preRows = preReport.rows();
/*GAQL setup for the "post" period query*/
var postCampaignFilter = config.campaignsContain.length > 0 ? ' AND CampaignName CONTAINS "' + config.campaignsContain + '" ' : '';
var postQuery = 'SELECT AdGroupId, CampaignName, AdGroupName, SearchImpressionShare FROM ADGROUP_PERFORMANCE_REPORT WHERE AdGroupStatus = ENABLED ' + 'DURING ' + postStartDate + ',' + postEndDate;
var postReport = AdsApp.report(postQuery);
var postAdgroups = [];
var postRows = postReport.rows();
/*Traverse the "pre" period query results, and add them to an array.*/
while (preRows.hasNext()) {
var preRow = preRows.next();
var preAdgroupid = preRow.AdGroupId;
var preAdgroup = preRow.AdGroupName;
var preCampaign = preRow.CampaignName;
var preImpressionShare = parseFloat(preRow.SearchImpressionShare.replace('%', '')) / 100;
let preAdGroupObject = {};
preAdGroupObject.adgroupid = preAdgroupid;
preAdGroupObject.adgroup = preAdgroup;
preAdGroupObject.campaign = preCampaign;
preAdGroupObject.preImpressionShare = preImpressionShare;
preAdgroups.push(preAdGroupObject);
}
/*Traverse the "post" period query results, and add them to an array.*/
while (postRows.hasNext()) {
var postRow = postRows.next();
var postAdgroupid = postRow.AdGroupId;
var postAdgroup = postRow.AdGroupName;
var postCampaign = postRow.CampaignName;
var postImpressionShare = parseFloat(postRow.SearchImpressionShare.replace('%', '')) / 100;
let postAdGroupObject = {};
postAdGroupObject.adgroupid = postAdgroupid;
postAdGroupObject.adgroup = postAdgroup;
postAdGroupObject.campaign = postCampaign;
postAdGroupObject.postImpressionShare = postImpressionShare;
//if(postImpressionShare > 0.1) {
postAdgroups.push(postAdGroupObject);
//}
}
/*Merge the "pre" query results with the "post" query results* and store everything into one single array*/
mergedAdGroups = mergeArrayObjects(preAdgroups, postAdgroups);
//Traverse the "mergedAdGroups" array and calculate the impression share difference between the pre vs. post period.
//Add the results to the "alerts" array only if the impression share difference is less than the threshold (10%)
var alerts = [];
mergedAdGroups.forEach(
function(adgroup)
{
if ((!isNaN(adgroup.preImpressionShare)))
{
//Logger.log(adgroup.preImpressionShare + " and " + adgroup.postImpressionShare);
var difference = (adgroup.postImpressionShare - adgroup.preImpressionShare) / adgroup.preImpressionShare;
//campaigns[campaign].difference = difference;
if (difference < -config.imprShareDropThreshold)
{
alerts.push(' - ' + adgroup.adgroup + " of the campaign " + adgroup.campaign + ': from ' + (adgroup.preImpressionShare * 100).toFixed(2) + '% to ' + (adgroup.postImpressionShare * 100).toFixed(2) +'%' + " [" + (difference * 100).toFixed(2) + '%' + " drop.]");
}
}
}
);
//Combine an intro message for the email alert with the contents of the "alerts" variable (see above) and store everything into the "message" variable.
var message =
'The following campaigns had impression share drops by more than ' + (Math.abs(config.imprShareDropThreshold) * 100).toFixed(2) + '% between the pre and post period' + '. \n\n' +
alerts.join('\n') + '\n\n' +
'---END OF MESSAGE---';
//This is for debugging (to see the results in the Google Ads script console)
//Logger.log(message);
//This line passes the "message" variable and sends it as an email alert.
//if (alerts.length > 0) {MailApp.sendEmail(config.emails, 'Impression Share Drop Alert!', message);}
}
//This function merges 2 arrays based on common adgroup IDs
function mergeArrayObjects(arr1,arr2){
return arr1.map((item,i)=>{
if(item.adgroupid === arr2[i].adgroupid){
//merging two objects
return Object.assign({},item,arr2[i])
}
})
}

How to render multiple html lists with AppScrip?

I am new to the world of * AppScript * I am currently designing a ** WepApp ** which is made up of html lists that connect to Mysql, when I individually test my lists paint correctly and their icons modify and update the data, without However ** the problem is ** when I join all my lists and call them through their corresponding url only the last one paints me the others are blank. For example of 10 lists I call # 2 the log tells me to call 10; I call 5 the same thing happens and if I call 10 it paints the data and they are allowed to be modified.
Within what I have searched I find that my problem lies in the way I render my pages but I cannot find the right path, so I ask for your support.
function doGet(e) {
var template ;
var view = e.parameters.v;
if(view == null){
template = HtmlService.createTemplateFromFile("Index");
}if(view == "Index"){
template = HtmlService.createTemplateFromFile("Index");
}if(view != null && view != "Index"){
template = HtmlService.createTemplateFromFile(view);
}
return template.evaluate()
.setTitle('Documental')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
function getTemplate(view){
return HtmlService.createTemplateFromFile(view);
}
and with this JavaScript method I connect my appscript code to pass it to my html
window.onload = function () {
google.script.run
.withSuccessHandler(run_This_On_Success)
.withFailureHandler(onFailure)
.readAreaPRE();
};
function onFailure(error) {
var div = document.getElementById("output");
div.innerHTML = "ERROR: " + error.message;
}
function run_This_On_Success (readAreaPRE) {
let table = $("#selectTable");
table.find("tbody tr").remove();
table.append("<tr><td>" + "</td><td>" + "</td></tr>");
readAreaPRE.forEach(function (e1, readAreaPRE) {
table.append(
"<tr><td>" +
e1[0] +
"</td><td>" +
e1[1] +
"</td><td>" +
"<p><a class='modal-trigger' id=" + e1[0] + " href='#modal1' onclick='capturaid("+e1[0]+",'"+ e1[1]+"')'><i class='material-icons'>edit</i></a>" +
"<a class='modal-trigger' href='#modal3' onclick='capturaidsup("+e1[0]+")'><i class='material-icons'>delete</i></a></p>" +
"</td></tr>"
);
});
};
function capturaidsup(dato1){
$("#delAreaPRE").val(dato1)
}
function capturaid(item1,item2) {
$("#uptAreaPRE1").val(item1);
$("#uptAreaPRE2").val(item2);
}
here is my function: readArePRE
function readAreaPRE() {
var conn = Jdbc.getCloudSqlConnection(url, user, contra);
var stmt = conn.createStatement();
stmt.setMaxRows(1000);
var results = stmt.executeQuery(
"CALL `BD_CENDOC_COL`.`sp_lee_tb_ref_AreasPRE`()"
);
var numCols = results.getMetaData().getColumnCount();
var rowString = new Array(results.length);
var i = 0;
while (results.next()) {
var id_areaPRE = results.getInt("id_areaPRE");
var AreaPRE = results.getString("AreaPRE");
rowString[i] = new Array(numCols);
rowString[i][0] = id_areaPRE;
rowString[i][1] = AreaPRE;
i++;
}
return rowString;
conn.close();
results.close();
}
Thank you in advance, any correction to ask my question will be welcome.

Sending message to Telegram from Google Sheet via Google Scripts

I'm trying to send a telegram message to myself, every morning, with a different quote that I have listed in a Google Sheet. I wrote some code that adds messages to the list, but I can't seem to generate a random row from the list to send daily.
var token = "TOKEN";
var telegramAPI = "https://api.telegram.org/bot" + token;
var webAppAPI = "https://script.google.com/macros/s/GOOGLE_WEB_APP_ADDRESS";
var ssId = "SPREADSHEET_ID";
function getMe() {
var url = telegramAPI + "/getMe";
var response = UrlFetchApp.fetch(url);
Logger.log(response.getContentText());
}
function setWebhook() {
var url = telegramAPI + "/setWebhook?url=" + webAppAPI;
var response = UrlFetchApp.fetch(url);
Logger.log(response.getContentText());
}
function sendText(id,text) {
var url = telegramAPI + "/sendMessage?chat_id=" + id + "&text=" + text;
var response = UrlFetchApp.fetch(url);
Logger.log(response.getContentText());
}
function doGet(e) {
return HtmlService.createHtmlOutput("Test Data" + JSON.stringify(e,null,4));
}
function doPost(e) {
Logger.log(e);
var data = JSON.parse(e.postData.contents);
var text = data.message.text;
var what = data.message.text.split("-")[0]
var who = data.message.text.split("-")[1]
var id = data.message.chat.id;
var name = data.message.chat.first_name;
var response = "Hi " + name + ", this quote has been added to your database: " + text;
sendText(id,response);
SpreadsheetApp.openById(ssId).getSheets()[1].appendRow([new Date(),id,name,text,response,what,who]);
All of this works fine. I added a query that pulls them over to my Quote sheet from my Telegram Feed sheet, that I'll put here to help someone:
=IFERROR(QUERY('Telegram Feed'!$G$1:$G$98,"",-1),"Error")
Now that I'm pulling in quotes, I want to generate a random one from the list and schedule it to send to myself on a daily basis. I've included what I've tried below, but I can't seem to figure out what I'm doing wrong.
The randomizer is partially working, but seems to be grabbing all of the content, which I need to refactor to say something along the lines of:
message = f"{quote} + ' - ' + {author}"
Randomizer:
function randomizer() {
var ssa = SpreadsheetApp.openById(ssId);
var ss = ssa.getSheetByName('Quotes');
var range = ss.getRange(1,1,ss.getLastRow(), 2);
var data = range.getValues();
for(var i = 0; i < data.length; i++)
{
var j = Math.floor(Math.random()*(data[i].length));
var element = data[i][j];
ss.getRange(i+1, 6).setValue(element);
Logger.log(element);
}
}
Up until this point, it mostly works (even though I need to figure out how to fix the randomizer function as mentioned above. It's when I try to send a random message from the script to Telegram that I run into problems.
function sendQuote(what,who) {
var data = randomizer();
var dataJSON = JSON.parse(data.postData.contents);
var url = telegramAPI + "/sendMessage?chat_id=" + 'CHAT_ID_NUM' + "&text=" + what + " - " who;
}
I'm getting nothing back. Anyone know what I'm doing wrong?
EDIT:
I followed the suggestions from Дмитро-Булах & carlesgg97, and I refactored a bunch of my randomize code to give me a quote and author. For some reason, I'm now getting the error "TypeError: Cannot read property "postData" from undefined.: from the line that reads var dataJSON = JSON.parse(data.postData.contents);
Does anyone know why this is happening?
I'll close the issue within 24hrs regardless. Thanks for the help everybody!
function sendQuote(quote,author) {
var data = randomize();
var dataJSON = JSON.parse(data.postData.contents);
var encodedText = encodeURIComponent(quote + " - " + author);
var url = telegramAPI + "/sendMessage?chat_id=" + 'CHAT_ID' + "&text=" + encodedText;
UrlFetchApp.fetch(url);
}
function randomize() {
var sss = SpreadsheetApp.openById(ssId);
var ss = sss.getSheetByName('Quotes');
var length = ss.getLastRow();
var overshoot = 97 //monitor for changes as list size increases
var true_length = length-overshoot;
var line = (Math.random() * ((true_length - 2) + 1)) + 2;
var quote_cell = ss.getRange(line,2);
var quote = quote_cell.getValue();
var author_cell = ss.getRange(line,1);
var author = author_cell.getValue();
Logger.log(quote + " - " + author);
}
Seems like you may be having two different problems:
You are not encoding the text as URL-safe. To safely append data (in this case the text URL Query string parameter) to your URL, you should use encodeURIComponent().
You don't seem to actually be sending the request. Did you miss the UrlFetchApp.fetch() call?
See below an example that fixes both issues:
function sendQuote(what,who) {
var data = randomizer();
var dataJSON = JSON.parse(data.postData.contents);
var encodedText = encodeURIComponent(what + " - " + who);
var url = telegramAPI + "/sendMessage?chat_id=" + 'CHAT_ID_NUM' + "&text=" + encodedText;
UrlFetchApp.fetch(url);
}

Javascript initially skipping over nested function and then comes back to it?

I'm experiencing some weird behavior in my code that I don't quite understand. I call a function, and inside that function there is another (anonymous) callback function it skips over and it goes to the end of the containing function, runs those lines, and then goes back into the callback function and runs those lines... Anybody have some insight, what am I doing wrong? Is it doing this because the "relatedQuery" method isn't complete yet so it hasn't hit the callback function before it runs the rest of the containing function's lines? That's the only thing I can think of, but I'm also not very skilled at JS. I've added some console.log statements that will tell you the order in which lines are being hit.
//Call the mgmtPopupContent function
mgmtTractPopupBox.setContent(mgmtPopupContent);
function mgmtPopupContent(feature) {
for (var attrb in feature.attributes) {
if (attrb == "HabitatManagement.DBO.MgmtTracts.OBJECTID") {
var OID = feature.attributes[attrb];
}
}
var relatedQuery = new RelationshipQuery();
relatedQuery.outFields = ["*"];
relatedQuery.relationshipId = 0;
relatedQuery.objectIds = [OID];
//Get data year that the map view is set to and set the definition expression on the table
viewYear = dom.byId("data-year").value;
relatedQuery.definitionExpression = "YearTreated = " + viewYear;
//Create table header that will go inside popup
var content = '<table id="mgmtPopupTable1"><tr><th>Veg Mgmt Practice</th><th>Herbicide</th><th>Month</th><th>Year</th>\
<th>Implemented By</th><th>Funded By</th><th>Farm Bill Code</th></tr>';
console.log("PRINTS FIRST");
//Do query and get the attributes of each related record for the popup
queryableMgmtTractFL.queryRelatedFeatures(relatedQuery, function (relatedRecords) {
console.log("PRINTS THIRD");
var fset = relatedRecords[OID].features;
fset.forEach(function (feature) {
var vegPractice = vegPName(feature.attributes.VegMgmtPractice);
var herbicide = herbName(feature.attributes.Herbicide);
var monthTreated = monthName(feature.attributes.MonthTreated);
var yearTreated = feature.attributes.YearTreated;
var impBy = impName(feature.attributes.ImplementedBy);
var fundBy = fundName(feature.attributes.FundedBy);
var fbc = feature.attributes.FarmBillCode;
if (fundBy == "CRP" || fundBy == "CRP - CREP") {
fbc = crpName(fbc);
}
else if (fundBy == "EQIP" || fundBy == "EQIP - RCPP") {
fbc = eqipName(fbc);
}
else {
fbc = "Not applicable";
}
row = '<tr><td>' + vegPractice + '</td><td>' + herbicide + '</td><td>' + monthTreated + '</td><td>' + yearTreated +
'</td><td>' + impBy + '</td><td>' + fundBy + '</td><td>' + fbc + '</td></tr>';
content = content + row;
});
content = content + '</table>';
});
console.log("PRINTS SECOND");
return content;
}
As mentioned in my comment, you have to wait for the queries to finish before you can render the content. So something like:
let content = '<table id="mgmtPopupTable1"><tr><th>Veg Mgmt Practice</th><th>Herbicide</th><th>Month</th><th>Year</th>\
<th>Implemented By</th><th>Funded By</th><th>Farm Bill Code</th></tr>';
const render_popup = function( content ) {
document.querySelector( '#myPopup' ).innerHTML = content;
};
// Render only the headers to begin with.
render_popup( content );
queryableMgmtTractFL.queryRelatedFeatures(relatedQuery, function (relatedRecords) {
var fset = relatedRecords[OID].features;
fset.forEach(function (feature) {
...
});
// Rerender the popup, now headers And content.
render_popup( content );
});

Export array of objects into Excel using Javascript

I'm writing a client side method, that creates an array of objects.I open an existing excel to write the values from the array. I get the values using getProperty and store in a variable.
When I try to write those in the excel, I get "event handler failed with message";" ".
Code:
var getItemtoExcel = document.thisItem.newItem("ToExcel", "get");
getItemtoExcel = getItemtoExcel.apply();
var arrToExcel = Array();
for (var j = 0; j < getItemtoExcel.getItemCount(); j++) {
var gotItemForExcel = getItemtoExcel.getItemByIndex(j);
arrToExcel.push(gotItemForExcel);
}
var Excel = new ActiveXObject("Excel.Application");
Excel.Visible = true;
Excel.Workbooks.Open("C:\\test.xls");
var offset = 0;
var row = 2;
for (var c = 0; c < arrToExcel.length; c++) {
var createExcel = arrToExcel[c];
var Number = createExcel.getProperty("nb");
var Type = createExcel.getProperty("type");
var Code = createExcel.getProperty("code");
var State = createExcel.getProperty("state");
Excel.Worksheets("sheet11").Range("A" & row + 1 + offset).Value = Number;
Excel.Worksheets("sheet11").Range("B" & row + 1 + offset).Value = Type;
Excel.Worksheets("sheet11").Range("C" & row + 1 + offset).Value = Code;
Excel.Worksheets("sheet11").Range("D" & row + 1 + offset).Value = State;
row = row + 1;
}
offset = offset + 1;
return this;
document.thisItem.newItem() is from ARASPLM. Its the standard used to call an ItemType(Item) in ARAS
If you have an opportunity to use SheetJS, it's pretty straightforward
Firstly, Install xlsx package npm install --save xlsx
const XLSX = require('xlsx')
// array of objects to save in Excel
let binary_univers = [{'name': 'Hi','value':1},{'name':'Bye','value':0}]
let binaryWS = XLSX.utils.json_to_sheet(binary_univers);
// Create a new Workbook
var wb = XLSX.utils.book_new()
// Name your sheet
XLSX.utils.book_append_sheet(wb, binaryWS, 'Binary values')
// export your excel
XLSX.writeFile(wb, 'Binaire.xlsx');
i think using this you can get what you want but you need to pass the your Object's value with this that i have mentioned here as (Your Data(Object))
window.open('data:application/vnd.ms-excel,' + **(Your Data(Object))**);
here i'm providing simple code for get data into excel format with jquery
SAMPLE DEMO
Thanks for all your suggestions on this question.
I have done with exporting the array into a .csv file successfully.
Here's the code, for others who will need.
var getItemtoExcel=this.newItem("MyForm", "get");
getItemtoExcel=getItemtoExcel.apply();
var arrToExcel = Array();
for (var j=0; j<getItemtoExcel.getItemCount(); j++)
{
var gotItemForExcel=getItemtoExcel.getItemByIndex(j);
arrToExcel.push(gotItemForExcel);
}
var fso = new ActiveXObject("Scripting.FileSystemObject");
var s = fso.CreateTextFile("C:\\REPORT.csv", true);
var title="Report";
s.WriteLine(title);
var header="Number" + ";" + "Type" + ";" + "Code" + ";" + "Created On" + ";" + "State" + '\n' ;
s.WriteLine(header);
for (var c=0; c<arrToExcel.length; c++){
var createExcel = arrToExcel[c];
var Number =createExcel.getProperty("nb");
var Type=createExcel.getProperty("type");
if(Type===undefined){Type="";}
var Code=createExcel.getProperty("code");
if(Code===undefined){Code="";}
var Date=createExcel.getProperty("created_on");
var State=createExcel.getProperty("created_by_id/#keyed_name");
var value=Number + ";" + Type + ";" + Code + ";" + Date + ";" + State;
s.WriteLine(value);
}
s.Close();
alert("Report Saved as C:\\REPORT.csv");
return this;

Categories

Resources