CoffeeScript Hubot help: getting data from a function - coffeescript

I'm a novice in need of some Hubot/CoffeeScript help.
I have several responses which will get data from the same source but use and respond to different pieces of the payload. For example...
module.exports = (robot) ->
robot.hear /score/i, (msg) ->
score = getScore(today)
msg.send "today's score is " + score
robot.hear /yesterday's score/i, (msg) ->
score = getStore(yesterday) ->
msg.send "yesterday's score was " + score
The process of building the URL for the score data includes looking up the current month, day and year. I don't want to do that more than once but I will have many responses like the above which use the same data. I had expected I could do this.
getScore = (day) ->
#build the url and get the data set
#pick the right piece of data based on the day variable and assign it to score'
I guess this doesn't work because it's async. But, doing msg.send from within the getScore function doesn't work. So, how do I do this so that I don't have to repeat the getScroe code in every robot.hear section?
Thanks!

Pseudo code:
getScore = (day, callback) ->
# get the score...
callback(score)
robot.hear /yesterday's score/i, (msg) ->
getScore "yesterday", (score) ->
msg.send "yesterday's score was " + score

Related

How to get email form submissions script to exclude blank response values from sheet?

I am editing an existing script that my team uses for a google form response sheet. The script automatically creates a message body using the headers and response cells for an order every time it is submitted, roughly like this:
Type of Order: Physical
Country: America
Digital Signature:
Favorite Color:
Favorite Food: Pasta
What I've been asked to do, is have the script read through the sheet and not include the header or response for questions that are not answered in any given submission. Like so, for the previous example:
Type of Order: Physical
Country: America
Favorite Food: Pasta
I should start by saying I have close to 0 experience in javascript or Google Apps. I have tried playing around with if clauses using both the len function and a negated isblank function to no avail. These all lead to undefined errors.
As you'll see, the original script was not created by me or the people who have been using it for the last few years.
Original script
function sendFormByEmail(e)
{
Logger.log('value of e is: ' + e);
var email = "xxx#xxx.com";
var s = SpreadsheetApp.getActiveSheet();
var headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
var message = "";
var subject = "Type A Request: ";
// The variable e holds all the form values in an array.
// Loop through the array and append values to the body.
// Insert variables from the spreadsheet into the subject.
// In this case, I wanted the new hire's name and start date in the
// email subject. These are the 3rd and 16th columns in my form.
for(var i in headers)
message += headers[i] + ': '+ e.namedValues[headers[i]].toString() + "\n\n";
subject += e.namedValues[headers[10]].toString() + " - " +
e.namedValues[headers[12]].toString();
MailApp.sendEmail(email, subject, message, {noReply:true});
// Based off of a script originally posted by Amit Agarwal - www.labnol.org
}
You can include a check for blank values inside the for loop.
if (e.namedValues[headers[i]].toString() === "") continue;

Combine Mono with every Flux element emitted

I have a Flux and Mono as below:
Mono<MyRequest> req = request.bodyToMono(MyRequest.class);
Mono<List<String>> mono1 = req.map(r -> r.getList());;
Flux<Long> flux1 = req.map(r -> r.getVals()) // getVals() return list of Long
.flatMapMany(Flux::fromIterable);
Now for each number in flux1, I want to call a method where params are the id from flux1 and the List<String> from mono1. Something like,
flux1.flatMap(id -> process(id, mono1))
But passing and processing same mono1 results in error Only one connection receive subscriber allowed. How can I achieve above? Thanks!
Since both information are coming from the same source, you could just run the whole thing with one pipeline like this and wrap both elements in a Tuple or better, a domain object that has more meaning:
Mono<MyRequest> req = // ...
Flux<Tuple2<Long, List<String>>> tuples = req.flatMapMany(r ->
Flux.fromIterable(r.getVals())
.map(id -> Tuples.of(id, r.getList()))
);
// once there, you can map that with your process method like
tuples.map(tup -> process(tup.getT1(), tup.getT2());
Note that this looks unusual, and this basically comes from the structure of that object you're receiving.

Hubot Hear Variable

I was hoping to find away to get hubot to here a variable.
Ex
name = "Peter"
module.exports = (robot) ->
robot.hear /hello name/i, (msg) ->
msg.send "Peter?! No I am Hubot."
I have already tried "#{}" syntax like below, but got nowhere with it.
name = "Peter"
module.exports = (robot) ->
robot.hear /hello #{name}/i, (msg) ->
msg.send "Peter?! No I am Hubot."
Any help would be greatly appreciated.
Regards,
Austin
Since your regex is not a constant, you should use new Regex():
Using the constructor function provides runtime compilation of the regular expression. Use the constructor function when you know the regular expression pattern will be changing, or you don't know the pattern and are getting it from another source, such as user input.
Code
name = "Peter"
regx = new Regex("hello #{ name }", 'i')
module.exports = (robot) ->
robot.hear regx, (msg) ->
msg.send "Peter?! No I am Hubot."
Edit
With name as a parameter
module.exports = (robot, name) ->
regx = new Regex("hello #{ name }", 'i')
robot.hear regx, (msg) ->
msg.send "#{ name }?! No I am Hubot."
In case its useful, a hubot example using javascript rather than coffeescript that also takes the option chosen and places it in another variable:
const options = 'now|later|never';
const regexOptions = new RegExp(`starting (${options})`, 'i');
robot.respond(regexOptions, (msg) => {
// this will respond to:
// hubot starting now
// but not to:
// hubot starting notAnOption
const optionChosen = msg.match[1];
msg.send(`option chosen: ${optionChosen}`);
});
I use this technique when I have dynamic lists that I can add or remove names into, that I then want to use within responses - it does require a quick reload of hubot when adding a name to a list used in a command of course.

What is the procedure to email the test score of a test conducted on google forms?

I have created a test through google form, and I want to send the score result to the participants. I have created a copy of the response and put scoring through if functions. And then in the next worksheet, I have summed the score. Now I want to send that calculated score to the participants. I have entered the script in the script editor and set trigger on form submit but I am getting errors.
Would the error be because the script takes the default sheet and not the one where I have created score function? If so, how do I change that?
Here is the code that I used:
function myFunction(e)
{
var userName = e.values[1];
var userEmail = e.values[2];
var score = e.values[3];
var subject = "Thank you for your participation: Find your Score";
var message = "Thank you, " + userName + " for choosing to participate in this test. Your score is " + score;
MailApp.sendEmail(userEmail, subject, message);
}
There are two ways. Send an e-mail from the form or send a notification from the result collection spreadsheet (limited to spreadsheet collaborators).
For the first, you have to use the script editor found under tools. There are several examples to get you started. I'd recommend you take a look at: http://www.labnol.org/internet/auto-confirmation-emails/28386/
For the second, you use the notifications option, see: https://www.maketecheasier.com/send-email-notifications-google-forms/
Here is the code that I used
function myFunction(e){
var userName = e.values[1];
var userEmail = e.values[2];
var score = e.values[3];
var subject = "Thank you for your participation: Find your Score";
var message = "Thank you, " + userName + " for choosing to participate in this test. Your score is " +score;
MailApp.sendEmail (userEmail, subject, message);}

Coffeescript running a second function before the first has finished executing

My code is as follows. I would expect the code inside configureMission() to finish running before running mapSurface. This does not appear to be happening.
missionCommands = "" # you don't have a mission yet
$('#startExploration').click -> # Set #missionControl as input for game configuration
configureMission()
mapSurface()
configureMission =->
$('#MissionControl').on "submit", ->
# cool helper function shamelessly pinched from http://jsfiddle.net/sxGtM/3/
$.fn.serializeObject = function() # not shown
missionCommands = JSON.stringify($('form').serializeObject())
return false #stops click event from firing
mapSurface =->
console.log('map')
console.log(missionCommands)
I have noticed that if I submit the form a second time, the missionCommands variable has been updated with the json data, so it appears that the form data has been processed, but that this is happening after the second function has run.
map login.js:60
login.js:61
map login.js:60
{"xMaximum":"","yMaximum":"","xCoord":["iuoiuyi",""],"yCoord":["",""],"orientaiton":["",""]} login.js:61
I can make it work by moving the mapSurface function inside the configureMission function, but this seems like bad form. I wonder, is there a more correct pattern I could use to achieve my desired result of processing the form data into json, setting this into a variable and passing the variable to a second function.
You're treating asynchronous commands as synchronous commands. JavaScript (and thereby CoffeeScript) works asynchronously, meaning that functions will be run in parallel.
You can solve this problem by specifying mapSurface() as the callback for configureMission() as follows:
missionCommands = ""
$('#startExploration').click ->
configureMission(mapSurface)
configureMission = (cb) ->
console.log "now running configureMission()"
$('#MissionControl').on "submit", ->
$.fn.serializeObject = function() # not shown
missionCommands = JSON.stringify($('form').serializeObject())
console.log "completed configureMission()"
return cb()
mapSurface = ->
console.log "now running mapSurface()"
console.log missionCommands
console.log "completed mapSurface()"
Learn about JavaScript callbacks here: http://recurial.com/programming/understanding-callback-functions-in-javascript/.