Using MVC3, C#, jQuery, Ajax ++
My html
<div>
Start Long Running Process
</div>
<br />
<div id="statusBorder">
<div id="statusFill">
</div>
</div>
The javascript part part of the html
var uniqueId = '<%= Guid.NewGuid().ToString() %>';
$(document).ready(function (event) {
$('#startProcess').click(function () {
$.post("SendToDB/StartLongRunningProcess", { id: uniqueId,
//other parameters to be inserted like textbox
}, function () {
$('#statusBorder').show();
getStatus();
});
event.preventDefault;
});
});
function getStatus() {
var url = 'SendToDB/GetCurrentProgress';
$.get(url, function (data) {
if (data != "100") {
$('#status').html(data);
$('#statusFill').width(data);
window.setTimeout("getStatus()", 100);
}
else {
$('#status').html("Done");
$('#statusBorder').hide();
alert("The Long process has finished");
};
});
}
This is the controller.
//Some global variables. I know it is not "good practice" but it works.
private static int _GlobalSentProgress = 0;
private static int _GlobalUsersSelected = 0;
public void StartLongRunningProcess(string id,
//other parameters
)
{
int percentDone = 0;
int sent = 0;
IEnumerable<BatchListModel> users;
users = new UserService(_userRepository.Session).GetUsers(
//several parameters)
foreach (var c in users)
{
var usr = _userRepository.LoadByID(c.ID);
var message = new DbLog
{
//insert parameters
};
_DbLogRepository.Save(message);
sent++;
double _GlobalSentProgress = (double)sent / (double)_GlobalUsersSelected * 100;
if (percentDone < 100)
{
percentDone = Convert.ToInt32(_GlobalSentProgress);
}
//this is supposed to give the current progress to the "GetStatus" in the javascript
public int GetCurrentProgress()
{
return _GlobalSentProgress;
}
Right now the div with the progress bar never shows up. It is honestly kind of broken. But I hope you understand my logic.
In the loop doing the insertions, I do have this calculation:
double _GlobalSentProgress = (double)sent / (double)_GlobalUsersSelected * 100;
Then I convert the _GlobalSentProgress to a normal int in the
percentDone = Convert.ToInt32(_GlobalSentProgress);
so it no longer has any decimals any longer.
If only I could send this "percentDone" or "_GlobalSentProgress" variable (wich is showing perfectly how many percent I have come in the insertion) asynchronous into the "data" variable in javascript every single time it loops, it would work. Then "data" would do it's "statusFill" all the time and show the bar correctly. This is the logic I use.
I believe the word thrown around in order to accomplish this is "asynchronous". I have looked at 2 very promising guides but I was not able to make it work with my loop.
Anyone have a suggestion on how I can do this?
Edit 2: Outer div is named statusBorder not status.
Related
I am developing a tracking system for candidates
Attached a search button.
But I want a UI to pop up which will show the duplicates if present.
And when one of the duplicates is clicked, it will fill the required details in the form
I have made this.
But the results are the first row which matches the search text in B4
function Search()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("Form");
var dataS = ss.getSheetByName("Data");
var str = formS.getRange("B4").getValue();
var values = dataS.getDataRange().getValues();
var valuesFound = false;
for (var i = 0; i < values.length; i++)
{
var rowValue = values[i];
if (rowValue[0] == str) {
formS.getRange("B7").setValue(rowValue[0]) ;
formS.getRange("B9").setValue(rowValue[1]) ;
formS.getRange("B11").setValue(rowValue[2]) ;
formS.getRange("B13").setValue(rowValue[3]) ;
formS.getRange("E7").setValue(rowValue[4]) ;
formS.getRange("E9").setValue(rowValue[5]) ;
formS.getRange("E11").setValue(rowValue[6]) ;
formS.getRange("E13").setValue(rowValue[7]) ;
return;
}
}
if(valuesFound==false){
var ui = SpreadsheetApp.getUi();
ui.alert("No record found!");
}
}
Description
You could get all the first name matches and show them in a dialog, numbered so its easier to pick. Once the user picks the name you can fill out the form.
To use a dropdown and pick a name, that would require a custom dialog. Doable but more complex.
This script is executed from a menu item.
Data
Script
function search() {
try {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let dataS = spread.getSheetByName("Data");
let str = "John";
let values = dataS.getDataRange().getValues();
let rowValue = values.filter( row => row[0] === str );
let ui = SpreadsheetApp.getUi();
let prompt = "";
rowValue.forEach( (row,i) => prompt = prompt.concat((i+1).toString(),": ",row[0], " ",row[1],"\n"));
if( rowValue.length === 0 ) {
ui.alert("Name not found ["+str+"]");
return;
}
else if( rowValue.length > 1 ) {
let response = ui.prompt("Pick a name",prompt,ui.ButtonSet.OK_CANCEL);
if( response.getSelectedButton() == ui.Button.OK ) {
str = response.getResponseText();
str = parseInt(str)-1;
rowValue = rowValue[str];
}
}
else {
rowValue = rowValue[0];
}
ui.alert(rowValue.toString());
// Now you can fill out your form with rowValue
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}
Reference
Array.filter()
Array.forEach()
SpreadsheetApp Ui prompt
Description
So I decided to show how a custom dialog can be used to display a list of names to pick from. First a Ui dialog is displayed to use a filter name. If no name is specified all names will be listed.
Using HTMLService a custom dialog is build using the the pushed variable option for an HTM Template.
Once a name is picked from the list google.script.run is used to return the name to the server and the form can be built from there.
Code.gs
function onOpen(e) {
var menu = SpreadsheetApp.getUi().createMenu("My Menu");
menu.addItem("Test","showTest");
menu.addToUi();
}
function showTest() {
try {
let ui = SpreadsheetApp.getUi();
let response = ui.prompt("What name do you want to search for",ui.ButtonSet.OK_CANCEL);
if( response.getSelectedButton() == ui.Button.OK ) {
var name = response.getResponseText();
if( name === "" ) name = "__All";
}
let spread = SpreadsheetApp.getActiveSpreadsheet();
let dataS = spread.getSheetByName("Data");
let data = dataS.getRange(1,1,dataS.getLastRow(),2).getValues(); // Get range A1:B
if( name !== "__All" ) {
data = data.filter( row => row[0] === name );
}
if( data.length === 0 ) {
ui.alert("No names mathcing ["+name+"] found");
return;
}
let html = HtmlService.createTemplateFromFile('HTML_Test');
html.data = data;
html = html.evaluate();
SpreadsheetApp.getUi().showModalDialog(html,"Show Test");
}
catch(err) {
SpreadsheetApp.getUi().alert(err);
}
}
function pickName(name) {
try {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let dataS = spread.getSheetByName("Data");
dataS.getRange(1,5).setValue(name);
// Build your form here
}
catch(err) {
console.log(err);
}
}
HTML_Test.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<select id="selectName">
<? for (let i = 0; i < data.length; i++) { ?>
<option><?= data[i][0]+" "+data[i][1] ?></option>
<? } ?>
</select>
<input type="button" onclick="buttonOnClick()" value="Submit">
<script>
function buttonOnClick() {
let name = document.getElementById("selectName").value;
google.script.run.pickName(name);
google.script.host.close();
}
</script>
</body>
</html>
Reference
HTMLService
HTML Template
google.script.run
google.script.host
I created a script that tracks attendance for distance learning. After a while it times out so I think I am having issues with too many calls to the Google Classroom API, however I don't see a way that I can change it to take those calls out of a loop.
The script takes all the Google Classroom classes that my apps script account is a co-teacher on and using timed triggers creates a daily attendance assignment with one question that says 'here'. Students are then supposed to answer the question and then another trigger at night runs the function to 'grade' each assignment and populate my spreadsheet so school secretaries can view it in the morning and record the previous days attendance.
The part that seems to have the bottleneck is my getStudentResponses() function. I tried to reduce time by filtering out students that didn't submit the assignment, but it wasn't enough. Does anyone see any way that I can make this faster? I was reading up on using the Cache Service, but I couldn't figure out how to get that to work. Any help would be appreciated.
var ss = SpreadsheetApp.getActive();
var date = new Date();
/*
creates a button to programmatically create all necessary timed triggers for easy deployment
*/
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Attendance')
.addItem('Create Triggers', 'createTriggers')
.addToUi();
}
/*
auto accepts any co-teacher invites
*/
function acceptInvite() {
try{
var optionalArgs = {
userId: "me"
};
var invites = Classroom.Invitations.list(optionalArgs);
for(var i = 0; i < invites.invitations.length; i++) {
Classroom.Invitations.accept(invites.invitations[i].id);
}
}
catch(e){}
}
/*
populates a spreadsheet with all the classes that the script Google account is a co-teacher of
the sheet has two columns one with the course name and two with the course id
*/
function listCourses() {
var optionalArgs = {courseStates: "ACTIVE"};
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
var classSheet;
try{
classSheet = ss.insertSheet("Classes", 0);
ss.insertSheet("Assignments", 1);
}
catch(e) {
classSheet = ss.getSheetByName("Classes");
}
classSheet.clear();
if (courses && courses.length > 0) {
for (i = 0; i < courses.length; i++) {
var course = courses[i];
classSheet.appendRow([course.name, course.id]);
}
}
}
/*
reads the sheet to get all the classes and creates a new array with all the class IDs
*/
function getCourses() {
var classSheet = ss.getSheetByName("Classes");
var classList = new Array();
var range = classSheet.getDataRange();
var values = range.getValues();
for(var i in values) {
var row = values[i];
var courseId = row[1]+"";
classList.push(Classroom.Courses.get(courseId));
}
createTopics(classList);
}
/*
called immediatly after getCourses, creates topics in each class that will contain the daily attendance assignment
*/
function createTopics(classList) {
for(i = 0; i < classList.length; i++) {
var topic;
var resource = {name: "Daily Online Attendance"};
try {
topic = Classroom.Courses.Topics.create(resource, classList[i].id);
createAssignment(topic,classList[i]);
}
catch(e) {
if(e == "GoogleJsonResponseException: API call to classroom.courses.topics.create failed with error: Requested entity already exists") {
var topics = Classroom.Courses.Topics.list(classList[i].id);
for(j = 0; j < topics.topic.length; j++) {
if(topics.topic[j].name == "Daily Online Attendance") {
createAssignment(topics.topic[j], classList[i]);
}
}
}
}
}
}
/*
creates an assignment in each class, under each topic
each assignment only has one choice that says "here" and is going to be 'graded' each night to track attendance
*/
function createAssignment(topic,course) {
var resource = {
title: "Attendance for "+(date.getMonth()+1)+"/"+date.getDate()+"/2020",
description: "Please fill this assignment out each day for attendance",
topicId: topic.topicId,
state: "PUBLISHED",
workType: "MULTIPLE_CHOICE_QUESTION",
multipleChoiceQuestion: {
"choices": [
"Here"
]
}
};
try {
var assignment = Classroom.Courses.CourseWork.create(resource, course.id);
var sheet = ss.getSheetByName("Assignments");
sheet.appendRow([course.id,assignment.id]);
}
catch(e){}
}
/*
creates a new sheet for each day and logs each assignement
*/
function getStudentResponses() {
var assignmentSheet = ss.getSheetByName("Assignments");
var sheet2;
var response;
assignmentSheet.sort(1, true);
try{
sheet2 = ss.insertSheet("Attendance for "+(date.getMonth()+1)+"/"+date.getDate()+"/2020",(ss.getSheets().length-(ss.getSheets().length-2)));
sheet2.appendRow(["Student Last Name","Student First Name","Grade","Class Name","Assignment Answer"]);
}
catch(e) {
sheet2 = ss.getSheetByName("Attendance for "+(date.getMonth()+1)+"/"+date.getDate()+"/2020");
}
sheet2.setFrozenRows(1);
var range = assignmentSheet.getDataRange();
var values = range.getValues();
for(var i in values) {
var row = values[i];
var courseId = row[0]+"";
var courseWorkId = row[1]+"";
try {
response = Classroom.Courses.CourseWork.StudentSubmissions.list(courseId, courseWorkId);
for(var j in response.studentSubmissions) {
if(response.studentSubmissions[j].state == "TURNED_IN") {
try {
var grade;
var email = Classroom.UserProfiles.get(response.studentSubmissions[j].userId).emailAddress;
sheet2.appendRow([Classroom.UserProfiles.get(response.studentSubmissions[j].userId).name.familyName,Classroom.UserProfiles.get(response.studentSubmissions[j].userId).name.givenName,grade,Classroom.Courses.get(courseId).name,response.studentSubmissions[j].multipleChoiceSubmission.answer]);
}
catch (e) {}
}
}
}
catch(e) {}
}
}
/*
deletes all assignemnts that were created
*/
function deleteAssignments() {
var assignmentSheet = ss.getSheetByName("Assignments");
assignmentSheet.sort(1, true);
var range = assignmentSheet.getDataRange();
var values = range.getValues();
for(var i in values) {
var row = values[i];
var courseId = row[0]+"";
var courseWorkId = row[1]+"";
try {
Classroom.Courses.CourseWork.remove(courseId, courseWorkId);
}
catch(e) {}
assignmentSheet.clear();
}
}
function createTriggers() {
ScriptApp.newTrigger('getCourses')
.timeBased()
.everyDays(1)
.atHour(6)
.create();
ScriptApp.newTrigger('getStudentResponses')
.timeBased()
.everyDays(1)
.atHour(22)
.create();
ScriptApp.newTrigger('deleteAssignments')
.timeBased()
.everyDays(1)
.atHour(23)
.create();
ScriptApp.newTrigger('listCourses')
.timeBased()
.everyDays(1)
.atHour(21)
.create();
ScriptApp.newTrigger('acceptInvite')
.timeBased()
.everyDays(1)
.atHour(20)
.create();
}
appendRow is slow, you should avoid to used it inside a for loop. Instead build an array, then pass the values using a single setValues call.
Resources
Best Practices | Apps Script
Related
Google Script Performance Slow Down
Increase my script performance Google Sheets Script
Very slow execution of for...in loop
Here is some code, in a cloud function, on Parse-Server using promises.
It is supposed to be called once and do a certain job of removing some contents layed out in a tree stucture.
The problem is that I have to call the same code twice to get the job fully done.
If someone can take a look and say what is wrong, that will be very helpful.
Here is what happens, when the code is called for the first time. All the work is done, except the last removal.
That means the code from the line:
console.log("We finally remove the unit.");
and after is not executed.
When the code is run the second time, the last part is properly executed.
I may have done something incorrect with the promises, but I can't see what.
Parse.Cloud.define
("removeTheThing", function(request, response) {
var thingQuery;
thingQuery = .....;
.....
thingQuery.find().then
(function(resUnit) {
var secondLevelQuery;
secondLevelQuery = .....;
.....
secondLevelQuery.find().then
(function(resSentence) {
var thirdLevelQuery;
thirdLevelQuery = .....;
.....
thirdLevelQuery.find().then
(function(resTranslat) {
var fourthLevelQuery;
fourthLevelQuery = .....;
.....
fourthLevelQuery.find().then
(function(resExplain) {
var destroyPromises = [];
for (i = 0; i < resExplain.length; i++) {
destroyPromises.push(resExplain[i].destroy({}));
}
return Parse.Promise.when(destroyPromises);
}).then
(function() {
var destroyPromises = [];
for (iT = 0; iT < resTranslat.length; iT++) {
destroyPromises.push(resTranslat[iT].destroy({}));
}
return Parse.Promise.when(destroyPromises);
}).then
(function() {
var destroyPromises = [];
const s3 = new aws.S3();
for (iS = 0; iS < resSentence.length; iS++) {
// Let us remove the voice recording:
destroyPromises.push(s3.deleteObject({
Bucket: "londonspeak",
Key: resSentence[iS].get("audio")
}).promise());
destroyPromises.push(resSentence[iS].destroy({}));
}
return Parse.Promise.when(destroyPromises);
}).then
(function() {
console.log("We finally remove the unit.");
//return resUnit[0].destroy({});
//return Parse.Promise.when(resUnit[0].destroy({}));//Promise()
var destroyPromise = [];
destroyPromise.push(resUnit[0].destroy({}));
return Parse.Promise.when(destroyPromise);
}).then(response.success);
});
});
});
});
I do not know what I am talking about here I go.
On some pages it filters them and others like Youtube comments don't work.
What code needs to change in order for it to work in these sites?
// ==UserScript==
// #name profanity_filter
// #namespace localhost
// #description Profanity filter
// #include *
// #version 1
// #grant none
// ==/UserScript==
function recursiveFindTextNodes(ele) {
var result = [];
result = findTextNodes(ele,result);
return result;
}
function findTextNodes(current,result) {
for(var i = 0; i < current.childNodes.length; i++) {
var child = current.childNodes[i];
if(child.nodeType == 3) {
result.push(child);
}
else {
result = findTextNodes(child,result);
}
}
return result;
}
var l = recursiveFindTextNodes(document.body);
for(var i = 0; i < l.length; i++) {
var t = l[i].nodeValue;
t = t.replace(/badword1|badword2|badword3/gi, "****");
t = t.replace(/badword4/gi, "******");
t = t.replace(/badword5|badword6|badword7/gi, "*****");
t = t.replace(/badword8/gi, "******");
l[i].nodeValue = t;
}
* Replaced profanity in code to badword
Youtube comments are loaded asynchronously, quite a long time after the page has loaded (userscripts by default are executed at DOMContentLoaded event), so you need to wrap your code as a callback function of waitForKeyElements with a selector for the comments container or MutationObserver or setInterval.
replaceNodes(); // process the page
waitForKeyElements('.comment-text-content', replaceNodes);
function replaceNodes() {
..............
..............
}
Using setInterval instead of waitForKeyElements:
replaceNodes(); // process the page
var interval = setInterval(function() {
if (document.querySelector('.comment-text-content')) {
clearInterval(interval);
replaceNodes();
}
}, 100);
function replaceNodes() {
..............
..............
}
P.S. Don't blindly assign the value to the node, check first if it has changed to avoid layout recalculations:
if (l[i].nodeValue != t) {
l[i].nodeValue = t;
}
I finally figured out how properly to use Q.all() in my code and it works as expected, but I don't know how to detect the reject if error comes from database in my specific code. I googled a lot but the problem is that in this particular case I can't relate the information I find by google to my own problem! Now with code, I have:
function username(user) {
var deferred = Q.defer();
var queryu = User.find();
queryu.where({_id: user});
queryu.exec(function(err, results) {
if (err) { //system level error
deferred.reject(err);
} else {
var nameAndFB = extractinfo(results[0]);
deferred.resolve(nameAndFB);
}
});
return deferred.promise;
}
later, I have another method that uses this one:
function masterUserObj(user, curstate) {
var p1 = username(user);
var p2 = getState(curstate);
return Q.spread([p1, p2], function(userinfo, pairstate) {
var obj1 = {};
obj1.username = userinfo[0];
obj1.fbid = userinfo[1];
obj1.idprovider = userinfo[2];
obj1.state = pairstate;
return obj1;
});
}
finally, a for loop puts all above to use:
function exposePairs(results, res) {
var plist = [];
for (var i = 0, m = results.length; i < m; i++) {
plist[i] = masterUserObj(results[i].user, results[i].state);
}
Q.all(plist).then(function(theArr) {
return res.jsonp({pairs: theArr});
});
}
Code works, but I don't know where and how best to detect the reject case: deferred.reject(err);
Mainly I'm confused because I just learned to put promises in use together with a loop.
Please point me to the best practices.
Edit:
Also, please comment on code if I should use .done() after the final Q.all() or it is not necessary here.