Wait for latest values from dependent streams in BaconJS? - functional-programming

I have 3 streams. gradingResult and contextId depend on studentResponse. I need to fire an event and only one event (otherwise, this is trivial) when all 3 have the latest values.
I've tried #combineTemplate and #sampledBy studentResponse. Unfortunately, I always see the wrong data---gradingResult and contextId have the old values in the combined template. How can I wait for all streams to have the latest values?
Code is shown below:
var studentResponse = new Bacon.Bus();
var gradingResult = new Bacon.Bus();
var contextId = new Bacon.Bus();
studentResponse.onValue(function(f) {
gradingResult.push(f);
contextId.push(f);
});
Bacon.combineTemplate({
studentResponse: studentResponse,
gradingResult: gradingResult,
contextId: contextId
}).sampledBy(studentResponse)
.onValue(function(t) {
console.log(t);
});
studentResponse.push(1);
studentResponse.push(2);
studentResponse.push(3);
Link to jsfiddle: https://jsfiddle.net/3o4c9sm8/1/
UPDATE: this is a contrived example. In the real code, gradingResult is an ajax request. Both gradingResult and contextId have time dependencies on studentResponse

The solution is to sample by the stream that updates last. In this case, it's contextId. Changing the code to the following makes it work:
var studentResponse = new Bacon.Bus();
var gradingResult = new Bacon.Bus();
var contextId = new Bacon.Bus();
studentResponse.onValue(function(f) {
gradingResult.push(f);
contextId.push(f);
});
Bacon.combineTemplate({
studentResponse: studentResponse,
gradingResult: gradingResult,
contextId: contextId
}).sampledBy(contextId) //Sampling by stream that updates last <---
.onValue(function(t) {
console.log(t);
});
studentResponse.push(1);
studentResponse.push(2);
studentResponse.push(3);

Looks like plugging the Buses instead of pushing the value inside studentResponse.onValue does the trick:
var studentResponse = new Bacon.Bus();
var gradingResult = new Bacon.Bus();
var contextId = new Bacon.Bus();
gradingResult.plug(studentResponse);
contextId.plug(studentResponse);
Bacon.combineTemplate({
studentResponse: studentResponse,
gradingResult: gradingResult,
contextId: contextId
}).sampledBy(studentResponse)
.onValue(function(t) {
console.log(t);
});
studentResponse.push(1);
studentResponse.push(2);
studentResponse.push(3);

Related

Bug with Woocommerce Webhook and Google Sheet API?

I have set up a link between Woocommerce orders (a ecommerce plugin for WordPress that we use for our NGO), and a Google Sheet table using this script in Google Sheet's script editor:
//this is a function that fires when the webapp receives a GET request
function doGet(e) {
return HtmlService.createHtmlOutput("request received");
}
//this is a function that fires when the webapp receives a POST request
function doPost(e) {
var myData = JSON.parse([e.postData.contents]);
var order_number = myData.number;
var order_created = myData.date_created;
var order_status = myData.status;
var order_total = myData.total;
var billing_first_name = myData.billing.first_name;
var billing_last_name = myData.billing.last_name;
var billing_email = myData.billing.email;
var billing_phone = myData.billing.phone;
var shipping_first_name = myData.shipping.first_name;
var shipping_last_name = myData.shipping.last_name;
var shipping_address_1 = myData.shipping.address_1;
var shipping_address_2 = myData.shipping.address_2;
var shipping_postcode = myData.shipping.postcode;
var shipping_city = myData.shipping.city;
var shipping_country = myData.shipping.country;
var payment_method = myData.payment_method_title;
var currency = myData.currency;
var timestamp = new Date();
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
}
Everything works as intended, every new order is populated in the Google Sheet table a few seconds later.
However, when I apply a filter on any column in Google Sheet, let's say for payment method, selecting "PayPal", no new order will populate the Google Sheet's table.
They are registered in the woocommerce plugin, payment is ok, all is fine, except that Google Sheet does not receive the order.
Even after removing the filter, it doesn't appear.
All next orders will appear if all filters are deactivated in Google Sheet.
So, there is an issue with Google Sheet filters, but I don't know what is causing it. Is it my script? Is it Google API's fault? Woocommerce webhook?
Please note that I am not a developer, I found this script online and tweaked it myself by try and guess for my own needs.
Modification points:
When the sheet of Google Spreadsheet uses the basic filter, when the values are put using appendRow(), the values are not appended. This might be the current specification.
I thought that this might be the reason of your issue.
In your script, the values are put using appendRow(), and appendRow() is used in a loop. In this case, the process cost of the script will become a bit high. When setValues() is used, this issue can be also removed.
In this case, I would like to propose to append the values using setValues(). When setValues() is used, the values can be put to the filtered sheet. But, when the values are put to the filtered sheet, the filtered sheet is not changed while the values are put.
So it is required to refresh the basic filter after the values are put.
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify your script as follows.
From:
var sheet = SpreadsheetApp.getActiveSheet();
for (var i = 0; i < myData.line_items.length; i++)
{ var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
sheet.appendRow([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]); }
To:
var sheet = SpreadsheetApp.getActiveSheet();
var values = [];
for (var i = 0; i < myData.line_items.length; i++) {
var product_sku = myData.line_items[i].sku;
var product_name = myData.line_items[i].name;
var order_status = myData.status;
var product_qty = myData.line_items[i].quantity;
var product_total = myData.line_items[i].total;
values.push([order_created,order_number,order_status,payment_method,product_name,product_sku,product_qty,product_total,order_total,currency,billing_first_name,billing_last_name,billing_phone,billing_email,shipping_first_name,shipping_last_name,shipping_address_1,shipping_address_2,shipping_postcode,shipping_city,shipping_country]);
}
// Put values using "setValues".
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
// Refresh basic filter.
var filter = sheet.getFilter();
if (filter) {
var range = filter.getRange();
for (var i = range.getColumn(), maxCol = range.getLastColumn(); i <= maxCol; i++) {
var filterCriteria = filter.getColumnFilterCriteria(i)
if (filterCriteria) {
filter.setColumnFilterCriteria(i, filterCriteria);
}
}
}
Note:
When you modified the script of Web Apps, please redeploy the Web Apps as new version. By this, the latest script is reflected to the Web Apps. Please be careful this.
References:
setValues(values)
getLastRow()
getFilter()
Class Filter

How to add conferenceDataVersion to Event using C# using parameters or other way

I am trying to use this but after insert event, the property ConferenceData is null
var pm = new Dictionary<string, string>
{
{"conferenceDataVersion", "1"}
};
calenderEvent.ConferenceData.Parameters = new ConferenceParameters(); calenderEvent.ConferenceData.Parameters.AddOnParameters = new ConferenceParametersAddOnParameters(); calenderEvent.ConferenceData.Parameters.AddOnParameters.Parameters = pm;
You don't need to use the Parameters property in order to set ConferenceDataVersion.
If you just want to add a conference to the Event, you can set the ConferenceDataVersion as a parameter of your request before executing it.
You also have to make sure that the request body has the appropriate conference data properties (requestId, conferenceSolutionKey, etc.).
For example:
Event newEvent = new Event()
{
ConferenceData = new ConferenceData()
{
CreateRequest = new CreateConferenceRequest()
{
ConferenceSolutionKey = new ConferenceSolutionKey()
{
Type = "hangoutsMeet" // Change according to your preferences
},
RequestId = "XXXXX" // Unique request ID
}
},
// ... Rest of event properties (start, end, attendees, name, etc.)
};
EventsResource.InsertRequest request = service.Events.Insert(newEvent, calendarId);
request.ConferenceDataVersion = 1; // Set conference data version
Event createdEvent = request.Execute();

Issues DocumentMerge in Google AppMaker

As I would like to create documents by merging the entries in a list into a Google Docs template. I have therefore integrated the DocumentMerge method from my previous question into a printButton in a list widget.
Clicking on the printButton should produce a document that merges the contents of the current row into the document template. But when I click on the printButton the method fails due to a circular reference. How can I fix that? The print method goes like this ...
function printReview(widget) {
var review = app.models.Review.getRecord(widget.datasource.item._key);
var templateId = 'templateId';
var filename = 'Review for ...' + new Date();
var copyFile = DriveApp.getFileById(templateId).makeCopy(filename);
var copyDoc = DocumentApp.openById(copyFile.getId());
var copyBody = copyDoc.getBody();
var fields = app.metadata.models.Review.fields;
for (var i in fields) {
var text = '$$' + fields[i].name + '$$';
var data = review[fields[i].name];
copyBody.replaceText(text, data);
}
copyDoc.saveAndClose();
}
As Morfinismo noticed you are getting the error because you are trying to pass complex object from client to server and serializer fails to handle it. In order to fix that you need to adjust your code:
// onClick button's event handler (client script)
function onPrintClick(button) {
var reviewKey = button.datasource.item._key;
google.script.run
.withSuccessHandler(function() { /* TODO */ })
.withFailureHandler(function() { /* TODO */ })
.printReview(reviewKey);
}
// server script
function printReview(reviewKey) {
var review = app.models.Review.getRecord(reviewKey);
...
}

How to write Meteor.wrapAsync fs.readFile?

I need a function that emits individual lines from a file with newlines. Nothing hard.
But with node, it is hard, and with Meteor, there's an additional complication: you must use Meteor.wrapAsync. Surprisingly, there isn't an example of how to use wrapAsync in the docs, and I could only find a couple of examples online, none of which helped.
I have something like:
var readFileAsync = function (file, cb) {
// From here to below comment works synchronously
var instream = fs.createReadStream(file, function () {
var outstream = new stream;
outstream.readable = true;
outstream.writable = true;
var rl = readline.createInterface({
input: instream,
output: outstream,
terminal: false
});
rl.on('line', function(line) {
console.log(line);
return line;
});
});
// Reference to aforementioned comment
};
var readWatFile = Meteor.wrapAsync(readFileAsync);
var line = readWatFile('/path/to/my/file');
console.log(line);
I know this is wrong because it doesn't work, so how do I write this?
There are two ways to go around it.
Load the whole file into memory and do whatever you want. To do that you can use the Private Assets API
Use node.js streams and stream the file line by line. You would have something like this.
Example code that you would need to tweak to your favorite streaming methods:
var Future = Npm.require('fibers/future');
var byline = Npm.require('byline');
var f = new Future;
// create stream in whatever way you like
var instream = fs.createReadStream(...);
var stream = byline.createStream(instream);
// run stream handling line-by-line events asynchronously
stream.on('data', Meteor.bindEnvironment(function (line) {
if (line) console.log(line)
else future.return();
}));
// await on the future yielding to the other fibers and the line-by-line handling
future.wait();

AS3 Passing and getting data to ASP

I've been researching for days on the issude but till now I still haven found a solution yet.
I have 0 knowledge on ASP. And I just want to able to pass and get var/text from ASP.
Anyone kind enuff to guide me how I can furthur from here?
private function loadASP():void {
var aspSend:URLRequest=new URLRequest("testASP.asp");
var aspLoader:URLLoader = new URLLoader();
aspLoader.load(aspSend);
trace("did send");
//aspLoader.addEventListener(Event.COMPLETE, processASP);
}
private function processASP(e:Event):void {
}
Why have you commented the call to addEventListener method? Uncomment it (and move it up two lines so that it comes before the load call). If the url is correct, the processASP method will be called when the response arrives (in a real life application, make sure you listen for ioError and securityError on the URLLoader - check the link for examples on doing this). You can read the response as e.target.data in the processASP method.
private function processASP(e:Event):void
{
var loader:URLLoader = URLLoader(e.target);
trace("Response is " + loader.data);
}
URLLoader can also be used to send data to the asp page (server).
var ldr:URLLoader = new URLLoader();
var data:URLVariables = new URLVariables();
data.something = "someData";
data.somethingElse = "moreData";
var request:URLRequest = new URLRequest("url.asp");
request.data = data;
request.method = URLRequestMethod.POST;//or GET
ldr.addEventListener(Event.COMPLETE, onLoad);
//listen for other events
ldr.load(request);

Resources