How to map JSON Array with Adaptive Card Row - Using Designer to Create Template - adaptive-cards

I am trying to create a template for the adaptive card. My adaptive card is similar to the expense report adaptive card in many ways. https://adaptivecards.io/samples/ExpenseReport.html
It is basically a timesheet submission card for the manager to approve the timesheet. It should look something like this.
Preview of the draft Adaptive Card (with a static number of rows)
The challenge I am facing is fixing a number of rows, samples provided has a fixed number of rows. In real cases, the number of rows will be dynamic. One timesheet will have 4 rows and others will have 2 rows. So template with a fixed number of rows is not going to work in my case.
What I would like to do is use the templating feature and create one row in the adaptive card template and bind it with an array of rows in JSON. Based on the size of the Array, rows will be replicated in the adaptive card. The following is a sample template.
Adaptive Card template
Databinding screenshot
JSON: Number of array items will be dynamic, and the expectation is to have a template consider this and expand.
"teRows": [{
"date": "Date1",
"task": "task1",
"hours": "10"
}, {
"date": "Date2",
"task": "task2",
"hours": "20"
}, {
"date": "Date3",
"task": "task3",
"hours": "30"
}, {
"date": "Date4",
"task": "task4",
"hours": "10"
}
]
Templating Guide: https://learn.microsoft.com/en-us/adaptive-cards/templating/language

I figured it out, for array we need to create binding using {$root.arrayname}. I was missing that part.
Basically $root is your entire JSON. Now, wherever the array is JSON
we need to address it accordingly.
Example JSON:
{
"title": "username (timePeriod)",
"header":[
{
"field":"Submitted On",
"value":"Date"
},
{
"field":"Total Hours",
"value":"40"
}
],
"submittedOn": "dateField",
"totalHours": "totalHours",
"description": "data editor",
"creator": {
"name": "NxP"
},
"teRows":[ {
"date": "Date1",
"task": "task1",
"hours": "10"
},{
"date": "Date2",
"task": "task2",
"hours": "20"
},{
"date": "Date3",
"task": "task3",
"hours": "30"
}
]
}
Case 1: Retrieve Title using
text property = {title}
Data Context = blank
Case 2: Retrieve Creator Name
text property = {creator.name}
Data Context = blank
Case 3: Map Rows to the teRows Array.
Option 1: Add binding at container level - ColumnSet level
columnset text property = blank
columnset Data Context = {$root.teRows}
Add individual columns text property
Date text property = {date}
Task text property = {task}
Hours text property = {hours}
Option 2: Add data binding and text property at column level and not at column set level
Date text property = {date}
Date Data Context = {$root.teRows}
Task text property = {task}
Task Data Context = {$root.teRows}
Hours text property = {hours}
Hours Data Context = {$root.teRows}
The output of the Card with dynamic array binding.

Related

Add data to a json file using Talend

I have the following JSON:
[
{
"date": "29/11/2021",
"Name": "jack",
},
{
"date": "30/11/2021",
"Name": "Adam",
},
"date": "27/11/2021",
"Name": "james",
}
]
Using Talend, I wanna add 2 lines to have something like:
[
{
"company": "AMA",
"service": "BI",
"date": "29/11/2021",
"Name": "jack",
},
{
"company": "AMA",
"service": "BI",
"date": "30/11/2021",
"Name": "Adam",
},
"company": "AMA",
"service": "BI",
"date": "27/11/2021",
"Name": "james",
}
]
Currently, I use 3 components (tJSONDocOpen, tFixedFlowInput, tJSONDocOutput) but I can't have the right configuration of components in order to get the job done !
If you are not comfortable with json .
Just do these steps :
In the metaData just create a FileJson like this then paste it in your job as a tFileInputJson
Your job design and mapping would be
In your tFileOutputJson don't forget to change in the name of the data block "Data" with ""
What you need to do there according to the Talend practices is read your JSON. Then extract each object of it, add your properties and finally rebuild your JSON in a file.
An efficient way to do this is using tMap componenent like this.
The first tFileInputJSON will have to specify what properties it has to read from the JSON by setting your 2 objects in the mapping field.
Then the tMap will simply add 2 columns to your main stream, here is an example with hard coded string values. Depending on you needs, this component will also offer you the possibility to assign dynamic data to your 2 new columns, it's a powerful tool for manipulating the structure of a data stream.
You will find more infos about this component in the official documentation : https://help.talend.com/r/en-US/7.3/tmap/tmap; especially the "tMap scenarios" part.
Note
Instead of using the tMap, if you are comfortable with Java, you can use a tjavaRow instead. Using this, you can setup your 2 new columns with whatever java code you want to put as long as you have defined the output schema of the component.
output_row.Name = input_row.Name;
output_row.date = input_row.date;
output_row.company = "AMA";
output_row.service = "BI";

Use Doctrine to search into a json database column

I have a Symfony 3.2 project, and I need to filter data from a json column.
Given that we have an entity named "pack" with a json column named "settings" containing this kind of data:
{
"name": "My pack",
"blocks": [
{
"name": "Block 1",
"fields": [
{"label": "A", "value": "57"},
{"label": "B", "value": "100"}
]
},
{
"name": "Bock 2",
"fields": [
{"label": "C", "value": "80"}
]
}
]
}
I have to search packs with a field which has the label "B" and its value at "100", but each pack doesn't have same blocks and fields order.
So in my repository, using Doctrine\ORM\EntityRepository and opsway/doctrine-dbal-postgresql (for GET_JSON_FIELD and GET_JSON_OBJECT functions), this kind of condition works:
use Doctrine\ORM\EntityRepository;
class Packs extends EntityRepository
{
public function findFiltered(...)
{
return $this->createQueryBuilder('pack')
->andWhere("GET_JSON_FIELD(GET_JSON_OBJECT(pack.settings, '{blocks,0,fields,1}'), 'label') = :label")
->andWhere("GET_JSON_FIELD(GET_JSON_OBJECT(pack.settings, '{blocks,0,fields,1}'), 'value') = :value")
->setParameter('label', 'B')
->setParameter('value', '100')
;
}
}
But the problem is that I have to specify the precise block (the first block object), and the precise field (the second field object of the first block object). And my two condition aren't connected, it search if there is a label "B", then it search if there is a value "100". When I would like to have a research in all blocks and fields to find the good label for the good value. Any idea?
I found the good SQL request for my problem:
SELECT *
FROM pack p, json_array_elements(p.settings#>'{blocks}') blocks, json_array_elements(blocks#>'{fields}') fields
WHERE fields->>'label' = 'B' and fields->>'value' = '100';
But how I do that with doctrine?
Maybe this link can help you, it is a custom filter for a JSON type field, maybe it will serve as an example, but these functions with this bundle solved the problem for me. I hope this helps someone else too. Cheers!

Populating data after check from JSON data

I'm kind of confused and after spending a lot of time on it, I have not found any good solution to my problem. So without even wasting my time I'm explaining my question.
I have a JSON data, with a data[] having different objects which looks like this :
{
"data": [
{
"id": 1,
"name": "XYZ",
"course": {
"id": 25,
"name": "XYZ",
"description": "",
"teacher": {
"id": 4,
"name": "",
"email": "",
"role": "",
"token": "",
"about": "Blah blah blah ",
"phone": "2222222222",
"image_url": "",
"payment_information": {}
},
"image": "",
"cover_image": "",
"course_type": "",
"ongoing": true,
"price": 10,
"students_count": 4,
"lesson_price": 2.5,
"drop_in_price": 12,
"lessons_data": {
"first_lesson_at": "",
"last_lesson_at": "",
"next_lesson_at": ""
},
"data": {
"number_of_classes": "",
"start_time": "06:45",
"day_of_week": "Tuesday",
"duration": "-300",
"start_date": "2017-11-07T06:45:04.000-0800",
"end_time": "06:50"
}
},
"start_time": "2018-01-23T14:45:00.000Z",
"end_time": "2018-01-23T14:50:00.000Z",
"zoom_url": "http://zoom.us/j/5124648907"
}
]
}
This is only one object which I just showed it to you but there are many other objects present inside the data array.
I have a widget which has a block and accepts the title and the , we will look at the later. So for this first I want to loop through the data object and check if the timestamps' date is matching with the other one or not and then get the particular amount of that widget after using *ngFor in my html
For now I have 7 object inside the JSON data and after getting the result from the timestamps I have 4 objects which has similar start_time (2 same and other 2 same).
More pieces of the information :
JSON data
1 with Wednesday from Timestamp
2 objects with Thursday from Timestamp
2 Objects with Friday from Timestamp
1 with Saturday from Timestamp
So according to the above data we have to get only 4 blocks, but I have tried finding some solution to it but failed.
I have tried doing this
<div *ngFor="les lesson of lessons">
<widget-app-block [title]="lesson.start_time | date: 'EEEE, MMMM d'"></widget-app-block>
</div>
But it prints all the 7 blocks and prints the Time for all the 7 blocks. But what we need here to get only 4 blocks which would be sorted after checking.
POSSIBLE ATTEMPTS
I have read about using *ngIF-else condition from HERE, so what I have done is I have tried getting the results from my TYPESCRIPT file and tried matching it with the lesson variable from the foreach loop in my html and then pass the title else just print the timestamp for individual objects but didn't work out.
TYPESCRIPT
ngAfterViewInit() {
this.http.get("/lessons").subscribe(data => {
this.lessons = castCollection(data['data'], Lesson)
for(let time of this.lessons){
this.timeTitle = time.start_time
}
})
}
and then comparing it to the lesson variable in my html
HTML
<div *ngFor="let lesson of lessons">
<div *ngIf="lesson.start_time === timeTitle; else elseTimeBlock">
<widget-app-block [title]="timeTitle | date: 'EEEE, MMMM d'"></widget-app-block>
</div>
<ng-template #elseTimeBlock>
<widget-app-block [title]="lesson_start_time | date: 'EEEE, MMMM d'"></widget-app-block>
</ng-template>
</div>
But no luck. Since my data fetching is working fine so, now I have to just focus on the data itself. Could any one please help me with this. I'd be grateful to learn new thing. Since this is very tricky.
It sounds like what you need is to filter your list to match a particular timestamp. I have a blog post here on filtering data in Angular: https://blogs.msmvps.com/deborahk/filtering-in-angular/
Something like this:
performFilter(filterBy: string): IProduct[] {
filterBy = filterBy.toLocaleLowerCase();
return this.products.filter((product: IProduct) =>
product.productName.toLocaleLowerCase().indexOf(filterBy) !== -1);
}
I've filtering on a string, but you could change this to filter on a timestamp instead.
See the blog post for the full code.

ElasticSearch Nested Array Partial Update

I have this particular object which contains the my_array:
"description": "My Object Description",
"my_array": [
{
"id": 1000,
"name": "abc",
"url" : "abc.html",
"content": "somebig content"
},
{
"id": 1001,
"name": "def",
"url" : "def.html",
"content": "somebig content"
},
{
"id": 1002,
"name": "xyz",
"url" : "xyz.html",
"content": "somebig content"
} ]
Each element in array contains a url. Now whenever this object changes, i have a task which hits the url for each element of the array, gets the html content for that element, and creates request document which can be indexed into elasticsearch.
Lets say, the url for id = 1001 is not accessible, and content for this element cannot be accessed. I still want to go ahead and process changes for elements 1000, and 1002. In that case my update would look like this:
"description": "My New Object Description",
"my_array": [
{
"id": 1000,
"name": "abc",
"url" : "abc-new-url.html",
"content": "some modified content"
},
{
"id": 1002,
"name": "xyz",
"url" : "xyz-new-url.html",
"content": "some modified content"
} ]
If i send this partial update to elasticsearch, the collection gets updated but element 1001 is removed from the collection.
My problem is how can i selectively update elements 1000, and 1002 without touching 1001. Index being stale with 1001 here is ok for me. One obvious choice is to fetch the existing doc from elasticsearch, and do the merging manually before doing the update. Is there any other way this partial update can be performed?
Another question, is there any way to send just the url to elasticsearch, and write a plugin to fetch the html content at index time, rather then doing it beforehand?
I think you could solve this using scripting in a update query, see these answers here:
remove objects from array elastic search
You can't do such an update using Elasticsearch native APIs. However, if you don't want to merge the updated content manually on your application level, a possible solution is to store each element of the array in a document with the same index as your original document, but different type.
Then do the update for each one of these elements (which in this case becomes documents) separately

typeahead nested json object

I am new to Ember and JSON. I want to parse a JSON object that is below with typeahead library
and access nested object values by searching their keys.
I have this Json format:
return [
{
"id": 1,
"category_name": "Supermarket",
"category_description": "SUPER MARKET",
"image_url": "",
"merchants": [
{
"name": "CARREFOUR",
"id": 12,
"merchant_type_id": 1,
"merchant_type_description": "Gold",
"merchant_redeption_rate": 0.002500,
"image_url": "https://jpg",
"branches": [
{
"id": 123456,
"latitude": 37.939483,
"area": "ΑΓ. ΔΗΜΗΤΡΙΟΣ",
"zip": "12345"
},
{
"id": 4567890,
"longitude": 23.650622,
"area": "ΑΓ. ΙΩΑΝΝΗΣ ΡΕΝΤΗΣ",
"zip": "12345"
}
]
},
{
"name": "CAFCO",
"id": 13,
"merchant_type_id": 3,
"merchant_type_description": "None",
"merchant_redeption_rate": 0.002500,
"image_url": "https:.jpg",
"branches": [
{
"id": 127890,
"latitude": 38.027870,
"area": "ΠΕΡΙΣΤΕΡΙ",
"zip": "12345"
}
]
}
]
},
{
"id": 2,
"category_name": "Πολυκαταστήματα",
"category_description": "ΠΟΛΥΚΑΤΑΣΤΗΜΑ",
"image_url": "",
"merchants": [
{
"name": "AGGELOPOYLOS CHR.",
"id": 15,
"merchant_type_id": 2,
"merchant_type_description": "Silver",
"merchant_redeption_rate": 0.002500,
"image_url": "https://www.nbg.gr/greek/retail/cards/reward-programmes/gonational/PublishingImages/aggelopoulos.jpg",
"branches": [
{
"id": 234780,
"latitude": 35.366118,
"longitude": 24.479461,
"address": "ΕΘΝ. ΜΑΚΑΡΙΟΥ 9 & ΕΛ. ΒΕΝΙΖΕΛΟΥ 1",
"area": "Ν. ΦΑΛΗΡΟ",
"zip": "12345"
}
]
}
]
}
];
--------------------------Updated----------------------------
For example, i want to search using typeahead the name of merchants and when the letter we write to search matches the name of merchants it will appear the corresponding category_name and backwards.
Example -> when i keyboard the s it will appear :
Category : Supermarket,
Name: CARREFOUR
Name: CAFCO
And the same output on the dropdown of search when i keyboard the letter c.
Any help?
New Jsbin example
The simplest way (in my mind) to get this to work is to create a computed property that will contain an array of latitudes. But how do we get there?
To get to latitude, you need to go through array of merchants and then array of branches. Being that this will be across multiple elements, you are going to end up with "array of arrays" type data structure, which is annoying to deal with. So, to simplify this, we can create a simple flatten function as follows:
flatten: function(origArray){
var newArr = [];
origArray.forEach(function(el) {
el.forEach(function(eachEl){
newArr.push(eachEl);
});
});
return newArr;
},
In addition to our function above, Ember already provides us with many other useful functions that can be used on arrays (see here). One of those is mapBy(property) which transforms an array into another array only keeping the values of the property we specified.
So, to create a lats (for latitudes) property, we can just do this:
lats: function(){
var merchantsArr = this.get('model').mapBy('merchants');
merchantsArr = this.flatten(merchantsArr);
var branchesArr = merchantsArr.mapBy('branches');
branchesArr = this.flatten(branchesArr);
return branchesArr.mapBy("latitude").compact();
}.property('model')
Above, I am basically using mapBy, flatten (see above) and compact which
Returns a copy of the array with all null and undefined elements removed.
Once you have the lats property with all the necessary data, the rest is easy.
Your call to component becomes:
{{x-typeahead data=lats name='category_name' selection=myColor}}
Note lats instead of model you originally were passing into the component.
And now, to access the value of data property in the component, you do
`this.get('data')`
which you can just pass in as the source like so:
source: substringMatcher(self.get('data'))
Working solution here
Update
Updating my answer based on your updated question.
OK, so this is getting a little more complicated. You now need more than just one property (latitude) from the object. You need category_name and merchant name.
In addition to mapBy, which just grabs one property out of array, Ember also has map which lets you transform the array into pretty much anything you want to:
lats: function(){
var merchantsArr = this.get('model').map(function(thing){
var category_name = thing.category_name;
return thing.merchants.map(function(merchant){
return {
"name": merchant.name,
"category": category_name
};
});
});
merchantsArr = this.flatten(merchantsArr);
return merchantsArr;
}.property('model')
The code above looks complicated, but it's basically just returning an array of top level objects' merchants accompanied by category_name. Since this is an array of arrays, we will need to flatten it.
Then, inside the component, we need to keep in mind that we are not just passing in an array of strings, but rather we are passing in an array of objects. Therefore, we need to look through object's properties (name and category) for a match
$.each(strs, function(i, str) {
if (substrRegex.test(str.name) || substrRegex.test(str.category)) {
matches.push(str);
}
});
Lastly, to actually display both category and merchant name, you need to tell Typeahead how to do that:
templates: {
suggestion: Handlebars.compile('<p>{{name}} – {{category}}</p>')
}
Working solution here