This might not be the best place to ask, but I was wondering if someone can help a new person out with fulfillment based on #sys.duration. For example, if someone responds to a prompt with 5 years or 10 months, they'll get differing replies based on those values.
I understand this may get tricky if someone replies "5 years" vs. a reply with "3 months..."
I've been using the inline editor and used the below based off of some demos and such I've found online:
const functions = require('firebase-functions');
const {dialogflow} = require('actions-on-google');
const TIME_INTENT = "Time";
const LENGTH_OF_TIME_ENTITY = "LengthOfTime";
const app = dialogflow();
app.intent(TIME_INTENT, (conv) => {
const length_of_service = conv.parameters[LENGTH_OF_TIME_ENTITY].toLowerCase();
if (length_of_time > 5) {
conv.ask("Response 1");
} else {
conv.ask("Response 2");
}
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
Looks like you're on the right track. I have a couple suggestions:
I would change the name of LENGTH_OF_TIME_ENTITY to LENGTH_OF_TIME_PARAMETER. Entities are sort of like categories that user inputs fall under, which in your case would be #sys.duration. Parameters are the actual inputs.
According to their docs the parameter you get from Dialogflow for a #sys.duration parameter is going to come in as an object that looks like {"amount":10,"unit":"min"}, so you'll want to make sure you're handling an object in that form. The Dialogflow docs are a good reference when working with system entities.
It looks like you're trying to do some sort of duration comparison. Moment.js is a frequently used library that can help with that. If you don't want to depend on a library, you'll need to roll your own solution to convert the different possible inputs you get from Dialogflow into the same unit so you can compare it to the duration cutoff you made to decide which response to use.
Putting that together, you'd do something like this:
const functions = require('firebase-functions');
const {dialogflow} = require('actions-on-google');
const moment = require('moment');
const TIME_INTENT = "Time";
const LENGTH_OF_TIME_ENTITY = "LengthOfTime";
const CUTOFF = moment.duration(5, "month");
const app = dialogflow();
app.intent(TIME_INTENT, (conv) => {
const input = conv.parameters[LENGTH_OF_TIME_ENTITY];
const length_of_service = moment.duration(input.amount, input.unit);
if (length_of_service.asSeconds() > CUTOFF.asSeconds()) {
conv.ask("Response 1");
} else {
conv.ask("Response 2");
}
});
exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);
You might need to do some conversion to get the unit strings Dialogflow uses to the form that Moment.js expects, but that should be pretty simple to do. I didn't thoroughly check the unit values for either, but something along these lines:
const toMomentUnit = (unit) => {
switch(unit) {
case "min":
return "minutes";
case "day":
return "days";
case "mo":
return "months";
case "year":
return "years";
default:
throw new Error("Unrecognized unit");
}
};
Related
I am attempting to port an application that had used sequelize 3.30.4 and I'm updating to 6.13, I'm assuming some things have changed because I can't use string literals in a where clause, or so the debugger tells me.
That said, I've done some googling and have found some basic examples that make sense sure but I'm not entirely sure how to convert this string to a format acceptable for findAndCountAll to be happy.
I've attempted something like this, thinking it might at least point me in the right direction however it does not.
let attributes = ['id', 'name', 'locationId'];
let where = undefined;
let order = [['name', 'ASC']];
where = {
classroom: {
locationId: request.query.locationId
}
}
this is the line that did at one time work but no longer works.
where = `"classroom"."locationId" = ${request.query.locationId}`;
const classrooms = await model.classroom.findAndCountAll(_.assign({},
requestHelper.computePaginationObject(request.query.limit, request.query.page), {
attributes,
where: where ? [where] : undefined,
order
}));
how would I go about porting this into the proper format?
Try something like this:
let where = {}
if (<some condition>) {
where = {
locationId: request.query.locationId
}
}
const classrooms = await model.classroom.findAndCountAll(_.assign({},
requestHelper.computePaginationObject(request.query.limit, request.query.page), {
attributes,
where: where,
order
}));
You can also try that even if the first comment of ANATOLY works wonders :
const where = <some condition> ? {locationId: request.query.locationId} :{};
const classrooms = await model.classroom.findAndCountAll(_.assign({},
requestHelper.computePaginationObject(request.query.limit, request.query.page), {
attributes,
where,
order
}));
I have a method called populateProvidedValuesForNewMandate that looks like this
exports.populateProvidedValuesForNewMandate = (team, assignee, disputeValue, lawField,
subjectOfDispute, party, fileReference, costUnit, clientUnit, sideEffect, comment) => {
const teamInput = element(by.css('div#team input'));
const assigneeInput = element(by.css('div#assignee input'));
const disputeValueInput = element(by.id('dispute_value'));
const lawFieldInput = element(by.css('div#law_field input'));
const subjectOfDisputeInput = element(by.id('subject_of_dispute'));
const partyInput = element(by.id('party'));
const fileReferenceInput = element(by.id('file_reference'));
const costUnitInput = element(by.css('div#cost_unit input'));
const clientUnitInput = element(by.id('client_unit'));
const sideEffectInput = element(by.css('div#side_effect input'));
const mandateComment = element(by.id('mandate_comment'));
// TODO: Figure out how to choose these dynamically as well
// relevantCase, risReportRelevant, economicRelevance, activePassive
const relevantCaseInput = element(by.css(".relevant_case input[value='no']"));
const riskReportRelevantInput = element(by.css(".risk_report_relevant input[value='no']"));
const economicRelevanceInput = element(by.css("label[for='economic_relevance']"));
const activePassiveInput = element(by.css(".active_passive input[value='passive']"));
teamInput.sendKeys(team);
assigneeInput.sendKeys(assignee);
disputeValueInput.sendKeys(disputeValue);
lawFieldInput.sendKeys(lawField);
subjectOfDisputeInput.sendKeys(subjectOfDispute);
partyInput.sendKeys(party);
fileReferenceInput.sendKeys(fileReference);
costUnitInput.sendKeys(costUnit);
clientUnitInput.sendKeys(clientUnit);
sideEffectInput.sendKeys(sideEffect);
mandateComment.sendKeys(comment);
// TODO: Figure out how to choose these dynamically as well
// relevantCase, risReportRelevant, economicRelevance, activePassive
browser.actions().mouseMove(relevantCaseInput).doubleClick().perform();
browser.actions().mouseMove(riskReportRelevantInput).click().perform();
browser.actions().mouseMove(economicRelevanceInput).click().perform();
browser.actions().mouseMove(activePassiveInput).click().perform();
};
and here is an example of its use case
values.populateProvidedValuesForNewMandate(texts.DISPUTE_VALUE, texts.PARTY, texts.CLIENT_UNIT,
texts.SIDE_EFFECT, texts.COMMENT);
The method fills out the specified values that lie within a file called texts.js into the appropriate fields. The problem is that I get the error message: 'each key must be a number of string; got undefined' meaning that this method doesn't work because I have to send the keys for each specified variable in the method.
I really want to avoid sending empty strings for this method (especially because it won't work, I've tried it out -> I get a error from the app itself, not protractor/selenium).
How can I turn this method into one that only considers the specified variables in the test cases.
Also as you can see from my comment, I am trying to figure out how to do this for the checkbox and radio buttons as well. If anyone has a hint, I'd really appreciate it
Honestly, only you can answer the question. Because there are hundreds of ways to fo this, and some may work better than another. So to us it's silly to make guesses which way is the best for you. So I'll give one example and hopefully you can take it from here
One way is to make the method accept an object and check if a property has been passed
function fillForm(obj) {
if (obj.hasOwnProperty('team')) teamInput.sendKeys(team);
if (obj.hasOwnProperty('assignee')) assigneeInput.sendKeys(assignee);
if (obj.hasOwnProperty('disputeValue')) disputeValueInput.sendKeys(disputeValue);
// ...
}
and then call it
fillForm({
assignee: texts.ASIGNEE,
disputeValue: texts.DISPUTE_VALUE
})
so it will skip sending keys to team field
So I am using Cloud Functions at the moment. What I am doing is this:
export const onMessageCreate = functions.database.ref('/Users/{user}/{message}/{text}').onCreate((snapshot, context) => {
const data = snapshot.val()
const changedData: string = change(byteData.text)
return snapshot.ref.update({id: compressedByteData}) //This is the problem
})
The change(input: string): string is a custom function of mine that just replaces every occurrence of the word happy birthday with a birthday cake emoji. Thing is however that in my reference which looks like this: "/Users/{user}/master/{messageGroup}/content/{message}" I want to update the value of text. Since, however, I don't know what {text} is I can't store it in the correct location. Does anyone have some suggestions?
The change() function is:
function change(data:string):string {
return data.replace(/\bhappy-birthday\b/g, '🎂')
}
Here is the Firebase node picture:
Firebase Node Setup
So I want to change the 01, 02, 03 with my Cloud Functions (the values don't matter).
Visual Representation of what I want to do:
Visual Representation
New Update following your comments:
If I understand correctly your last comments, this should do the trick:
export const onMessageCreate = functions.database.ref('/Users/{user}/master/{messageGroup}/content/{message}').onCreate((snapshot, context) => {
const data = snapshot.val();
const changedData: string = change(byteData.text);
const user = context.params.user;
const messageGroup = context.params.messageGroup;
return admin.database().ref(`/Users/${user}/master/${messageGroup}/content`).update({4f3f: changedData});
})
Update following your comments:
Sorry but it not 100% clear what you want to update.
If you want "change the value of /Users/{user}/master/{messageGroup}/content/{message} when (you) create the value at /Users/{user}/master/{messageGroup}/content/{message}" with the value of changedData, just do as follows:
export const onMessageCreate = functions.database.ref('/Users/{user}/master/{messageGroup}/content/{message}').onCreate((snapshot, context) => {
const data = snapshot.val();
const changedData: string = change(byteData.text);
return snapshot.ref.update({message: changedData});
})
Within your Cloud Function you will be able to get the path values by doing
const user = context.params.user;
const message = context.params.message;
and then build the desired node reference.
As you can read in the documentation:
You can specify a path component as a wildcard by surrounding it with curly brackets; ref('foo/{bar}') matches any child of /foo. The
values of these wildcard path components are available within the
EventContext.params object of your function. In this example, the
value is available as event.params.bar.
I'm trying to make a compound query (combining two children). What I want to do is fetch objects that are older than 5 minutes AND also are of a type.
So I've combined the children type and date into a new child called type_date and I do my query like this:
const now = Date.now();
const fiveMinutesMs = 5*60*1000;
const fiveMinutesAgo = now - fiveMinutesMs;
const fiveMinutesAgoQuery = "type_" + fiveMinutesAgo;
ref.orderByChild('type_date').endAt(fiveMinutesAgoQuery).once('value');
Only thing is that it doesn't work. It's giving me results of objects which doesn't even have the type_date child.
Try startAt instead of endAt. I hope it works.
EDIT: Oops I was too quick.... it works only if I'm only filtering on the date string but not together with the prepended type which I want...
So this works:
const fiveMinutesAgoIso = new Date(fiveMinutesAgo).toISOString();
const fiveMinutesAgoQuery = String(fiveMinutesAgoIso);
But not:
const fiveMinutesAgoIso = new Date(fiveMinutesAgo).toISOString();
const fiveMinutesAgoQuery = "type_" + fiveMinutesAgoIso;
Just make a Compound queries as doc says :
const fiveMinutesAgoIso = new Date(fiveMinutesAgo).toISOString();
ref.where("type", "==", TYPE_YOU_WANT).orderByChild('type').endAt(fiveMinutesAgoQuery).once('value');
Recently Firebase introduce Cloud Functions.
In my case this feature is very usefull to count elements in my database.
Firebase posted a sample code to count elements but I ask myself some questions with big data.
In our example we consider that we need to count likes for a post.
In the sample code, at each new like, the function count all likes for the current post and update the count.
Do you think it's a good solution for big data ? (For example if we have 1M likes)
Thank you in advance !
Agreed that the code in the functions sample is not ideal for large sets of data.
For a long time I've used a two-stepped approach in my counters:
when a child is added/removed, increase/decrease the counter
when the counter gets deleted, recount all the children (as it does now)
So case #2 is memory-bound the same as the current code. But case #1 triggers on child writes, so is a lot less memory hungry.
The code:
// Keeps track of the length of the 'likes' child list in a separate property.
exports.countlikechange = functions.database.ref("/posts/{postid}/likes/{likeid}").onWrite((event) => {
var collectionRef = event.data.ref.parent;
var countRef = collectionRef.parent.child('likes_count');
return countRef.transaction(function(current) {
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
}
else if (!event.data.exists() && event.data.previous.exists()) {
return (current || 0) - 1;
}
});
});
// If the number of likes gets deleted, recount the number of likes
exports.recountlikes = functions.database.ref("/posts/{postid}/likes_count").onWrite((event) => {
if (!event.data.exists()) {
var counterRef = event.data.ref;
var collectionRef = counterRef.parent.child('likes');
return collectionRef.once('value', function(messagesData) {
return counterRef.set(messagesData.numChildren());
});
}
});
I also submitted this in a PR for the repo.
See the sample of this in functions-samples.
Given a data structure similar to this:
/functions-project-12345
/posts
/key-123456
likes_count: 32
/likes
user123456: true
user456789: true
user786245: true
...
This function would do the trick:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// Keeps track of the length of the 'likes' child list in a separate attribute.
exports.countlikes = functions.database.ref('/posts/{postid}/likes').onWrite(event => {
return event.data.ref.parent.child('likes_count').set(event.data.numChildren());
});
Note that this code is copyright Google and apache licensed. See the code for more details.