load json into excel using office js web addin - excel

if I have json string which looks like this: [{"id":1,"name":"manish"},{"id":1,"name":"John"}]
can I just office js to simply load it in table. I saw this https://learn.microsoft.com/en-us/office/dev/add-ins/excel/excel-add-ins-tables#import-json-data-into-a-table
but then when I add more columns in my json I will have to do code changes and code is not generic enough. I could live with it but was wondering if there is a better way.

You can use Object.keys(obj).length to count the number of properties in one of your JSON objects. Something like myObjects[0].keys(obj).length.
Then get a Range object for the cell that will be the upper-left cell of the table; for example, getRange("A1").
Then use that Range object's getResizedRange method and pass 1 as the first parameter (rows) and pass the number of properties in your JSON object as the second parameter (columns).
Use the Range object that is returned by getResizedRange as the first parameter to the sheet.tables.add method.

Expanding on #RickKirkham answer and the link in the OP from https://learn.microsoft.com/en-us/office/dev/add-ins/excel/excel-add-ins-tables#import-json-data-into-a-table I build a "more generic" approach. My issue was that I have no control over the JSON response, I don't know how many rows, columns etc. I first tried to just use A1 and expected it to expand as needed, but that didn't work.
I did finally get it to work as I wanted below:
var transactions = [
{
"DATE": "2017",
"MERCHANT": "The Phone Company",
"CATEGORY": "Communications",
"AMOUNT": "120"
},
{
"DATE": "2017",
"MERCHANT": "Southridge Video",
"CATEGORY": "Entertainment",
"AMOUNT": "40"
}
];
var sheet = context.workbook.worksheets.getItem("Sheet1");
var rng = sheet.getRangeByIndexes(0, 0, 1, Object.keys(transactions[0]).length)
var expensesTable = sheet.tables.add(rng, true);
expensesTable.name = "ExpensesTable";
expensesTable.getHeaderRowRange().values = [Object.keys(transactions[0])];
for (let i = 0; i < transactions.length; i++) {
expensesTable.rows.add(null, [Object.values(transactions[i])]);
}
if (Office.context.requirements.isSetSupported("ExcelApi", "1.2")) {
sheet.getUsedRange().format.autofitColumns();
sheet.getUsedRange().format.autofitRows();
}
sheet.activate();
PS: Very new to JS/Office JS atm.

Related

How to access a look up activity result with special chars in Azure Data factory?

I need to get a attribute with the total number of pages that an API returns so I can control the loop to get all pages. It's small so I created a LookUp activity I must inside a JSON file but the Field names have special chars on it (":") so I have fields like "soapenv:Envelope". Code like this will not work:
#activity('LookupTest').output.value[0].value.soapenv:Envelope.soapenv:Header.hdr:paginacao.hdr:totalPaginas
It Bring me an error.
{"code":"BadRequest","message":null,"target":"pipeline//runid/28573e28-b5ef-41a6-9f8d-2655696193e1","details":null,"error":null}
How can I access it?
The output of LookUp Activity:
{
"count": 1,
"value": [
{
"soapenv:Envelope": {
"soapenv:Header": {
"mh:messageHeader": {
"mh:codigoPerfilAgente": 3801,
"mh:transactionId": "d6080aa1-80c2-4743-85e3-a6550a22adda"
},
"hdr:paginacao": {
"hdr:numero": 2,
"hdr:quantidadeItens": 30,
"hdr:totalPaginas": 2,
"hdr:quantidadeTotalItens": 48
}
},
"soapenv:Body": {
...
I managed to solve converting to string replacing the ":" to "_":
#string(json(replace(replace(string(activity('LookupTest').output.value[0]), 'soapenv:', 'soapenv_'), 'hdr:', 'hdr_')).soapenv_Envelope.soapenv_Header.hdr_paginacao.hdr_totalPaginas)
Does anyone have a more elegant solution?

Updating multiple worksheets in google spreadsheet

I have some code that look this:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
service = build('sheets', 'v4', credentials=creds)
spreadsheet = {
'properties': {
'title': 'Data Integrity Report Completed on {}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
}
}
spreadsheet = service.spreadsheets().create(body=spreadsheet,
fields='spreadsheetId').execute()
gsheet_id = spreadsheet.get('spreadsheetId')
response_date = service.spreadsheets().values().append(
spreadsheetId=gsheet_id,
valueInputOption='RAW',
range='A1:Z100',
body=dict(
majorDimension='ROWS',
values=miss_occ_df.T.reset_index().T.values.tolist())
).execute()
This code basically creates a google spreadsheet and appends my dataframe to the first worksheet. What I want is to have a spreadsheet that has 3 worksheets. I also need to name each worksheet and upload 3 different dataframes to each worksheet. How can I do this?
You want to achieve the following things.
Create new Spreadsheet.
You want 3 sheets in the created Spreadsheet.
You want to rename the sheets.
Put the values to each sheet.
You want to achieve them using google-api-python-client with Python.
If my understanding is correct, how about this modification? I think that your goal can be achieved by one API call. But in your case, it seems that 2 dimensional array for the request body is required to be used. So in this answer, I would like to propose the method for achieving your goal by 2 API calls. So please think of this as just one of several answers.
The flow of this method is as follows.
Flow:
Create new Spreadsheet.
At that time, the 3 sheets (worksheets) are created by giving the names. In this case, the method of create() is used.
Put the values to 3 sheets using the method of values().batchUpdate().
In your case, the values are put to the new Spreadsheet. So the values can be put using this method.
Modified script:
Please modify the script below service = build('sheets', 'v4', credentials=creds) as follows. And please set variables of sheet names and values.
# Please set worksheet names.
sheetNameForWorksheet1 = "sample1"
sheetNameForWorksheet2 = "sample2"
sheetNameForWorksheet3 = "sample3"
# Please set values for each worksheet. Values are 2 dimensional array.
valuesForWorksheet1 = miss_occ_df.T.reset_index().T.values.tolist()
valuesForWorksheet2 = miss_occ_df.T.reset_index().T.values.tolist()
valuesForWorksheet3 = miss_occ_df.T.reset_index().T.values.tolist()
spreadsheet = {
'properties': {
'title': 'Data Integrity Report Completed on {}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
},
"sheets": [
{
"properties": {
"title": sheetNameForWorksheet1
}
},
{
"properties": {
"title": sheetNameForWorksheet2
}
},
{
"properties": {
"title": sheetNameForWorksheet3
}
}
]
}
spreadsheet = service.spreadsheets().create(body=spreadsheet, fields='spreadsheetId').execute()
gsheet_id = spreadsheet.get('spreadsheetId')
batch_update_values_request_body = {
"data": [
{
"values": valuesForWorksheet1,
"range": sheetNameForWorksheet1,
},
{
"values": valuesForWorksheet2,
"range": sheetNameForWorksheet2,
},
{
"values": valuesForWorksheet3,
"range": sheetNameForWorksheet3,
}
],
"valueInputOption": "USER_ENTERED"
}
response = service.spreadsheets().values().batchUpdate(spreadsheetId=gsheet_id, body=batch_update_values_request_body).execute()
Note:
This modified script supposes that you have already been able to put and get values to the Spreadsheet using Sheets API.
References:
Method: spreadsheets.values.batchUpdate
Method: spreadsheets.create
At first, please confirm whether my understanding for your question is correct. If I misunderstood your question and this was not the direction you want, I apologize.

CDON API RESTful Api GET request

I'm currently working on fetching customer data from cdon, it's an e-commerce platform. They have their API documentation here:
CDON Api Docu
First let me show you my code:
myToken = '<token here>'
myUrl = 'https://admin.marketplace.cdon.com/api/reports/d8578ef8-723d-46cb-bb08-af8c9b5cca4c'
head = {'Authorization': 'token {}'.format(myToken),
'Status':'Online',
'format':'json'}
filters = '?filter={"Status":["Online"],"format": ["json"] }}'
response = requests.get(myUrl + filters, headers=head)
report = response.json()
print(report.products)
This is returning only the parameters. like for example at at this JSON: CDON Github
Status has a value Online this online is a group of itemsthat I only want to get.
What I'm trying to get is a response like this:
{
"Products": [
{
"SKU": "322352",
"Title": "Fabric Cover",
"GTIN": "532523626",
"ManufacturerArticleNumber": "",
"StatusCDON": "Online",
"ExposeStatusCDON": "Buyable",
"InStock": 0,
"InStockCDON": 0,
"CurrentPriceSE": null,
"OrdinaryPriceSE": null,
"CurrentPriceCDONSE": 299.0000,
"OrdinaryPriceCDONSE": null,
"CurrentPriceDK": null,
"OrdinaryPriceDK": null,
"CurrentPriceCDONDK": null,
"OrdinaryPriceCDONDK": null,
"CurrentPriceNO": null,
"OrdinaryPriceNO": null,
"CurrentPriceCDONNO": null,
"OrdinaryPriceCDONNO": null,
"CurrentPriceFI": null,
"OrdinaryPriceFI": null,
"CurrentPriceCDONFI": null,
"OrdinaryPriceCDONFI": null
},
Which means the full list of the items that are Online
How should I put this... among all the API's I tried this one is very confusing, is this even RestFul? If I can achieve the python equivalent of this C# sample code:
public string Post(Guid repordId, string path)
{
var filter = new JavaScriptSerializer().Serialize(new
{
States = new[] { "0" } // Pending state
});
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair("ReportId", repordId.ToString()),
new KeyValuePair("format", "json"),
new KeyValuePair("filter", filter)
});
var httpClient = new HttpClient() { BaseAddress = new Uri("https://admin.marketplace.cdon.com/") };
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("api", ApiKey);
var response = httpClient.PostAsync(path, content).Result;
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
I may be able to undestand how this API works, the response that I got was taken manually from their report function in JSON format.
Image
I made many attempts and at that code ( my code ) I stopped, being on this for 4 hours made me give up and ask. Trust that I have searched as many references as I could. It's really confusing.
How do I get the response that I want? Filtering via url? or via header? is this even restful? Help T_T
The documentation states in the first line, emphasis mine:
In order to generate a report you perform a POST call to the reports API with the parameters you wish to use for the report.
Your Python code does not make a POST request, you are trying a GET request. The documentation goes on
[...] to filter on Swedish orders you set the CountryCodes
attribute to “Sweden” and to get returned and cancelled orders you set
the States attribute to 2 and 3. So in the end the filter would look
like this:
{
"CountryCodes": [ "Sweden" ],
"States": ["2", "3"]
}
So you need to prepare a filter object (a dictionary in Python) with the filters you want. Luckily the Python syntax for dictionaries is equivalent (Python is flexible and also allows single-quoted strings):
filter = {
'CountryCodes': [ 'Sweden' ],
'States': [ '0' ]
}
The documentation goes on
You then post the parameters as form data (content-type:
application/x-www-form-urlencoded) so the request body would look like
this:
ReportId=d4ea173d-bfbc-48f5-b121-60f1a5d35a34&format=json&filter={"CountryCodes":["Sweden"],"States":["2","3"]}
application/x-www-form-urlencoded is the default for HTTP post, the requests module knows that and does this for you automatically. All you need to do is to prepare a data dict which will contain the data you want to post.
data = {
'ReportId': 'd4ea173d-bfbc-48f5-b121-60f1a5d35a34',
'format': 'json'
'filter': json.dumps(filter)
}
The filter parameter is supposed to be in JSON format. You must encode that yourself via json.dumps().
import json
head = { ... as above }
filter = { ... as above }
data = { ... as above }
response = requests.post(url, data, header=head)
I'll leave figuring out setting the Authorization header properly as an exercise for you. Partly because it isn't hard, partly because I have no intention of creating an API key with this website just for testing this and partly because it's entirely possible that your current header already works.

Need to get the values of the objects (JSON) which have been created dynamically

How to get the top object value in PentahoDI? I have got the other elements like Category, Subcategory, section from the following example of Json file. However, I need to capture the first root object which is x#chapter#e50de0196d77495d9b50fc05567b4a4b and x#e50de0196d77495d9b50fc05567b4a4b
{
"x#chapter#e50de0196d77495d9b50fc05567b4a4b": {
"Category": "chapter",
"SubCategory": [
"x#4eb9072cf36f4d6fa1e98717e6bb54f7",
"x#d85849fbde324690b6067f3b18c4258d",
"x#3edff1a1864f41fe8b212df2bc96bf13"
],
"Section": {
"display_name": "Week 1 Section"
}
},
"x#e50de0196d77495d9b50fc05567b4a4b": {
"category": "course",
"Subcategory": [
"x#e50de0196d77495d9b50fc05567b4a4b"
],
"Section": {
"advanced_modules": [
"google-document"
],
}
}
}
In the Fields tab of the Json Input step I have given the Names and Paths as: Category --> $..Category, Subcategory --> $..Subcategory, Section --> $..Section.
However, I am unable to get the root element as it is crucial information for us to work on it. ex (x#chapter#e50de0196d77495d9b50fc05567b4a4b and x#e50de0196d77495d9b50fc05567b4a4b)
I have used the following code to get the values of the dynamic objects but it didnt work. The following is the code I used it.
var obj = JSON.parse (JBlock) //Jblock is the one which holds the entire string.
var keys = Object.name( obj);
JSONPath is not able to get the keys of a JSON structure. This is one of my main issues with JSONPath, and I wish Pentaho had included other JSON parsing engines.
This JavaScript to be used in Modified Java Script Value works for me. Add a value in the fields editor like this:
And then a script like this:
var obj = JSON.parse(JBlock);
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
    var row = createRowCopy(getOutputRowMeta().size());
    var idx = getInputRowMeta().size();
row[idx++] = keys[i];
putRow(row);
}
trans_Status = SKIP_TRANSFORMATION;

mustache.js date formatting

I have started using mustache.js and so far I am very impressed. Although two things puzzle me. The first leads on to the second so bear with me.
My JSON
{"goalsCollection": [
{
"Id": "d5dce10e-513c-449d-8e34-8fe771fa464a",
"Description": "Multum",
"TargetAmount": 2935.9,
"TargetDate": "/Date(1558998000000)/"
},
{
"Id": "eac65501-21f5-f831-fb07-dcfead50d1d9",
"Description": "quad nomen",
"TargetAmount": 6976.12,
"TargetDate": "/Date(1606953600000)/"
}
]};
My handling function
function renderInvestmentGoals(collection) {
var tpl = '{{#goalsCollection}}<tr><td>{{Description}}</td><td>{{TargetAmount}}</td><td>{{TargetDate}}</td></tr>{{/goalsCollection}}';
$('#tblGoals tbody').html('').html(Mustache.to_html(tpl, collection));
}
Q1
As you can see my 'TargetDate' needs parsing but I am unsure of how to do that within my current function.
Q2
Say I wanted to perform some function or formatting on one or more of my objects before rendering, what is the best way of doing it?
You can use "Lambdas" from mustache(5)
"TargetDate": "/Date(1606953600000)/",
"FormatDate": function() {
return function(rawDate) {
return rawDate.toString();
}
}, ...
Then in the markup:
<td>
{{#FormatDate}}
{{TargetDate}}
{{/FormatDate}}
</td>
From the link:
When the value is a callable object, such as a function or lambda, the object will be invoked and passed the block of text. The text passed is the literal block, unrendered.
I have created a small extension for Mustache.js which enables the use of formatters inside of expressions, like {{expression | formatter}}
You would anyway need to create a function that parses your date value like this:
Mustache.Formatters = {
date: function( str) {
var dt = new Date( parseInt( str.substr(6, str.length-8), 10));
return (dt.getDate() + "/" + (dt.getMonth() + 1) + "/" + dt.getFullYear());
}
};
And then just add the formatter to your expressions:
{{TargetDate | date}}
You can grab the code from here: http://jvitela.github.io/mustache-wax/
It's a long time ago but got on this looking for exactly the same. Mustachejs (now) allows you to call functions of the passed data and not only that; in the function the value of this is whatever value is true in a section.
If my template is like this:
{{#names}}
<p>Name is:{{name}}</p>
<!-- Comment will be removed by compileTemplates.sh
#lastLogin is an if statement if lastLogin it'll do this
^lastLogin will execute if there is not lastLogin
-->
{{#lastLogin}}
<!--
formatLogin is a method to format last Login
the function has to be part of the data sent
to the template
-->
<p>Last Login:{{formatLogin}}</p>
{{/lastLogin}}
{{^lastLogin}}
not logged in yet
{{/lastLogin}}
{{#name}}
passing name to it now:{{formatLogin}}
{{/name}}
{{/names}}
And Data like this:
var data={
names:[
{name:"Willy",lastLogin:new Date()}
],
formatLogin:function(){
//this is the lastDate used or name based on the block
//{{#name}}{{formatLogin}}{{/name}}:this is name
//{{#lastLogin}}{{formatLogin}}{{/lastLogin}}:this is lastLogin
if(!/Date\]$/.test(Object.prototype.toString.call(this))){
return "Invalid Date:"+this;
}
return this.getFullYear()
+"-"+this.getMonth()+1
+"-"+this.getDate();
}
};
var output = Mustache.render(templates.test, data);
console.log(output);
You can get the timestamp using simple String methods:
goalsCollection.targetDate = goalsCollection.targetDate.substring(6,18);
Of course, this depends on your timestamp being the same length each time. Another option is:
goalsCollection.targetDate =
goalsCollection.targetDate.substring(6, goalsCollection.targetDate.length - 1);
These techniques aren't specific to Mustache and can be used to manipulate data for any library. See the Mozilla Developer Center Documentation on substring for more details.
To declare a function within a json you can always do this.
var json = '{"RESULTS": true, "count": 1, "targetdate" : "/Date(1606953600000)/"}'
var obj = JSON.parse(json);
obj.newFunc = function (x) {
return x;
}
//OUTPUT
alert(obj.newFunc(123));
Working example of a 'lambda' function for parsing an ISO-8601 date and formatting as UTC:
var data = [
{
"name": "Start",
"date": "2020-04-11T00:32:00.000-04:00"
},
{
"name": "End",
"date": "2022-04-11T00:32:00.000-04:00"
},
]
var template = `
{{#items}}
<h1>{{name}}</h1>
{{#dateFormat}}
{{date}}
{{/dateFormat}}
{{/items}}
`;
var html = Mustache.render(template, {
items: data,
dateFormat: function () {
return function (timestamp, render) {
return new Date(render(timestamp).trim()).toUTCString();
};
}
});
document.getElementById("main").innerHTML = html;
<script src="https://unpkg.com/mustache#4.2.0/mustache.min.js"></script>
<div id="main"></div>
If you want fancier date formatting you could use for example something like:
new Date().toLocaleDateString('en-GB', {
day : 'numeric',
month : 'short',
year : 'numeric', hour: 'numeric', minute: 'numeric'
})
// outputs '14 Apr 2022, 11:11'
I've been using Mustache for my projects as well, due to its ability to be shared across client/server. What I ended up doing was formatting all values (dates, currency) to strings server-side, so I don't have to rely on helper Javascript functions. This may not work well for you though, if you're doing logic against these values client-side.
You might also want to look into using handlebars.js, which is essentially Mustache, but with extensions that may help with client-side formatting (and more). The loss here is that you will probably not be able to find a server-side implementation of handlebars, if that matters to you.

Resources