I can’t get two groups of dynamic sprites or a dynamic sprite and a group of dynamic sprites to collide. I can get each to collide with groups of static sprites that make up the level, but not the dynamic ones.
If I setup a collider for the sprites themselves instead of the group, they work fine. Not sure what’s going on.
Here is a link to my code. Could someone take a look?
https://github.com/deusprogrammer/mega-man-clone/blob/master/src/scripts/scenes/mainScene.ts
The reason why the collisions doesn't work, is the physics - body of the group of enemies, seems to be interfering with, physics objects of the objects inside of the group.
Just replace the line 19 this.enemyGroup = this.physics.add.group(); (from the linked file) mainScene.ts, with this line this.enemyGroup = this.add.group(); and the collisions should work.
That line will only create a "simple" phaser group, without a physics body.
Here a running demo:
(I stripped your code, down to only essentials)
class MegaMan extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
super(scene, x, y, 'megaman');
scene.add.existing(this);
scene.physics.add.existing(this);
this.bulletGroup = scene.physics.add.staticGroup();
this.scale *= 2;
this.body.setSize(22, 22);
this.setOrigin(0.5, 0.5);
this.setCollideWorldBounds(true).setInteractive();
this.controls = {
up: this.scene.input.keyboard.addKey('W'),
down: this.scene.input.keyboard.addKey('S'),
left: this.scene.input.keyboard.addKey('A'),
right: this.scene.input.keyboard.addKey('D'),
jump: this.scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE),
shoot: this.scene.input.keyboard.addKey('J')
};
}
update() {
super.update();
// Controls
if (this.controls.right.isDown) {
this.setVelocityX(150);
} else if (this.controls.left.isDown) {
this.setVelocityX(-150);
} else {
this.setVelocityX(0);
}
// If player is jumping
if (this.controls.jump.isDown && this.body.touching.down) {
this.setVelocityY(-400);
}
if (this.controls.shoot.isDown && !this.isShooting) {
this.isShooting = true;
let megaBusterShot = this.scene.physics.add.sprite(this.x + 32, this.y, 'wood1');
this.bulletGroup.add(megaBusterShot);
megaBusterShot.body.allowGravity = false;
megaBusterShot
.refreshBody()
.setVelocityX(this.flipX ? 300 : -300);
setTimeout(() => {
this.isShooting = false;
}, 200);
}
}
onHit() {
console.log("OUCH");
}
}
class MetHat extends Phaser.Physics.Arcade.Sprite {
constructor(scene, x, y) {
super(scene, x, y, 'methat');
scene.add.existing(this);
scene.physics.add.existing(this);
this.scale *= 1.5;
this.state = "idle";
this.setCollideWorldBounds(true);
this.setInteractive();
this.refreshBody();
}
onCollision() {
if (this.body.touching.left) {
this.body.velocity.x = 200;
} else if (this.body.touching.right) {
this.body.velocity.x = -200;
}
}
onHit(weapon) {
this.destroy();
}
update() {
if (this.scene.game.loop.frame % 500 === 0 && this.body.touching.down && this.state === 'idle') {
this.state = 'standing';
// Make him start walking shortly after standing up
setTimeout(() => {
this.state = 'walking';
this.setVelocityX(-200);
}, 200);
// Make him idle again after 10 seconds.
setTimeout(() => {
this.state = 'idle';
this.setVelocityX(0);
}, 3000);
}
}
}
let assetMap = {
"W1": {},
"W2": {},
"W3": {
name: "wood3",
file: "https://github.com/deusprogrammer/mega-man-clone/raw/master/src/assets/img/wood3.png",
},
"W4": {
name: "wood4",
file: "https://github.com/deusprogrammer/mega-man-clone/raw/master/src/assets/img/wood4.png",
},
}
const levels = {
level1: {
tilemap: [
["W3", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "W3"],
["W3", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "W3"],
["W3", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", "W3"],
["W3", " ", " ", " ", " ", "W4", "W4", " ", " ", " ", " ", "W3"],
["W3", " ", " ", " ", " ", "W4", "W4", " ", " ", " ", " ", "W3"],
["W4", "W4", "W4", "W4", "W4", "W4", "W4", "W4", "W4", "W4", "W4", "W4"],
],
blocksX: 12,
blocksY: 6,
nextLevel: 'level2'
}
}
class Level extends Phaser.GameObjects.GameObject {
constructor(scene, levelConfig) {
super(scene, 'level');
this.levelConfig = levelConfig;
scene.add.existing(this);
scene.physics.add.existing(this);
this.blocks = this.scene.physics.add.staticGroup();
for (let y = 0; y < this.levelConfig.blocksY; y++) {
for (let x = 0; x < this.levelConfig.blocksX; x++) {
let asset = assetMap[this.levelConfig.tilemap[y][x]];
if (!asset) {
continue;
}
let block = scene.physics.add.staticSprite(x * 32, y * 32, 'floor');
block.scale *= 2;
block.setOrigin(0, 0)
.setPushable(false)
.setImmovable(true)
.setGravity(0)
.refreshBody();
this.blocks.add(block);
}
}
}
}
class MainScene extends Phaser.Scene {
constructor() {
super({ key: 'MainScene' });
}
preload(){
let graphics;
graphics = this.make.graphics({x: 0, y: 0});
graphics.fillStyle(0xffffff, 1);
graphics.fillRect(0, 0, 22, 22);
graphics.generateTexture('megaman', 22, 22);
graphics.fillStyle(0xff0000, 1);
graphics.fillRect(0, 0, 22, 22);
graphics.generateTexture('methat', 22, 22);
graphics.fillStyle(0xcdcdcd, 1);
graphics.fillRect(0, 0, 16, 16);
graphics.generateTexture('floor', 16, 16);
}
create() {
this.player = new MegaMan(this, 60, 0);
this.level = new Level(this, levels['level1']);
this.enemyGroup = this.add.group();
this.enemyGroup.add(new MetHat(this, 300, 0));
// Doesn't work? Why?
this.physics.add.collider(this.player, this.enemyGroup, () => {
this.player.onHit();
});
this.physics.add.collider(this.player.bulletGroup, this.enemyGroup, (megaBusterShot, obj2) => {
let enemy = obj2;
megaBusterShot.destroy();
enemy.onHit(megaBusterShot);
});
// Works
this.physics.add.collider(this.player.bulletGroup, this.level.blocks, (megaBusterShot) => {
megaBusterShot.destroy();
});
this.physics.add.collider(this.enemyGroup, this.level.blocks, (obj) => {
let enemy = obj;
enemy.onCollision();
});
this.physics.add.collider(this.player, this.level.blocks);
}
update() {
this.player.update();
this.enemyGroup.children.each((enemy) => {
enemy.update();
})
}
}
const config = {
type: Phaser.AUTO,
backgroundColor: '#0000000',
scene: [MainScene],
width: 400,
height: 248,
physics: {
default: 'arcade',
arcade: {
debug: true,
gravity: { y: 600 }
}
}
}
const game = new Phaser.Game(config)
<script src="https://cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.js"></script>
I had a similar problem (https://stackoverflow.com/a/70944607/1679286), seems to be a problem with nesten Objects, that have physics bodies.
Related
Current code
async function checkPoint(arrayPoint, collection, origin){
const trial = await collection.findOne({ 'Points': { $exists: true, }})
if(trial == null){
collection.insertOne({Points: ['floater', '1']})
trial = await collection.findOne({ 'Points': { $exists: true, }})
}
foundPoint = false;
pointArray = trial.Points
for(let i = 0; i < pointArray.length; i++){
newPoint = pointArray[i]
newID = newPoint[0]
newValue = newPoint[1]
if(newID == arrayPoint){
console.log("It seems we have data ! Aha !" + arrayPoint )
val = parseInt(newValue)
val = value + 1;
val = value.toString();
newValue = val;
pointArray[i] = [newId, newValue]
await collection.updateOne(trial, {$set: {'Points': pointArray}} )
foundPoint = true
}
else{
console.log("It seems id: " + arrayPoint + " doesn't match id " + newID)
}
}
if (foundPoint == false){
pointArray.push([arrayPoint, '1'])
await collection.updateOne(trial,{$set: {'Points': pointArray}} )
}
}
The code executes itself perfectly, but doesn't change 'points' without anything
I tried basic bug fixing, looking at documentation and couldn't find anything about passing arrays.
the value it passes is
{
acknowledged: true,
modifiedCount: 0,
upsertedId: null,
upsertedCount: 0,
matchedCount: 0
}
Usually I refuse to give an answer if required information are not delivered or if comments are rejected, but I make an exception. If you would have run console.log(tojson(trial)) as suggested in my comment, you would see immediately that trial is
{ _id: ObjectId("6389ea1d06..."), Points: { ["floater", ["1", "NaN"] ] }
but not
{ _id: ObjectId("6389ea1d06..."), Points: { ["floater", "1"] }
as you may assume.
Your problem is this code:
pointArray = trial.Points // <- pointArray is not a copy of trial.Points, it is a reference to original object
for (let i = 0; i < pointArray.length; i++) {
newPoint = pointArray[i]
newID = newPoint[0]
newValue = newPoint[1]
if (newID == arrayPoint) {
console.log("It seems we have data ! Aha !" + arrayPoint)
val = parseInt(newValue)
val = value + 1;
val = value.toString();
newValue = val;
pointArray[i] = [newId, newValue]
await collection.updateOne(trial, { $set: { 'Points': pointArray } })
foundPoint = true
} else {
console.log("It seems id: " + arrayPoint + " doesn't match id " + newID)
}
}
Maybe it becomes clear if I rewrite it. Below code does exactly the same:
for (let i = 0; i < trial.Points.length; i++) {
newPoint = trial.Points[i]
newID = newPoint[0]
newValue = newPoint[1]
if (newID == arrayPoint) {
console.log("It seems we have data ! Aha !" + arrayPoint)
val = parseInt(newValue)
val = value + 1;
val = value.toString();
newValue = val;
trial.Points[i] = [newId, newValue] // <- here is the problem
await collection.updateOne(trial, { $set: { 'Points': trial.Points } })
foundPoint = true
} else {
console.log("It seems id: " + arrayPoint + " doesn't match id " + newID)
}
}
I would suggest to update collection like this:
await collection.updateOne({ _id: trial._id }, { $set: { 'Points': trial.Points } })
I am having a extension where it can notify about new items added to RSS feed reader
All works in v2, but v3 I am unable to load jquery into service worker, since chrome doesnt allow.
As a workaround I have added as module
"background": {
"service_worker": "js/background.js","type": "module"
},
But still its a issue and says ReferenceError: $ is not defined
at Object.parseFeed
Or is there a way I can tweak my code to read xml without jquery?
import $ as module from 'js/jquery-2.1.4.min.js';
var Storage = (function() {
return {
getFeeds: function(callback, returnArray, sortArray) {
returnArray = typeof returnArray !== 'undefined' ? returnArray : true;
sortArray = typeof sortArray !== 'undefined' ? sortArray : true;
chrome.storage.sync.get(function(dataObject) {
var result = dataObject;
if (returnArray) {
var feedArray = this.parseDataObjectIntoArray(dataObject);
result = sortArray ? this.sortArrayOfObjects(feedArray, 'position') : feedArray;
} else {
delete result['RssR:Settings'];
}
callback(result)
}.bind(this));
},
getFeedByUrl: function(feedUrl, callback) {
chrome.storage.sync.get(feedUrl, function(feedData) {
callback(feedData[feedUrl]);
});
},
removeFeedByUrl: function(feedUrl) {
chrome.storage.sync.remove(feedUrl);
},
saveFeedData: function(feedData) {
var saveFeed = {};
saveFeed[feedData.url] = feedData;
this.setDataObject(saveFeed);
},
parseDataObjectIntoArray: function(object) {
var array = [];
Object.keys(object).forEach(function(objectKey) {
if (objectKey.indexOf('RssR:Settings') !== 0) {
array.push(object[objectKey]);
}
});
return array;
},
sortArrayOfObjects: function(array, sortKey) {
array.sort(function(a, b) {
if (typeof a[sortKey] === 'undefined') {
return true;
} else if (typeof b[sortKey] === 'undefined') {
return false;
}
return a[sortKey] - b[sortKey];
});
return array;
},
clearAllData: function() {
chrome.storage.sync.clear();
},
setDataObject: function(dataObject) {
chrome.storage.sync.set(dataObject);
}
}
}());
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
function msToTime(ms) {
let seconds = (ms / 1000).toFixed(1);
let minutes = (ms / (1000 * 60)).toFixed(1);
let hours = (ms / (1000 * 60 * 60)).toFixed(1);
let days = (ms / (1000 * 60 * 60 * 24)).toFixed(1);
if (seconds < 60) return seconds + " Sec";
else if (minutes < 60) return minutes + " Min";
else if (hours < 24) return hours + " Hrs";
else return days + " Days"
}
var FeedService = (function() {
return {
downloadFeed: function(feed) {
if (!feed.url) {
return;
}
console.log(feed.url)
fetch(feed.url)
.then(response => response.text())
.then(xmlString => {
console.log(xmlString)
this.parseFeed(xmlString, feed);
}
)
.then(data => console.log(data))
},
parseFeed: function(rawData, existingFeedData) {
var newFeedData = {};
var $feedEntries = $(rawData).find("entry").length > 0 ? $(rawData).find("entry") : $(rawData).find('item');
var lastUpdate = Date.now()
console.log("Now = " + lastUpdate)
lastUpdate = (existingFeedData && existingFeedData.lastUpdate) || Date.now()
console.log("Last Update = " + lastUpdate)
console.log("existingFeedData = " + JSON.stringify(existingFeedData))
pubDates = [];
$feedEntries.each(function(index, elm) {
pubDates.push(new Date($(elm).find("pubDate").text()).getTime())
if (lastUpdate < new Date($(elm).find("pubDate").text()).getTime()) {
console.log($(elm).find("title").text().substring(0, 20));
chrome.notifications.create(
$(elm).find("link").text(), {
type: "basic",
iconUrl: "../icons/254.png",
title: $(elm).find("title").text(),
message: "(" + msToTime((Date.now() - new Date($(elm).find("pubDate").text()).getTime())) + ") ",
requireInteraction: true
},
function() {}
);
}
}.bind(this));
console.log("Max date = " + Math.max.apply(null, pubDates))
existingFeedData.lastUpdate = Math.max.apply(null, pubDates);
Storage.saveFeedData(existingFeedData);
},
getUrlForFeed: function($feed, feedSettings) {
if (feedSettings.linkType && $feed.find(feedSettings.linkType).length > 0) {
return $feed.find(feedSettings.linkType).text();
} else if ($feed.find('link').length == 1) {
return $feed.find('link').text();
} else {
if ($feed.find('link[rel="shorturl"]').length > 0) {
return $feed.find('link[rel="shorturl"]').attr('href');
} else if ($feed.find('link[rel="alternate"]').length > 0) {
return $feed.find('link[rel="alternate"]').attr('href');
} else if ($feed.find('link[rel="related"]').length > 0) {
return $feed.find('link[rel="related"]').attr('href');
} else {
return $feed.find('link').first();
}
}
},
saveFeed: function(existingFeedData, newFeedData) {
;
},
updateReadStatusOfNewItems: function(oldItems, newItems) {
if (typeof oldItems === 'undefined' || Object.keys(oldItems).length == 0 || Object.keys(newItems).length == 0) {
return newItems;
}
Object.keys(newItems).forEach(function(url) {
if (oldItems[url]) {
newItems[url].unread = oldItems[url].unread;
}
});
return newItems;
},
setNewBadge: function() {
chrome.browserAction.setBadgeText({
text: 'NEW'
});
}
};
}());
chrome.alarms.onAlarm.addListener(function(alarm) {
if (alarm.name == 'updateRssFeeds') {
console.log("Called!")
updateRssFeeds();
}
});
chrome.notifications.onClicked.addListener(function(notificationId) {
chrome.tabs.create({
url: notificationId
});
});
chrome.runtime.onInstalled.addListener(onInstall);
function onInstall() {
chrome.runtime.openOptionsPage();
chrome.alarms.create('updateRssFeeds', {
when: 0,
periodInMinutes: 1
});
}
function updateRssFeeds() {
Storage.getFeeds(function(feeds) {
feeds.forEach(function(feed) {
FeedService.downloadFeed(feed);
});
}, true, false)
}
I'm working on an app to retrieve data from a library catalog from a Dialogflow chat. I'm not getting any errors, and I have a billing account attached to the service. The intent code is here:
const {WebhookClient} = require('dialogflow-fulfillment');
const function = require('firebase-functions');
const agent = new WebhookClient({ request: request, response: response });
const catalogSearch = require("rss-to-json");
exports.libraryChat = functions.https.onRequest(request, response) => {
function catalog_search(agent) {
var itemSubject = agent.parameters["item-subject"] ? "+" + agent.parameters["item-subject"] : "";
var itemTitle = agent.parameters["item-title"] ? "+" + agent.parameters["item-title"] : "";
var chatResponse = "";
var itemList = new Array();
if (agent.parameters["author-name"]) {
var authorName = agent.parameters["author-name"]["name"] ? "+" + agent.parameters["author-name"]["name"] + " " + agent.parameters["author-name"]["last-name"] : "+" + agent.parameters["author-name"]["last-name"];
}
var searchString = "";
if (itemSubject.length > 0) { searchString = searchString + itemSubject; }
if (itemTitle.length > 0 ) { searchString = searchString + itemTitle; }
if (authorName.length > 0) { searchString = searchString + authorName; }
var url = "https://gapines.org/opac/extras/opensearch/1.1/-/rss2-full?searchTerms=site(GCHR-CCO)" + searchString + "&searchClass=keyword";
console.log(url);
catalogSearch.load(url, (err, jsonResponse) => {
if (!err) {
itemList = jsonResponse.items;
chatResponse = "The first ten items returned for your search are: ";
}
else {
chatResponse = "I'm sorry! I've encountered an error while retrieving that data!";
}
});
itemList.forEach( (title, index) => {
chatResponse = chatResponse + (index + 1).toString() + title.title;
});
agent.add(chatResponse);
}
let intentMap = new Map();
intentMap.set("Catalog Search", catalog_search);
}
The JSON response from the intent is:
{
"responseId": "958f0d66-13ba-4bf5-bed8-83480da4c37e",
"queryResult": {
"queryText": "Do you have any books about goats?",
"parameters": {
"item-subject": "goats",
"item-title": "",
"author-name": ""
},
"allRequiredParamsPresent": true,
"fulfillmentMessages": [
{
"text": {
"text": [
""
]
}
}
],
"intent": {
"name": "projects/library-chatbot/agent/intents/a5f8ad9b-ff73-49f7-a8c0-351da3bf4802",
"displayName": "Catalog Search"
},
"intentDetectionConfidence": 1,
"diagnosticInfo": {
"webhook_latency_ms": 3591
},
"languageCode": "en"
},
"webhookStatus": {
"message": "Webhook execution successful"
}
}
I've verified through a separate function that the Opensearch call works and produces a list of titles, however when accessed through the Dialogflow intent nothing comes back. I thought it might be due to the free account limitations, but the error continues after adding billing information and upgrading.
Or, rather, the lack of error with no response generated. Is there something I'm missing in here?
Your third line:
const agent = new WebhookClient({ request: request, response: response });
Is trying to instantiate the new agent, but request and response aren't defined in that scope. Try moving that line inside your libraryChat function body.
The issue: The DialogFlow agent was returning a response before the Opensearch results had been processed.
Solution: First, route the chat intents without using the built in .handleRequest method for the intent which makes a call to Opensearch, returning a promise.
function chooseHandler(agent) {
var intentRoute = "";
switch (agent.intent) {
case "Default Welcome Intent":
intentRoute = "welcome";
break;
case "Default Fallback Intent":
intentRoute = "fallback";
break;
case "Catalog Search":
intentRoute = "catalog";
break;
}
return new Promise ((resolve, reject) => {
if (intentRoute.length > 0) {
resolve(intentRoute);
}
else {
reject(Error("Route Not Found"));
}
});
}
function assignResponse(intentRoute, agent) {
switch (intentRoute) {
case "welcome":
agent.handleRequest(welcome);
break;
case "fallback":
agent.handleRequest(fallback);
break;
case "catalog":
catalog_response();
break;
default:
Error("Intent not found");
}
}
Next, retrieve the data from the OpenSearch API, returning a promise.
function catalog_search(agent) {
var itemSubject = agent.parameters["item-subject"] ? "+" + agent.parameters["item-subject"] : "";
var itemTitle = agent.parameters["item-title"] ? "+" + agent.parameters["item-title"] : "";
var itemType = agent.parameters["item-type"] ? "+" + agent.parameters["item-type"] : "";
var authorName = "";
if (agent.parameters["author-name"]) {
authorName = agent.parameters["author-name"]["name"] ? "+" + agent.parameters["author-name"]["name"] + " " + agent.parameters["author-name"]["last-name"] : "+" + agent.parameters["author-name"]["last-name"];
}
var searchString = "";
if (itemSubject.length > 0) { searchString += itemSubject; }
if (itemTitle.length > 0 ) { searchString += itemTitle; }
if (authorName.length > 0) { searchString += authorName; }
if (itemType.length > 0) { searchString += itemType; }
var url = "https://gapines.org/opac/extras/opensearch/1.1/-/rss2-full?searchTerms=site(GCHR-CCO)" + encodeURIComponent(searchString) + "&searchClass=keyword";
console.log(url);
console.log(encodeURIComponent(searchString));
var itemList = [];
var chatResponse = "this was untouched";
return new Promise((resolve, reject) => {
catalogSearch.load(url, (err, rss) => {
if (err) {
reject(Error("url failed!"));
}
else {
itemList = rss;
if (itemList.items.length > 0) {
if (itemList.items.length === 1) {
chatResponse = "The only item found for that search is ";
}
else if (itemList.items.length < 10) {
chatResponse = "The first few items in your search are ";
}
else {
chatResponse = "The first ten items in your search are ";
}
itemList.items.forEach( (item, index) => {
chatResponse = chatResponse + item.title.replace(/\//g, "").trim();
if (index + 1 < itemList.items.length) {
chatResponse += ", ";
} else {
chatResponse += ".";
}
});
}
else {
chatResponse = "We're sorry, there are no items matching that search at our library!";
}
console.log(rss);
resolve(chatResponse);
}
});
});
}
Create a properly-formatted JSON response to the intent, and send it back.
function catalog_response() {
catalog_search(agent).then((response) => {
res.json({ "fulfillmentText": response });
res.status(200).send();
return 1;
}), (error) => {
console.log("failed", error);
};
}
taking the example from http://www.flotcharts.org/flot/examples/stacking/ I am trying to implement the same but x-axis is time
https://jsfiddle.net/shorif2000/u6kvfjzc/
processData(json.rows.incidents.data, json.rows.incidents.tab);
function processData(rows, tab) {
p_start = new Date().getTime();
console.log("start process: " + p_start);
var arr = filterData(rows);
p_end = new Date().getTime();
console.log("finish process: " + p_end);
console.log("process duration : " + ((p_end - p_start) / 1000) + "s");
plot_graph(arr, tab);
}
function filterData(data) {
var arr = [];
for (i = 0; i < data.length; i++) {
var to_seconds = moment(data[i].TIMESTAMP, 'YYYY-MM-DD').unix() * 1000;
if (typeof arr[data[i].PRIORITY] == 'undefined' && !(arr[data[i].PRIORITY] instanceof Array)) {
arr[data[i].PRIORITY] = [];
}
arr[data[i].PRIORITY].push({
0: to_seconds,
1: parseInt(data[i].VALUE)
});
}
return arr;
}
function plot_graph(arr, id) {
var stack = 0,
bars = true,
lines = false,
steps = false;
var data = [{
stack: stack,
lines: {
show: lines,
fill: true,
steps: steps
},
bars: {
show: bars,
barWidth: 0.6
},
"points": {
"show": false
},
data: arr
}];
console.log(data);
$.plot("#" + id + "network-graph", data, {
series: {
stack: stack,
lines: {
show: lines,
fill: true,
steps: steps
},
bars: {
show: bars,
barWidth: 0.6
}
},
xaxis: {
mode: "time",
timeformat: "%y/%m/%d"
}
});
}
I managed to fix it. https://jsfiddle.net/shorif2000/epnv9wrw/ . I had to loop the array object and create labels for it
var datasets = [];
var i = 0;
for (var key in arr) {
datasets.push({label:key,data:arr[key],color:i});
++i;
}
I am trying to export rallygrid data in excel file, but getting only headers not values.
Below is my code which I wrote to generate grid and export button
From here [https://github.com/andreano/TaskDelta/blob/master/App.js], I stole the export code
prepareChart: function(iteration_data) {
this.converted_values = [];
this.accept_values = [];
this.commit_values = [];
parents = [];
rootParent = this.getContext().getProject().Name;
sortedArray = [];
var project_hash = {}; // project_by_name, with children
Ext.Array.each(iteration_data, function(iteration){
if ((iteration.ProjectName != rootParent && iteration.ChildCount > 0) || iteration.ParentName == rootParent) {
parents.push(iteration.ProjectName);
}
// make a place for me
if ( ! project_hash[iteration.ProjectName] ) { project_hash[iteration.ProjectName] = []; }
// make a place for my parent so it can know children
if ( iteration.ParentName ) {
if ( ! project_hash[iteration.ParentName]) { project_hash[iteration.ParentName] = []; }
project_hash[iteration.ParentName] = Ext.Array.merge( project_hash[iteration.ParentName], iteration.ProjectName);
}
}, this);
// build order this way:
//console.log("Current: ", this.getContext().getProject().Name );
// order the array by parents to children to grandchildren
sortedArray = this._getTreeArray( this.getContext().getProject().Name , project_hash);
parents = Ext.Array.unique(parents);
sortedData = [];
Ext.Array.each(sortedArray, function(name){
Ext.Array.each(iteration_data, function(ite){
if(ite.ProjectName == name) {
sortedData.push(ite);
};
});
});
Ext.Array.each(iteration_data, function(iteration){
if (iteration.ProjectName == rootParent) {
sortedData.push(iteration);
}
}, this);
iteration_data = sortedData;
sprints = [];
teams = [];
this.ratio = {};
for ( var i=0; i<iteration_data.length; i++ ) {
commit_accept_ratio = 0;
var data_point = iteration_data[i];
this.commit_values.push( data_point.Commit );
this.accept_values.push( data_point.Accept );
if ( data_point.Commit > data_point.Accept ) {
this.converted_values.push( data_point.Commit - data_point.Accept );
} else {
this.converted_values.push( 0 );
}
if (data_point.Commit != 0) {
commit_accept_ratio = (data_point.Accept / data_point.Commit ) * 100;
} else {
commit_accept_ratio = 0;
};
sprints.push(iteration_data[i].Name);
teams.push(iteration_data[i].ProjectName);
teams.push(rootParent);
this.ratio[data_point.ObjectID] = commit_accept_ratio;
}
this.sprints = Ext.Array.unique(sprints).sort();
this.teams = Ext.Array.unique(teams);
removable_teams = [];
for ( var i=0; i<this.teams.length; i++ ) {
team_name = null;
var count = 0;
Ext.Array.each(iteration_data, function(data) {
if (this.teams[i] == data.ProjectName && data.Commit == 0 || null || undefined && data.Accept == 0 || null || undefined) {
count += 1;
team_name = data.ProjectName;
}
}, this);
if (count == this.sprints.length) {
removable_teams.push(team_name);
}
}
removable_teams = Ext.Array.unique(removable_teams);
records = [];
recordHash = {};
summaryHash = {};
Ext.Array.each(iteration_data, function(iter) {
if (!recordHash[iter.ProjectName]) {
recordHash[iter.ProjectName] = {
Team: iter.ProjectName,
Name: '4 Sprint Summary',
Commit: [],
Accept: [],
Perc: [],
Summary: 0
};
}
if (!Ext.Array.contains(removable_teams, iter.ProjectName)) {
recordHash[iter.ProjectName]["Commit-" + iter.Name] = iter.Commit;
recordHash[iter.ProjectName]["Accept-" + iter.Name] = iter.Accept;
recordHash[iter.ProjectName]["Perc-" + iter.Name] = this.ratio[iter.ObjectID];
}
}, this);
var summaryArray = Ext.Array.slice( this.sprints, (this.sprints.length - 4))
var iterated_data = [];
Ext.Array.each(summaryArray, function(summ){
Ext.Array.each(iteration_data, function(team) {
if( summ == team.Name){
iterated_data.push(team);
}
});
});
Ext.Array.each(iteration_data, function(summ){
Ext.Array.each(iterated_data, function(team) {
if (!summaryHash[team.ProjectName]) {
summaryHash[team.ProjectName] = {
Commit: 0,
Accept: 0,
Total: 0
};
};
if (!Ext.Array.contains(removable_teams, team.ProjectName)) {
if( summ.ProjectName == team.ProjectName && summ.Name == team.Name) {
summaryHash[team.ProjectName]["Commit"] += summ.Commit;
summaryHash[team.ProjectName]["Accept"] += summ.Accept;
if (summaryHash[team.ProjectName]["Commit"] != 0) {
summaryHash[team.ProjectName]["Total"] = (summaryHash[team.ProjectName]["Accept"] / summaryHash[team.ProjectName]["Commit"] ) * 100;
} else {
summaryHash[team.ProjectName]["Total"] = 0;
};
};
}
});
}, this);
Ext.Object.each(recordHash, function(key, value) {
if (summaryHash[key]) {
value["Summary"] = summaryHash[key].Total;
records.push(value);
}
});
var cfgsValues = [];
cfgsValues.push({text: 'Team', style:"background-color: #D2EBC8", dataIndex: 'Team', width: 170, renderer: function(value, meta_data, record, row, col) {
if (Ext.Array.contains(parents, value)) {
meta_data.style = "background-color: #FFF09E";
return Ext.String.format("<div style='font-weight:bold;text-align:center'>{0}</div>", value);
} else if (rootParent == value){
meta_data.style = "background-color: #CC6699";
return Ext.String.format("<div style='font-weight:bold;text-align:center'>{0}</div>", value);
} else {
return value;
};
}});
cfgsValues.push({text: '4 Sprint Summary', style:"background-color: #D2EBC8", width: 70, dataIndex: 'Summary', renderer: function(value, meta_data, record) {
var color = null;
if (value >= 80 && value <= 120) {
color = "#00AF4F";
}
else if (value >= 60 && value <= 80) {
color = "#FBFE08";
}
else if (value <= 60) {
color = "#FC0002";
}
else if (value >= 120) {
color = "#98CCFB";
};
meta_data.style = "background-color: "+color+"";
return Ext.Number.toFixed(value, 0)+"%";
}});
Ext.Array.each(this.sprints, function(sprint) {
cfgsValues.push(
{text: sprint, style:'background-color:#D2EBC8;text-align:center;font-weight:bold', defaults: {enableColumnHide:false}, columns:[
{text: "Commit", dataIndex: 'Commit-' + sprint, width: 50, renderer: function(value, meta_data, record) {
if( value ) {
return value;
} else {
return "NA";
}
}},
{text: "Accept", dataIndex: 'Accept-' + sprint, width: 60, renderer: function(value, meta_data, record) {
if( value) {
return value;
} else {
return "NA";
}
}},
{text: "%", dataIndex: 'Perc-'+ sprint, width: 50, renderer: function(value, meta_data, record) {
var color = null;
if (value >= 80 && value <= 120) {
color = "#00AF4F";
}
else if (value >= 60 && value <= 80) {
color = "#FBFE08";
}
else if (value <= 60) {
color = "#FC0002";
}
else if (value >= 120) {
color = "#98CCFB";
}
meta_data.style = "background-color: "+color+"";
if (value) {
return Ext.Number.toFixed(value, 0)+"%";
} else {
return "NA";
};
}}
]}
);
});
var chart = Ext.getCmp('mychart');
if (chart) {
chart.destroy();
};
Ext.Array.each(this.sprints, function(sprint) {
Ext.Array.each(records, function(record) {
if (record["Accept-" + sprint] == undefined) {
record["Accept-" + sprint] = undefined;
}
if (record["Commit-" + sprint] == undefined) {
record["Commit-" + sprint] = undefined;
}
if (record["Perc-" + sprint] == undefined) {
record["Perc-" + sprint] = undefined;
}
});
});
this.add({
xtype: 'rallygrid',
id: 'mychart',
store: Ext.create('Rally.data.custom.Store', {
data: records,
pageSize: 100
}),
//viewConfig: {
//stripeRows: false
//},
columnCfgs: cfgsValues,
//columnLines: true
});
this.globalStore = Ext.getCmp('mychart');
console.log("this.globalStore", this.globalStore);
this.down('#grid_box').add(this.globalStore);
//this.setLoading(false);
},
_addPrintButton: function() {
var me = this;
this.down('#print_button_box').add( {
xtype: 'rallybutton',
itemId: 'print_button',
text: 'Export to Excel',
disabled: false,
margin: '20 10 10 0',
region: "right",
handler: function() {
me._onClickExport();
}
});
},
_onClickExport: function () { //using this function to export to csv
var that = this;
if (this.down('#grid_box')){
//Ext.getBody().mask('Exporting Tasks...');
//console.log('inside export');
setTimeout(function () {
var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-' +
'microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>' +
'<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>' +
'{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
'</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}' +
'</table></body></html>';
var base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)));
};
var format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
});
};
var table = that.getComponent('grid_box');
//console.log("Exporting table ",table);
var excel_data = '<tr>';
Ext.Array.each(table.getEl().dom.outerHTML.match(/<span .*?x-column-header-text.*?>.*?<\/span>/gm), function (column_header_span) {
excel_data += (column_header_span.replace(/span/g, 'td'));
});
excel_data += '</tr>';
Ext.Array.each(table.getEl().dom.outerHTML.match(/<tr class="x-grid-row.*?<\/tr>/gm), function (line) {
excel_data += line.replace(/[^\011\012\015\040-\177]/g, '>>');
});
//console.log("Excel data ",excel_data);
var ctx = {worksheet: name || 'Worksheet', table: excel_data};
window.location.href = 'data:application/vnd.ms-excel;base64,' + base64(format(template, ctx));
Ext.getBody().unmask();
}, 500);
}else{
console.log("grid_box does not exist");
}
}
There is an example in new AppSDK2 documentation of exporting to CSV.
I also have an example of exporting to CSV in this github repo.