How to capture reject on Q.all() when querying mongoose? - promise

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.

Related

Reduce script run time in Google Apps Script?

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

In Parse server get() method causes crash

I have a find query with include to get pointer data. It's working fine but if the pointer object does not exist then server crashes.
Here is my query:
var repliesQuery = new Parse.Query("Reply");
repliesQuery.include("author");
repliesQuery.find({
useMasterKey: true
}).then(function(foundMessages) {
var results = [];
for (var i = 0; i < foundMessages.length; i++) {
var rp = {};
rp.title = foundMessages[i].get("title");
rp.description = foundMessages[i].get("description");
var author = foundMessages[i].get("author");
rp.authorId = author.id;
results.push(rp);
}
promise.resolve(results);
});
Everything works fine when the author exists, but if it does not exist then the server crashes.
I tried to add this:
if (author.hasOwnProperty('id')) {
rp.authorId = author.id;
}
But still the issue is not resolved.
Is there any way we can fix this issue?
It's most likely because you're accessing a property of an undefined object, in this case author, in line
rp.authorId = author.id
Like Davi suggests, include a check if author exists.
var repliesQuery = new Parse.Query("Reply");
repliesQuery.include("author");
repliesQuery.find({
useMasterKey: true
}).then(function(foundMessages) {
var results = [];
for (var i = 0; i < foundMessages.length; i++) {
var rp = {};
rp.title = foundMessages[i].get("title");
rp.description = foundMessages[i].get("description");
var author = foundMessages[i].get("author");
if (author) {
rp.authorId = author.id;
}
results.push(rp);
}
promise.resolve(results);
});
Your check
if (author.hasOwnProperty('id')) {
rp.authorId = author.id;
}
also accesses a property of author, so again it would throw an error if author is undefined.

Last promise not fullfilled on first call

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);
});
});
});
});

Get courses.list without archived classes

Using a HTTP GET request, how would you only get the classes that are active. Could you add a parameter to the Google API URL that only returns a list of active classes? Or do you have to search through the returned array and delete any classes are archived using a for loop?
var classroom = new XMLHttpRequest();
var accessToken = localStorage.getItem('accessToken');
classroom.open('GET',
'https://classroom.googleapis.com/v1/courses');
classroom.setRequestHeader('Authorization',
'Bearer ' + accessToken);
classroom.send();
classroom.onload = function () {
if (classroom.readyState === classroom.DONE) {
if (classroom.status === 200) {
var response = JSON.parse(classroom.response);
vm.classes = response.courses;
console.log(response);
for (var i = 0; i < response.courses.length; i++){
var courses = response.courses[i];
console.log(courses.name);
}
} else {
console.log("Error Unknown");
}
}
};
Any help would be much appreciated.
Thanks!
There's no filter option yet like with User objects. (That's documented for at least as far as I can tell). So yes you'll have to pull all of the courses and then just filter out the archived courses. https://developers.google.com/classroom/reference/rest/v1/courses there's a CourseState section that lists the 5 possible states a course can be in. [COURSE_STATE_UNSPECIFIED, ACTIVE, ARCHIVED, PROVISIONED, DECLINED]
Reading through the docs, courses.list returns a list of courses that the requesting user is permitted to view. It does not state a direct way of retrieving active classes only. You may have to resort to your said implementation.
Try this:
function get_courses(student) {
var optionalArgs = {
studentId: student
};
var response = Classroom.Courses.list(optionalArgs);
var courses = response.courses;
var active_courses = [];
if (courses && courses.length > 0) {
for (i = 0; i < courses.length; i++) {
var course = courses[i];
if(course.courseState == "ACTIVE"){
active_courses.push(course);
Logger.log('%s (%s)', course.name, course.id);
}
}
} else {
Logger.log('No courses found.');
}
return active_courses;
}

Greasemonkey profanity filter script to work on Youtube comments

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;
}

Resources