I have been playing with trying to set up a javascrip app able to run and display the outputs of an Alteryx analytic app hosted on my server. The idea is then to embed it in Tableau server.
I have got something set up which was able to run an app from a user input and show its status.
But fetching the output of the job is a bit more tricky and I can't get my head around it.
It seems I should use getOutputFileURL() function to get what I want but I am really unsure about how I should set that up.
I tried building a function getOutputs() line 54 but it is not giving me anything.
Anyone who had a similar experience would mind giving me a hand here?
Thank you
'''`// Importing the modules & the keys
// polyfilling the extensions browser
import "babel-polyfill";
import keys from "./keys";
import Gallery from "alteryx-subscription-api";
import tableauJS from "./tableau";
// Grab the Run Button
const btn = document.getElementById("btn");
// Grab the input box
const input = document.getElementById("peopleSearch");
// Grab the message box
const inputValue = document.getElementById("message");
// Grab the spinner gif
const loading = document.getElementById("spinner");
// Grab the status paragraph
const statusInfo = document.getElementById("status");
// Grab the output div
const dataPrint = document.getElementById("dataOutput");
// load into the gallery based on the values specified in the keys file
const createGallery = () => {
const gallery = new Gallery(keys.apilocation, keys.apikey, keys.apisecret);
return gallery;
};
async function jobs(jobId) {
const response = await createGallery().getJob(jobId);
const data = await response.json();
if (data.status !== "Completed") {
// if workflow hasn't been completed, keep checking the jobID for its status
jobs(jobId);
if (inputValue.style.display == "block") {
inputValue.style.display = "none";
}
loading.style.display = "inline";
// if error please report
} else if (data.status === "Error") {
console.error(data.status);
const status = data.status;
statusInfo.innerHTML = status;
} else {
// if finished display completed message
loading.style.display = "none";
const status = data.status;
statusInfo.innerHTML = status;
statusInfo.style.display = "inline";
// after 4 seconds remove the input and refresh the DS
removeElement();
}
}
// gets output from the job
async function getOutputs(jobId) {
const response = await createGallery().getJob(jobId);
const data = await response.json();
console.log(data);
for (var i = 0; i < data.length; i++) {
var url = galleryOutput.getOutputFileURL(jobId, data.id, "Raw");
dataPrint.innerHTML == url;
dataPrint.style.display = "inline";
}
}
// on click of buton run the workflow/app specified in the keys file
async function runWorkflow(appId, dataArray) {
const response = await createGallery().executeWorkflow(appId, dataArray);
const data = await response.json();
console.log(data);
// check if the job is running and if finished
jobs(data.id);
getOutputs(data.id);
}
// If you click run, first check if search input has been specified, then it grabs the appID from the keys file
btn.addEventListener("click", () => {
const peopleSearch = input.value;
const appId = keys.appId;
if (!peopleSearch) {
inputValue.style.display = "block";
} else {
inputValue.style.display = "none";
statusInfo.style.display = "none";
const dataArray = [
{
name: "Text Box (414)",
value: peopleSearch
}
];
runWorkflow(appId, dataArray);
}
});
//removes the messages and the search input after 4 seconds and refresh the data source
function removeElement() {
setTimeout(() => {
if ((statusInfo.style.display = "inline")) {
statusInfo.style.display = "none";
input.value = "";
tableauJS.refreshDS();
}
}, 6000);
}
`
A more efficient process would be use Output tool in Alteryx to write to a database or .TDE tableau data file, then ingest into Tableau properly..
Related
i know that the problem is that let todoList is an empty array, but i dont know how to solve it.
the id tags in my created html is so e can create a delete button later
heres my code:
const textArea = document.querySelector("textarea");
const button = document.querySelector("button");
const listContainer = document.querySelector(".list-container");
let id = 0;
let todoList = [];
button.onclick = function () {
const listItem = {
title: textArea.value,
};
todoList.push(listItem);
addToStorage(todoList);
const dataFromStorage = getFromStorage();
createHtml(dataFromStorage);
};
function addToStorage(items) {
const stringify = JSON.stringify(items);
localStorage.setItem("list", stringify);
}
function getFromStorage() {
const data = localStorage.getItem("list");
const unstrigified = JSON.parse(data);
return unstrigified;
}
const createHtml = (data) => {
id++;
listContainer.innerHTML = "";
data.forEach((item) => {
listContainer.innerHTML += `<div class="list-item" data-id=${id}><p>${item.title} </p><button class="remove" data-id=${id}>Delete</button></div>`;
});
};
The problem here is you just forgot to load the data from localStorage when the page loaded like this
window.onLoad = () => {
const dataFromStorage = getFromStorage();
if(dataFromStorage){
createHtml(dataFromStorage);
} else {
createHtml([]);
}
}
The problem in the code is as follows
Initially the todolist will be an empty array. so when you do the below
todoList.push(listItem);
// adding to local storage which will override the existing todos when page is refreshed
addToStorage(todoList);
// So when the below line is executed only the latest todo will be returned
const dataFromStorage = getFromStorage();
createHtml(dataFromStorage);
Fix:
Initialise the todos from localstorage instead of an empty array
let todoList = [];
// change it as below
let todoList = getFromStorage();
Now Modify the getFromStorage() as below
// If the data is present in the localStorage then return it, else return empty array
function getFromStorage() {
const data = localStorage.getItem("list");
if (!data) return [];
const unstrigified = JSON.parse(data);
return unstrigified;
}
Now when the page is loaded, we need to display the todos. Add the below lines of code
window.onload = function () {
createHtml(todoList);
};
That's it. This will fix the issue.
Few minor improvements can be made as well.
todoList.push(listItem);
addToStorage(todoList);
const dataFromStorage = getFromStorage(); // this line is not necessary, remove it
createHtml(dataFromStorage); // change this to createHtml(todoList)
Codepen
Thanks.
everyone, I have some problem with fetching data and displaying message on initial loading as well as when I change some of the input filed value. The idea here is to display specific message in two cases after doing some calculation.
const potrosnja = document.getElementById('potrosnja');
const nagib = document.getElementById('nagib');
const input = document.querySelectorAll('input[type="number"]');
const submitBtn = document.getElementById('submitBtn');
const poruka = document.getElementById('poruka');
let str = document.querySelector('input[name="strane-sveta"]:checked').value;
let godisnjaPotrosnja = parseInt(potrosnja.value);
let nagibKrovaInput = nagib.value;
//On button submit it fetches data and calculate the value needed for yearly consumption of energy
//fetching data
async function dataFetch(){
let response = await fetch('./csvjson.json')
let data = await response.json();
data.map(strana => {
strana.strana.map((item, i) => {
try {
if(item == str && nagibKrovaInput == strana.nagib) {
let result = Math.ceil(godisnjaPotrosnja / strana.vrednost[i]);
console.log("try works")
poruka.innerHTML = `You need <span class="kw">${result}</span>`
}
}
catch(err) {
poruka.innerHTML = `Please fill required fields.`
console.log(err)
}
})
})
}
//event listeners
submitBtn.addEventListener('click', () => {
dataFetch()
console.log('clicked')
input.forEach(input => {
if(input.value == ''){
input.classList.add("active");
}
})
})
I can see that the problem is inside try function, it like condition isn't treated on initial load, and I have to reload page so it would work. Can someone help me understanding what is the problem?
Ok, I found solution... First thing I have to do Is to check if nagibKrovaInput == strana.nagib, after I get true, I compared does the indexOf item is equal as str and after I get true, it will display something. I also changed on click on the button to send values to data function as an arguments and It solved the problem. Tnx for help.
I am working on a simple restaurant web app which uses a mongo-db database to store the menu items. My issue is that I have a client js file that will use a routing function that then accesses the database to return all the menu items of a certain restaurant. My issue is that my endpoint for the url isn't being recognized:
Client.js
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})
}
Server-routing.ts (Routings):
this.router.get("/menus", this.getResturants.bind(this))
this.router.post("/menus", this.addResturaunt.bind(this))
this.router.get("/menus/:rest", this.getResturauntItems.bind(this))
this.router.delete("/menus/:rest",this.deleteResturaunt.bind(this))
this.router.get("/menus/:rest/:item",[this.errorHandler.bind(this),this.getItem.bind(this)])
this.router.post("/menus/:rest",this.addItem.bind(this))
this.router.delete("/menus/:rest/:item",this.deleteItem.bind(this))
Server-routing.ts (function):
public async getResturauntItems(request, response) : Promise<void> {
console.log("Getting Restaurant Items")
let rest = request.params.rest
let obj = await this.theDatabase.getResturauntItems(rest)
console.log(obj)
response.status(201).send(JSON.stringify(obj))
response.end()
}
So, what should happen is a button calls readMenu(), it then makes a GET fetch request to localhost:8080/api/menus/ and then the menu items from the collection should be returned. The issue is that when I click the button, nothing happens. I know it is not being redirected to some other function as they all have "console.log()" to keep track of them and none of them where called. I used the "inspect" tool to see if the request was being sent or received anywhere and nothing. I am unsure of what the issue happens to be. If anyone can help, it would be really appreciated.
you just never called your function, you declared the async function inside your function but never called it.
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})();
}
you need to add () after creating the functions to call it.
I'm doing a Discord Bot and I have a infinite loop with setInterval each 10s but every loop that the setInterval does, it gives me every data of each loop, so I'd like to know how can I do to get only the last data of the last cycle, not every one.
const puppeteer = require('puppeteer');
const Discord = require('discord.js');
const client = new Discord.Client();
const url = 'url to scrape';
var clocks = [];
(async () => {
const URL = url
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto(URL, { 'waitUntil' : 'networkidle2' });
setInterval(async () => {
let clock = await page.evaluate(()=>{
var a = document.getElementById("task-listing-datatable").getAttribute("data-tasks");
var ar = eval(a);
var keyword = ['asdad', 'asdakdada', 'mama', 'Duplicate Fashion Product Identification Task'];
for(let i=0; i<ar.length; i++){
for(let j=0; j<keyword.length; j++){
if(ar[i][1] === keyword[j]){
let job = (`${ar[i][1]}`);
return (`${ar[i][0]} ${ar[i][1]} Paga ${ar[i][3]} Tareas: ${ar[i][5]}`);
}
}
}
});
console.log(`==== first login ====`)
console.log(`==================`)
if(!clocks.includes(clock)) {
client.on('message', (message)=>{
if(message.author.bot === false) {
message.channel.send(clock);
}
});
clocks.push(clock);
// Save the clock so you will remember it next time.
}
await page.reload();
}, 8000)
})()
client.login('discordjs token');
This is how the messages are shown:
enter image description here
As you can see, now it's giving each change not all the data of each cycle
enter image description here
Every time your setInterval runs, it loads the page fresh, gathers information in 'clock', and sends it via discord. The problem is, it does not know what it has already sent you, so you'll get some of the same data every time.
The solution to that is to save the data it finds, and then only create a discord message if the current batch of data is different from all of the previous data.
So you want some kind of data store:
var clocks = [];
(async () => {
setInterval(async () => {
const URL = url
const browser = await puppeteer.launch()
// ...
And then once you've gotten the current clock back, you want to check if it is NOT in the data store.
if(!clocks.includes(clock)) {
If it isn't, then you know that you have a new piece of data to send.
if(!clocks.includes(clock)) {
client.on('message', (message)=>{
message.channel.send(clock);
});
clocks.push(clock); // Save the clock so you will remember it next time.
}
So all in all you have something like:
var clocks = [];
(async () => {
setInterval(async () => {
const URL = url
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto(URL, { 'waitUntil' : 'networkidle2' })
let clock = await page.evaluate(()=>{
var a = document.getElementById("task-listing-datatable").getAttribute("data-tasks");
var ar = eval(a);
var keyword = ['asdad', 'asdakdada', 'mama', 'What Is The Best Dialogue Category About Phones'];
for(let i=0; i<ar.length; i++){
for(let j=0; j<keyword.length; j++){
if(ar[i][1] === keyword[j]){
let job = (`${ar[i][1]}`);
return (`${ar[i][0]} ${ar[i][1]} Paga ${ar[i][3]} Tareas: ${ar[i][5]}`);
}
}
}
});
console.log(`==== first login ====`)
console.log(`==================`)
if(!clocks.includes(clock)) {
client.on('message', (message)=>{
message.channel.send(clock);
});
clocks.push(clock); // Save the clock so you will remember it next time.
}
await page.reload();
console.log(`after reload`)
}, 8000)
})()
While we're at it though, there's no real reason to fire up a new browser window every 10 seconds, it will probably be easier on your computer to load the page once and then simply refresh every 10 seconds.
var clocks = [];
(async () => {
const URL = url
const browser = await puppeteer.launch()
const page = await browser.newPage()
await page.goto(URL, { 'waitUntil' : 'networkidle2' });
setInterval(async () => {
let clock = await page.evaluate(()=>{
var a = document.getElementById("task-listing-datatable").getAttribute("data-tasks");
var ar = eval(a);
var keyword = ['asdad', 'asdakdada', 'mama', 'What Is The Best Dialogue Category About Phones'];
for(let i=0; i<ar.length; i++){
for(let j=0; j<keyword.length; j++){
if(ar[i][1] === keyword[j]){
let job = (`${ar[i][1]}`);
return (`${ar[i][0]} ${ar[i][1]} Paga ${ar[i][3]} Tareas: ${ar[i][5]}`);
}
}
}
});
console.log(`==== first login ====`)
console.log(`==================`)
if(!clocks.includes(clock)) {
client.on('message', (message)=>{
message.channel.send(clock);
});
clocks.push(clock); // Save the clock so you will remember it next time.
}
await page.reload();
}, 8000)
})()
Now, to make sure that your page function (clock) finds a new data point each time, we need to pass our past data points in to it:
let clock = await page.evaluate(clocks=>{
// ...
}, clocks);
Now, inside of the page function you'll have access to the old data points.
Instead of
if(ar[i][1] === keyword[j]){
let job = (`${ar[i][1]}`); // What is this for?
return (`${ar[i][0]} ${ar[i][1]} Paga ${ar[i][3]} Tareas: ${ar[i][5]}`);
}
Check if the data point exists in your clocks array, and only return it if it's new.
if(ar[i][1] === keyword[j]){
let dataPoint =`${ar[i][0]} ${ar[i][1]} Paga ${ar[i][3]} Tareas: ${ar[i][5]}`;
if(!clocks.includes(dataPoint)){
return dataPoint;
}
}
i want to integrate the pagespeedinsights api to my website :
http://ccit324.firebird.sheridanc.on.ca/
ive tried putting the url into the js code shown in this website
https://developers.google.com/speed/docs/insights/v5/get-started
how do I put my website link into this code so that the PageSpeedInsight API works.
the code:
<script>
function run() {
const url = setUpQuery();
fetch(url)
.then(response => response.json())
.then(json => {
showInitialContent(json.id);
const cruxMetrics = {
"First Contentful Paint": json.loadingExperience.metrics.FIRST_CONTENTFUL_PAINT_MS.category,
"First Input Delay": json.loadingExperience.metrics.FIRST_INPUT_DELAY_MS.category
};
showCruxContent(cruxMetrics);
const lighthouse = json.lighthouseResult;
const lighthouseMetrics = {
'First Contentful Paint': lighthouse.audits['first-contentful-paint'].displayValue,
'Speed Index': lighthouse.audits['speed-index'].displayValue,
'Time To Interactive': lighthouse.audits['interactive'].displayValue,
'First Meaningful Paint': lighthouse.audits['first-meaningful-paint'].displayValue,
'First CPU Idle': lighthouse.audits['first-cpu-idle'].displayValue,
'Estimated Input Latency': lighthouse.audits['estimated-input-latency'].displayValue
};
showLighthouseContent(lighthouseMetrics);
});
}
function setUpQuery() {
const api =
'https://www.googleapis.com/pagespeedonline/v5/runPagespeed';
const parameters = {
url: encodeURIComponent('https://developers.google.com')
};
let query = `${api}?`;
for (key in parameters) {
query += `${key}=${parameters[key]}`;
}
return query;
}
function showInitialContent(id) {
document.body.innerHTML = '';
const title = document.createElement('h1');
title.textContent = 'PageSpeed Insights API Demo';
document.body.appendChild(title);
const page = document.createElement('p');
page.textContent = `Page tested: ${id}`;
document.body.appendChild(page);
}
function showCruxContent(cruxMetrics) {
const cruxHeader = document.createElement('h2');
cruxHeader.textContent = "Chrome User Experience Report Results";
document.body.appendChild(cruxHeader);
for (key in cruxMetrics) {
const p = document.createElement('p');
p.textContent = `${key}: ${cruxMetrics[key]}`;
document.body.appendChild(p);
}
}
function showLighthouseContent(lighthouseMetrics) {
const lighthouseHeader = document.createElement('h2');
lighthouseHeader.textContent = "Lighthouse Results";
document.body.appendChild(lighthouseHeader);
for (key in lighthouseMetrics) {
const p = document.createElement('p');
p.textContent = `${key}: ${lighthouseMetrics[key]}`;
document.body.appendChild(p);
}
}
run();
</script>
The PageSpeed Insight will only work once every couple of seconds or minutes without an API Key, because they add a cooldown timer.
So get one from here to be able to make multiple request without a cooldown timer: Get Started with the PageSpeed Insights API
You'll want to add it at the end of the "query" request otherwise it can give a "Invalid API KEY" error.
function setUpQuery() {
const api = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed';
const parameters = {
url: encodeURIComponent(`http://yourwebsite.com`)
};
let query = `${api}?`;
for (key in parameters) {
query += `${key}=${parameters[key]}`;
}
// Add API key at the end of the query
query += "&key=YOUR_API_KEY"
return query;
}
Now to get the results of your website. Everything will be in the json variable.
function run() {
const url = setUpQuery();
fetch(url)
.then(response => response.json())
.then(json => {
// See https://developers.google.com/speed/docs/insights/v5/reference/pagespeedapi/runpagespeed#response
// to learn more about each of the properties in the response object.
console.log(json) // ALL YOUR WEBSITE DATA WILL BE DISPLAYED IN THE CONSOLE
});
}