Not able to use if condition inside sum handlebar's helper - handlebars.js

in our company we are using Celigo middleware to get orders from eshop (US branch) into our accounting system (EU HQ).
They are using kinda tricky field mapping process between these two systems, if you want to use conditions or any other function they are using handlebars: https://docs.celigo.com/hc/en-us/articles/360039326071-Handlebars-helper-reference link to their documentation with basic helpers, etc.
I come into situation when I want to do sum of tax rates but only when tax amount is greater than zero... For instance, you can have four different tax rates in US, however for shipping only two-three of them are applied, but in the payload all four are available so I want to filter out rates with zero amount.
origin handlebar without conditions:
{{#if shipping_lines_1.price}}
{{multiply
(sum shipping_lines_1.tax_lines.[0].rate
shipping_lines_1.tax_lines.[1].rate
shipping_lines_1.tax_lines.[2].rate
shipping_lines_1.tax_lines.[3].rate)
100}}
{{/if}}
part of the payload regarding "shipping_lines":
{"shipping_lines_1":
{"id": 1111111111,"carrier_identifier": null,"code": "Standard Shipping","delivery_category": null,"discounted_price": "8.90","discounted_price_set": {"shop_money": {"amount": "8.90","currency_code": "USD"},"presentment_money": {"amount": "8.90","currency_code": "USD"}},"phone": null,"price": "8.90","price_set": {"shop_money": {"amount": "8.90","currency_code": "USD"},"presentment_money": {"amount": "8.90","currency_code": "USD"}},"requested_fulfillment_service_id": null,"source": "shopify","title": "Standard Shipping",
"tax_lines": [
{"price": 0.09,
"price_set": {"shop_money": {"amount": "0.09","currency_code": "USD"},"presentment_money": {"amount": "0.09","currency_code": "USD"}},
"rate": 0.0625,
"title": "IL STATE TAX"},
{"price": 0.00,
"price_set": {"shop_money": {"amount": "0.00","currency_code": "USD"},"presentment_money": {"amount": "0.00","currency_code": "USD"}},
"rate": 0,
"title": "IL COUNTY TAX"},
{"price": 0.00,
"price_set": {"shop_money": {"amount": "0.00","currency_code": "USD"},"presentment_money": {"amount": "0.00","currency_code": "USD"}},
"rate": 0.01,
"title": "IL CITY TAX"},
{"price": 0.07,
"price_set": {"shop_money": {"amount": 0.07,"currency_code": "USD"},"presentment_money": {"amount": "0.07","currency_code": "USD"}},
"rate": 0.0075,
"title": "IL SPECIAL TAX"}],
"discount_allocations": []
}}
as you can see the tax code with a name "IL CITY TAX" has rate 0.01 however amount is zero, which leads into miscalculation in the accounting system when imported shipping tax rate is 8% instead of 7%
So I've added conditions to check tax amount/price at the first place and then do sum of rates when tax amount is greater than zero (in their system 0 or 0.00 is considered as falsy as well when you use #if):
{{#if shipping_lines_1.price}}
{{multiply
(sum
({{#if shipping_lines_1.tax_lines.[0].price}}{{shipping_lines_1.tax_lines.[0].rate}}{{else}}0{{/if}})
({{#if shipping_lines_1.tax_lines.[1].price}}{{shipping_lines_1.tax_lines.[1].rate}}{{else}}0{{/if}})
({{#if shipping_lines_1.tax_lines.[2].price}}{{shipping_lines_1.tax_lines.[2].rate}}{{else}}0{{/if}})
({{#if shipping_lines_1.tax_lines.[3].price}}{{shipping_lines_1.tax_lines.[3].rate}}{{else}}0{{/if}})
)100
}}
{{else}}0{{/if}}
Unfortunately, I always get an error: " Could not compile handle bar ... Parse error on line 4 ... Expecting 'ID', 'STRING', 'NUMBER', 'BOOLEAN', 'UNDEFINED', 'NULL', 'DATA', got 'OPEN_BLOCK'"
The "sum" and "multiply" helpers/functions are already implemented in the middleware system as per documentation attached above. AFAIK I am not able to register any additional helpers, however I was testing in on http://tryhandlebarsjs.com/ with following registered helpers:
Handlebars.registerHelper("multiply",function(a, b) {
return Number(a) * Number(b);
});
Handlebars.registerHelper("sum", function() {
var args = [].concat.apply([], arguments);
var len = args.length;
var sum = 0;
while (len--) {
if (!isNaN(args[len])) {
sum += Number(args[len]);
}
}
return sum;
});
My question here is: is there any Handlebars limitation, am I dumb I don't see missing parenthesis, should I take other approach for sum function or anything else?
Do you have any ideas for handlebars to correct the issue, please?
Thank you in advance.

Handlebars does not support nesting mustache expressions within each other. So {{multiply... {{#if... is simply not valid. The problem you are encountering is a good example of why a templating language, which is used for formatting output, should not be used for performing business logic, as it is doing with the tax calculation in your example.
Truly, I think the most correct solution would be to abandon the sum and multiply Handlebars helpers and perform the tax calculation in JavaScript and pass the result to Handlebars to format.
If, for some reason, this is not an option, and you absolutely must perform this calculation in the template, then you will need to write a custom helper that takes three arguments and, if the first is truthy, returns the second, else returns the third.
The helper would be quite simple:
Handlebars.registerHelper("ifThenElse", function (condition, ifTrue, ifFalse) {
return condition ? ifTrue : ifFalse;
});
You would then use the helper in your template as a Handlebars Subexpression, which allows you to nest helpers:
{{#if shipping_lines_1.price}}
{{multiply
(sum
(ifThenElse shipping_lines_1.tax_lines.[0].price shipping_lines_1.tax_lines.[0].rate 0)
(ifThenElse shipping_lines_1.tax_lines.[1].price shipping_lines_1.tax_lines.[1].rate 0)
(ifThenElse shipping_lines_1.tax_lines.[2].price shipping_lines_1.tax_lines.[2].rate 0)
(ifThenElse shipping_lines_1.tax_lines.[3].price shipping_lines_1.tax_lines.[3].rate 0)
)
100
}}
{{/if}}
I have created a fiddle for reference.

Related

Godot 3.4: Quest System using Dictionaries

I took this to the Godot Reddit first, but honestly it's not much help imo. A lot of questions go unanswered there. So here I am.
As the title says, Im making a quest system in godot 2d using nested dictionaries. Ive seen people use Nodes, Classes or otherwise for their quest systems, and a few dictionary based ones out there. I chose dictionaries as I know them the best(still isnt a whole lot, or i probably wouldnt be here asking this lol) And my quest system is set up like so:
QuestBase --> QuestHandler --> PlayerData
QuestBase is a giant dictionary of all available quests in the game.
PlayerData is a giant dictionary of all the player stats(including active, completed, or failed quests)
and QuestHandler takes a questName in a function to Copy the quest(questName) from QuestBase dict into the PlayerData.quests_active dict.(quests_active is a dictionary of quests(also dictionaries) inside of the PlayerData dictionary lol) But i cant seem to get it to work. I've done it a couple different ways now, including the way the Godot documentation states on how to add Dinctionaries into dictionaries. Please help, this is the error I get from QuestHandler:
Invalid set index 'tutorialQuest' (on base: 'Nil') with value of type 'Dictionary'
QuestBase:
var storyQuests = {
"tutorialQuest":{
"name" : "Your First Steps",#Name of the Quest
"desc" : "Get to know your environment and learn the ropes.",#Description of the Quest
"level" : 1, #Required level before player can accept quest
"questType" : 0, #0=Story Quest || 1=Side Quest || 2=Repeateable Quest
"taskType": 0, #0=Progressive(task must be completed in order) || 1=Synchronous(tasks can be completed in any order, and all at once)
"tasks":{#Dictionary of Quest's Tasks
"task1":{
"text":"Talk to Bjorn",#Text to display in Quest log
"type":0,#0=Talk,1=Slay,2=Fetch,3=Deliver,4=Collect,5=Goto
"quantity":0,#Determines the amount to complete task for Slay, Fetch, and Collect
"target":"Bjorn",#Determines Who to talk to, or who to slay, or what to collect.
"completed":false#Is this task complete?
},
"task2":{
"text":"Talk to Bjorn",
"type":"Talk",
"target":"Bjorn",
"completed":false
},
"task3":{
"text":"Talk to Bjorn",
"type":"Talk",
"target":"Bjorn",
"completed":false
}
},
"itemReward":{
"items":["Sword", "Basic Elixer"],#Names of items to give
"amount":[1, 5]#Amount of each item to give, respectively.
},
"expReward":{
"skill":["Forestry","Smithing"],#Names of skills to add Exp to
"amount": [100,100] #Amount to add to each skill, respectively.
#1st number will increase first skill in "skill", etc...
},
"Complete":false,
"Failed":false
}
}
PlayerData:
var playerData = {
... irrelevant player data...
#Quests
"quests_active": {"blank":"blank"},
"quests_completed": {},
"quests_failed": {},
and Finally, Quest Handler:
func startQuest(questName: String):
var active_quests = PlayerData.playerData.get("quests_active")
if QuestBase.storyQuests.has(questName):
var questCopy = QuestBase.storyQuests.get(questName).duplicate(true)
PlayerData.playerData.get("quests_active")[questName] = questCopy #.quests_active.append(questCopy)
#print("Story Quest Started: " + PlayerData.playerData.QuestsActive.get(questName).get("name"))
elif QuestBase.sideQuests.has(questName):
var questCopy = QuestBase.sideQuests.get(questName).duplicate(true)
active_quests.append(questCopy)
#print("Side Quest Started: " + PlayerData.playerData.QuestsActive.get(questName).get("name"))
else:
print("Quest Doesn't Exist! Check Spelling!")

How to properly set Firebase Realtime Database to avoid null value at the beginning of array?

I'm new to NoSQL database. Currently I'm trying to use the Firebase and integrate it with iOS. When it comes to predefine the database, with trial and error, I try to make it look like this:
When I tried to retrieve the "stories" path in iOS, I get json structure like this:
[
<null>,
{
comments: [
<null>,
1,
2,
3
],
desc: "Blue versus red in a classic battle of good versus evil and right versus wrong.",
duration: 30,
rating: 4.42,
tags: [
<null>,
"fantasy",
"scifi"
title: "The Order of the Midnight Sun",
writer: 1
]
}
]
My question is, why there's always a null at the beginning of each array? What should I do in the database editor to avoid the null?
It looks like you start pushing data to index 1 and not 0, inserting/retrieving data to/from a list starts with index 0:

How do I create a Slot that accepts a currency amount

I want to receive a dollar amount in my utterance. So, for example, if I ask Alexa:
Send $100.51 to Kroger.
(pronounced, One hundred dollars and fifty one cents) I want to receive the value 100.51 in a proper slot.
I have tried searching and I defined my utterance slots like this:
"slots": [
{
"name": "Amount",
"type": "AMAZON.NUMBER"
}
]
But on JSON input I only get this result:
"slots": {
"Amount": {
"name": "Amount",
"value": "?"
}
}
How can I make Alexa accepts currency values?
I'm a bit confused by what you wrote in your last sentence and the code, but I'll confirm that there is no built-in intent or slot for handling currency.
So, you'll have to do it manually using AMAZON.NUMBER slot type as you seem to be trying.
I would imagine that you will want to create utterences with two AMAZON.NUMBER slots - one for dollars and one for cents.
Easy, make a custom slot and just use $10.11, $.03, and $1003.84 as the sample's. It will work as currency now, accepting users dollars and cents utterances and converting them to a dollar $XX.XX format.

CouchDB counting values from a Document

I want to count numeric values in my document. I have only simple documents.
{id: ####,
rev: ####,
"name": "Camera SX",
"colour": "white",
"manufacturer": "Sony",
"rec_limit": 180}
I have 420 documents in this structure with difference rec_limit. And I want to sum all rec_limit. How can i do this ? Is there any Map Function?
and how can i get the min rec_limit or max rec_limit ?
Simply create a view that will use such a map function:
function(doc) {
emit(doc.id, doc.rec_limit);
}
In combination with the reduce function: "_stats".
For more informations check out the docs: http://docs.couchdb.org/en/1.6.1/

Couchbase Reduce function

I am trying to learn how to use map reduce functions with Couchbase. until now i created reports engines based on SQL using Where with multi terms (adding and subtracting terms) and to modify the group part.
I am trying to create this report engine using views.
my problem is how to create a report that enable users to dive in and find more and more data, getting all the way to individual ip stats.
For example. how many clicks where today ? which traffic source ? what did they see? which country ? and etc..
My basic doc for this example look like this:
"1"
{
"date": "2014-01-13 10:00:00",
"ip": "111.222.333.444",
"country": "US",
"source":"1",
}
"2"
{
"date": "2014-01-13 10:00:00",
"ip": "555.222.333.444",
"country": "US",
"source":"1",
}
"3"
{
"date": "2014-01-13 11:00:00",
"ip": "111.888.888.888",
"country": "US",
"source":"2",
}
"4"
{
"date": "2014-01-13 11:00:00",
"ip": "111.777.777.777",
"country": "US",
"source":"1",
}
So i want to allow the user to see at the first screen , how many clicks per day there are at this site.
so i need to count the amount of clicks. simple map/reduce:
MAP:
function (doc, meta) {
emit(dateToArray(doc.date),1);
}
Reduce:
_count
group level 4, group true
will create the sum of clicks per hour.
Now if i want to allow a break down of countries, so i need a dynamic param to change.. from what i am understand it can only by the group level..
so assume i have added this to the emit like this:
emit([dateToArray(doc.date),source],1);
and then grouping level 5 will allow this divide, and using the key too focus on a certein date.. but what if i need to add a county break down? adding this to the emit again?
this seem to be a mess, also if i will want to do a country stats before the source.. is there any smarter way to do this?
Second part...
What if i want to get the first count as follow:
[2014,1,28,10] {ip:"555.222.333.444","111.222.333.444","count":"2"}
i want to see all the ips that are counted for this time...
how should i write my reduce function?
this is my current state that doesnt work..
function(key, values, rereduce) {
var result = {id: 0, count: 0};
for(i=0; i < values.length; i++) {
if(rereduce) {
result.id = result.id + (values[i]).ip +',';
result.count = result.count + values[i].count;
} else {
result.id = values.ip;
result.count = values.length;
}
}
return result;
i didnt get the answer format i was looking for..
i hope this is not to messy and that you could help me with this..
thanks!!
For the first part of your question, I think you are on the right track. That is how you break down views to enable coarse drill down. However, it is important to remember that views are not intended to store your entire documents, nor are they necessarily going to be able to give you a clean cut swatch of data. You probably will need to do fine-filtering within the access layer of your code (using Linq perhaps).
For the second part of your question, a reduce is not the appropriate mechanism to accomplish this. Reduce values have a very finite (and limited) size and will crash the map/reduce engine once they get too big. I suspect you have experimented with that and discovered this for yourself.
The way you worded the question, it seems like you wish to search for all IP addresses that have been counted "X" number of times. This cannot be accomplished directly in Couchbase's map/reduce architecture; however, if you simply want the count for a given IP address, that is something the map/reduce framework has built-in (just use Date + IP as a key).

Resources