I have researched and tried everything that I can think of to try and retrieve the actual values for the Iteration, Project, and User columns but I can never get the column data to populate for those like the name of the iteration, name of the project, and name of the submitted by user. I have read that it should be fine to do in the fetch the way I have it and others have said that you have to specify the types with something like this
types : ['defect','user','iteration','project'],
When I do that I dont ever load my grid. I have tried things like this as recommended by some
defect.Iteration.Name
OR
Iteration.Name
I could really use some help here. I also read one article saying the WSAPI no longer supports this kind of request and has to be handled in multiple queries/fetches. Anywho, here is the code that I am using...
function onLoad() {
var rallyDataSource = new rally.sdk.data.RallyDataSource(
'__WORKSPACE_OID__',
'__PROJECT_OID__',
'__PROJECT_SCOPING_UP__',
'__PROJECT_SCOPING_DOWN__');
var config = {
type : 'defect',
key : 'defects',
columnKeys : ["FormattedID", "Name", "Priority125", "Iteration", "Project", "SubmittedBy", "CreationDate", "ScheduleState", "State"],
fetch : 'FormattedID,Name,Priority125,Iteration,Project,SubmittedBy,CreationDate,ScheduleState,State',
query : '((State != "Closed") OR (ScheduleState != "Accepted"))',
order : 'Priority125'
};
var table = new rally.sdk.ui.Table(config, rallyDataSource);
table.display("tableDiv");
}
rally.addOnLoad(onLoad);
There are several things needed in order to get this to work as you're wanting:
You can fetch recursively up to a level of one deep. Thus if you want to grab a Defect's Name, Formatted ID, and the Project Name, your fetch would look like:
fetch: "Name,FormattedID,Project,Name"
Grab the data via rallyDataSource.findAll()
Post-process the data so that you feed your table all string data. I.e. clobber Object Reference fields like Project, with the Project Name instead.
Finally, populate and display the table.
Here's working example that illustrates what I think you're wanting to do (minus the "Priority 125" custom field that you have defined).
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Copyright (c) 2011 Rally Software Development Corp. All rights reserved -->
<html>
<head>
<title>Defect Information</title>
<meta name="Name" content="Defect Information" />
<meta name="Version" content="1.32" />
<meta name="Vendor" content="Rally Software" />
<script type="text/javascript" src="https://rally1.rallydev.com/apps/1.32/sdk.js?debug=True"></script>
<script type="text/javascript">
var rallyDataSource = null;
var table = null;
function showTable(results) {
if (table) {
table.destroy();
}
var tableConfig = {
columnKeys : ["FormattedID", "Name", "Iteration", "Project", "SubmittedBy", "CreationDate", "ScheduleState", "State"],
columnWidths : ["85px", "350px", "90px", "100px", "100px", "120px", "100px", "100px" ]
};
table = new rally.sdk.ui.Table(tableConfig);
// Loop through the rows and clobber object attributes of the results collection with
// string values
for(var i = 0; i < results.defects.length; i++){
thisDefect = results.defects[i];
var iterationName = "";
// Grab value fields
if (thisDefect.Iteration != null) {
iterationName = results.defects[i].Iteration.Name;
} else {
iterationName = "Un-scheduled";
}
var projectName = thisDefect.Project.Name;
// Re-map SubmittedBy object to SubmittedBy string
submittedByDisplayName = thisDefect.SubmittedBy === null ? "": thisDefect.SubmittedBy._refObjectName;
// Clober objects with values
results.defects[i].Iteration = iterationName;
results.defects[i].Project = projectName;
results.defects[i].SubmittedBy = submittedByDisplayName;
}
table.addRows(results.defects);
table.display(document.getElementById('defectsDiv'));
}
function onLoad() {
rallyDataSource = new rally.sdk.data.RallyDataSource(
'__WORKSPACE_OID__',
'__PROJECT_OID__',
'__PROJECT_SCOPING_UP__',
'__PROJECT_SCOPING_DOWN__');
var config = {
type : 'defect',
key : 'defects',
fetch: 'FormattedID,Name,SubmittedBy,Iteration,Name,Project,Name,CreationDate,ScheduleState,State',
query : '((State != "Closed") OR (ScheduleState != "Accepted"))',
};
rallyDataSource.findAll(config, showTable);
rallyDataSource.setApiVersion("1.38");
}
rally.addOnLoad(onLoad);
</script>
</head>
<body>
<div id="aDiv"></div>
<div style="font-weight: bold;"><p>Defects</p></div>
<div id="defectsDiv"></div>
</body>
</html>
Related
I am pulling data via a CRM API and successfully rendering that data in the front end of my Google Script web app. But manipulating or formatting this data for the front end is a challenge for me.
In the code below, the Potential Name on the second line is rendering the correct data to the page. But the first line called Quote is showing undefined. This data is the data I am trying to format so that only the last six characters or the string are printed to the page.
Clearly, I must be trying to access the data from the API incorrectly. Could someone please provide me with the correct way to manipulate this data in Google Scripts?
Code.gs
function doGet() {
var templ = HtmlService.createTemplateFromFile('Allied-po');
templ.data = requestRecordFromCRM();
return templ.evaluate()
.setTitle('Purchase Order')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
/*Fetch record data from CRM*/
function requestRecordFromCRM() {
requestedId = '1234';
var authToken = 'XXXX';
var zohoRequestUrl = 'https://crm.zoho.com/crm/private/json/Potentials/getRecordById?&authtoken=' + authToken + '&scope=crmapi&id=' + requestedId;
var response = UrlFetchApp.fetch(zohoRequestUrl);
var sanitizedResponse = (response.getContentText());
/*Sanitize json*/
var output = JSON.parse(sanitizedResponse);
Logger.log(output);
/*Declare the variables you want to print*/
var parsedOutput = output.response.result.Potentials.row.FL;
var recordObj = {}
Logger.log(typeof output)
Logger.log(output.response.result.Potentials.row.FL.length)
for (var i = 0; i < output.response.result.Potentials.row.FL.length; i++) {
if (output.response.result.Potentials.row.FL[i].val == 'Potential Name') {
recordObj.potentialName = output.response.result.Potentials.row.FL[i].content
}
}
return (recordObj);
}
Index.html
<html>
<head>
<base target="_blank">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Purchase Order</title>
<?!= HtmlService.createHtmlOutputFromFile('Stylesheet').getContent(); ?>
</head>
<body>
<div>
Quote: <span id="job-number"><?= data.potentialName ?></span>
</div>
<div>
Potential Name: <?= data.potentialName ?>
</div>
<?!= HtmlService.createHtmlOutputFromFile('Javascript').getContent(); ?>
</body>
</html>
Javascript.html
<!-- Load jQuery, jQuery UI, and Bootstrap libraries -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script>
//Format Job Numbers - return only last six characters in potentialName string
(function() {
var parts = document.getElementById('job-number');
var selectedPart = parts.split(":");
var thePart = selectedPart[0];
return (thePart);
}());
</script>
This particular code retrieves the HTML element but not the innerHTML text
var parts = document.getElementById('job-number');
Instead do this to get the embedded HTML which can used to split the string like so:
var parts = document.getElementById('job-number').innerHTML;
The final code will look like this:
<script>
//Format Job Numbers - return only last six characters in potentialName string
(function() {
var parts = document.getElementById('job-number');
var selectedPart = parts.innerHTML.split(":");
console.log(parts)
console.log(selectedPart)
var thePart = selectedPart[1];
parts.innerHTML = thePart
return (thePart);
}());
</script>
There is limited information on how the object structure looks like? The assumption here is there is six character before ":" in the data.potentialName value. if you rather want exactly six characters you can do this:
var selectedPart = parts.subString(0,6)
Hope that helps!
I implemented the map on web page using JavaScript API, and now I want to show the basic information about some location. In JavaScript API documentation, I found a part of which is called "Basic place display" in Places Components section, but there is an example of how to render information using placeId.
I need to be able to retrieve information using location coordinates if it is possible. I tried to display information using PHP code that define coordinates for some location on the map instead of using placeId, but it's not working.
This is an example of code that I used:
var basicPlace = new nokia.places.widgets.Place({
placeId: PHP code instead of placeId.
*exp: [<?php code;?>, <?php echo code;?>],*
targetNode: "map",
template: "nokia.blue.place"
});
Is it possible to solve the problem like that, or there is a method that does not involve placeId.
Links: Here Developer, Here JavaScript API
If you read the nokia.places.widgets.Place documentation, you will see that placeId is a mandatory parameter. It is in effect the primary key for the place information that is held by HERE. You will therefore need to make another request using the JavaScript API prior to display in order to obtain the placeId so you can show your place details. The obvious thing to do here is to make a category request first, and store the placeId with each marker as shown below:
// Function for receiving search results from places search and process them
var processResults = function (data, requestStatus, requestId) {
var i, len, locations, marker;
if (requestStatus == "OK") {
locations = data.results ? data.results.items : [data.location];
if (locations.length > 0) {
for (i = 0, len = locations.length; i < len; i++) {
// Add a marker and store the placeId
marker = new nokia.maps.map.StandardMarker(locations[i].position,
{ text: i+1 ,
placeId : locations[i].placeId});
resultSet.objects.add(marker);
}
}
});
// etc.. etc...
The second part is to add the click listener which displays an infobubble and populates the Place Widget using the stored placeId:
resultSet.addListener("click" , function(evt) {
infoBubbles.openBubble("<div id='basicPlaceContainer'></div>",
evt.target.coordinate);
var basicPlace = new nokia.places.widgets.Place({
placeId: evt.target.placeId,
targetNode: "basicPlaceContainer",
template: "nokia.blue.bubble"
});
}, false);
The complete working example can be seen below:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9; IE=10" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Nokia Maps API for JavaScript Example: Search by category</title>
<meta name="description" content="Search by category"/>
<script type="text/javascript" charset="UTF-8" src="http://js.cit.api.here.com/se/2.5.3/jsl.js?with=all"></script>
</head>
<body>
<div id="mapContainer" style="width:540px; height:334px;"></div>
<script type="text/javascript" id="exampleJsSource">
/* Setup authentication app_id and app_code
*/
nokia.Settings.set("app_id", "YOUR APP ID");
nokia.Settings.set("app_code", "YOUR APP CODE");
// Use staging environment (remove the line for production environment)
nokia.Settings.set("serviceMode", "cit");
// Get the DOM node to which we will append the map
var mapContainer = document.getElementById("mapContainer");
// Create a map inside the map container DOM node
var map = new nokia.maps.map.Display(mapContainer, {
// Initial center and zoom level of the map
center: [52.51, 13.4],
zoomLevel: 10,
components: [
new nokia.maps.map.component.Behavior()
]
});
this.infoBubbles = new nokia.maps.map.component.InfoBubbles();
map.components.add(infoBubbles);
var searchCenter = new nokia.maps.geo.Coordinate(52.51, 13.4),
searchManager = nokia.places.search.manager,
resultSet;
// Function for receiving search results from places search and process them
var processResults = function (data, requestStatus, requestId) {
var i, len, locations, marker;
if (requestStatus == "OK") {
locations = data.results ? data.results.items : [data.location];
if (locations.length > 0) {
if (resultSet) map.objects.remove(resultSet);
resultSet = new nokia.maps.map.Container();
resultSet.addListener("click" , function(evt) {
infoBubbles.openBubble("<div id='basicPlaceContainer'></div>", evt.target.coordinate);
var basicPlace = new nokia.places.widgets.Place({
placeId: evt.target.placeId,
targetNode: "basicPlaceContainer",
template: "nokia.blue.bubble"
});
}, false);
for (i = 0, len = locations.length; i < len; i++) {
marker = new nokia.maps.map.StandardMarker(locations[i].position,
{ text: i+1 ,
placeId : locations[i].placeId});
resultSet.objects.add(marker);
}
map.objects.add(resultSet);
map.zoomTo(resultSet.getBoundingBox(), false);
} else {
alert("Your search produced no results!");
}
} else {
alert("The search request failed");
}
};
// Make a place search request
var category = "eat-drink";
map.addListener("displayready", function () {
searchManager.findPlacesByCategory({
category: category,
onComplete: processResults,
searchCenter: searchCenter
});
});
</script>
</body>
</html>
The result can be see below:
I am writing a page with multiple jqGrids. My code follows a JavaScript MVC pattern which is going to provide an API for my HTML elements (including jqGrids). So, in the end of the day, I can create grids by calling my API. Something like:
var grid1 = new grid();
grid1.init();
var grid2 = new grid();
grid2.init();
I have done it with other javascript components and it worked great. However, when I create multiple jqGrid instances on the same page there is only one jqPager on the page attached to the last grid. Does anybody have an idea why?
Here is my code (Note that this is a simplified version, in reality I keep it separated in different .js files and also follow many other design patterns):
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/themes/redmond/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="http://www.ok-soft-gmbh.com/jqGrid/jquery.jqGrid-4.1.2/css/ui.jqgrid.css" />
</head><body>
<!-- IMPORT JS -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://www.ok-soft-gmbh.com/jqGrid/jquery.jqGrid-4.1.2/js/i18n/grid.locale-en.js"></script>
<script type="text/javascript" src="http://www.ok-soft-gmbh.com/jqGrid/jquery.jqGrid-4.1.2/js/jquery.jqGrid.min.js"></script>
<script>
$(document).ready(function() {
function grid() {
//=== LOCA VARIABLES ===//
var myGrid = $('<table>').attr( "id", "useraccount-search-datagrid");
var myPager = $("<div>").attr("id", "useraccount-search-datagrid-pager");
var localData1 = {
"page" : 1,
"totalRecords" : 5,
"pageSize" : 3,
"rows" : [
{ Name : "Name 1"},
{ Name : "Name 3"},
{ Name : "Name 2"}
]
};
function publicInit(){
$("body").append(myGrid, myPager);
myGrid.jqGrid({
pager : myPager,
data: localData1.rows,
datatype : "local",
colModel : [
{ name : 'Name', index : 'Name', width : "500"}
],
localReader: {
repeatitems: false
},
rowNum : 3,
viewrecords : true,
height : "auto",
ignoreCase : true
});
}
//=== REVEALING PATTERN===//
return {
init: publicInit
}
};
var grid1 = new grid();
grid1.init();
$("body").append("<br><br>"); //Add some spacing to distinguish between both grids
var grid2 = new grid();
grid2.init();
});
</script>
</body>
</html>
Any help would be highly appreciated.
It seems to me that your code produce <table> and <div> elements with the same id attributes. So the second grid var grid2 = new grid(); just add <table> and <div> elements which already exist on the page. It's a bug. All id attributes of all element on one HTML page must be unique. So the lines myGrid = $('<table>').attr( "id", "useraccount-search-datagrid"); and var myPager = $("<div>").attr("id", "useraccount-search-datagrid-pager"); must be changed.
If you need just to assign some unique ids you can use $.jgrid.randId() method used in jqGrid internally. The code could be
var myGrid = $("<table>").attr("id", $.jgrid.randId());
var myPager = $("<div>").attr("id", $.jgrid.randId());
Moreover I strictly recommend you to use name conversion used in JavaScript. If you need to use new operator to create an object you should rename the function grid to Grid.
For each story in a Rally project I would like to be able to get a count of the number of times, in the revision history, that a change has been made to either the DESCRIPTION or ACCEPTANCE CRITERIA fields. We're trying to get a quantitative estimate of the amount of requirements churn in our projects.
Here's a quick example that a colleague wrote - it parses the Revision History collection, matching on "DESCRIPTION changed" and adds the results to a table for display. It should be easy to modify in such a way to display counts instead of Revision summaries, and to match on your Acceptance Criteria field as well. You'd probably want to add an IterationDropdown and other niceties too.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Copyright (c) 2012 Rally Software Development Corp. All rights reserved -->
<html>
<head>
<title>Revision History Example</title>
<meta name="Name" content="App Example: Revision History" />
<meta name="Version" content="2012.2" />
<meta name="Vendor" content="Rally Labs" />
<script type="text/javascript" src="https://rally1.rallydev.com/apps/1.30/sdk.js?showHeader=false"></script>
<script type="text/javascript">
function revHistoryExample() {
var rallyDataSource = new rally.sdk.data.RallyDataSource(
'__WORKSPACE_OID__',
'__PROJECT_OID__',
'__PROJECT_SCOPING_UP__',
'__PROJECT_SCOPING_DOWN__');
function itemQuery() {
var queryObject = {
key: 'stories',
type: 'HierarchicalRequirement',
fetch: 'Name,ObjectID,FormattedID,RevisionHistory,Revisions,RevisionDescription,CreationDate,LastUpdateDate,Iteration'
query: '(Iteration.Name = "My Iteration")',
};
rallyDataSource.findAll(queryObject, populateTable);
}
function populateTable(results) {
var tableDiv = document.getElementById('aDiv');
var config = { columns:
[{key: 'FormattedIDLink', header: 'Formatted ID', width: 100},
{key: 'Name'},
{key: 'RevisionDescription', header: 'Revision Description'},
{key: 'CreationDate', header: 'Creation Date'}] };
var table = new rally.sdk.ui.Table(config);
// Revision condition to match/look for within Revision history
var revCondition = "DESCRIPTION changed";
for ( i=0 ; i<results.stories.length ; i++ ) {
for ( j=0 ; j<results.stories[i].RevisionHistory.Revisions.length ; j++ ) {
myStory = results.stories[i];
myRevision = myStory.RevisionHistory.Revisions[j];
if (myRevision.Description.indexOf(revCondition)>=0){
myFormattedID = myStory.FormattedID;
myDescription = myRevision.Description;
myCreationDate = myStory.CreationDate;
myFormattedIDLink = new rally.sdk.ui.basic.Link(
{item: myStory, text: results.stories[i].FormattedID}
)
// Clobber fields on Story with info from the matching Revision for easy inclusion in table
results.stories[i].CreationDate = myCreationDate;
results.stories[i].RevisionDescription = myDescription;
results.stories[i].FormattedIDLink = new rally.sdk.ui.basic.Link({item:results.stories[i], text: results.stories[i].FormattedID});
table.addRow(results.stories[i]);
}
}
}
table.display(tableDiv);
};
itemQuery();
}
rally.addOnLoad(revHistoryExample);
</script>
</head>
<body>
<div id="aDiv"></div>
</body>
</html>
I am in the process of trying to create a custom HTML app in Rally to have a User Story created through the Add New component and drawing on data from an external source using JQuery with YQL JSON query.
This process works a wonder for pre-filled name, description and notes. However, the only way at the moment Owner works pre-filled is using an Object Dropdown to manually select the user. I want to be able to pull the Owner from the same external source.
Presently this is dummy code to test the theory:
var statement = "select * from html where url='" + value + "' and xpath='//h1'";
document.getElementById('statement').innerHTML = statement;
$.queryYQL(statement, "json", function (data) {
var item = data.query.results.h1;
for(var i=0;i<item.length;i++){
title = item[i].content;
style = item[i].style;
output += "<h3>"+title +"<br />"+ style +"</h3>";
}
document.getElementById('results').innerHTML = output;
});
function onAddNewPreCreate(addNew, eventArgs) {
eventArgs.item["Name"] = title;
eventArgs.item["Description"] = output;
eventArgs.item["Notes"] = style;
eventArgs.item["Owner"] = user;
}
The 'value' variable is set via a textbox where the user can input the URL of where they want to grab the external data from.
The user variable is set via the dropdown. I have tried replacing the user variable with the exact same display name as the user in Rally such as eventArgs.item["Owner"] = "User Name"; but this results in a blank for the Owner of the User Story when created.
Any ideas on how this may be achieved?
Since Users are objects in Rally, you cannot set an Artifact's Owner attribute to be the string of a Rally username, rather, you must set the Owner to be a Reference to a valid Rally User, in the form of a ref: /user/12345678910 where the long integer is the Object ID of the User of interest. You can use the rallyDataSource and AppSDK to do a query on the string value of the Rally User Name, and obtain the needed ref.
I've included a basic example that illustrates the idea of taking an e-mail formatted User Name, and querying Rally for the reference to the needed User object. The example uses a simple DOM <select> component to simulate an "external" source of username data (in your case populated via JQuery with YQL JSON). The examples does this in lieu of the AppSDK ObjectDropdown, which although the simplest way to accomplish this, as you note doesn't suit your needs since you are polling an external source as the source of your user-selector.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- Copyright (c) 2011 Rally Software Development Corp. All rights reserved -->
<html>
<head>
<title>Component Example: Add New User Story, "External" User Chooser</title>
<meta name="Name" content="Component Example: Add New User Story, 'External' User Chooser" />
<meta name="Version" content="2012.1" />
<meta name="Vendor" content="Rally Labs" />
<script type="text/javascript" src="https://rally1.rallydev.com/apps/1.26/sdk.js"></script>
<script type="text/javascript">
// Global variables to store Reference to selected User
var userRef = null;
var rallyDataSource;
var getUserRef = function(results) {
var user = results.users[0];
userRef = user._ref;
};
function userSelectChanged() {
var userChooser = document.getElementById("userChooser");
var strUser = userChooser.options[userChooser.selectedIndex].value;
queryString = '(UserName = "' + strUser + '")';
var userQueryConfig = {
type : 'user',
key : 'users',
fetch: 'UserName,DisplayName,Role',
query: queryString
};
rallyDataSource.findAll(userQueryConfig, getUserRef);
}
function onAddNewAdd(addNew, eventArgs){
var createdItem = eventArgs.item;
var createdFormattedId = createdItem.FormattedID;
alert("Created new User Story with Formatted ID: " + createdFormattedId);
}
function onAddNewPreCreate(addNew, eventArgs)
{
// Grab Rally ref of User selected in HTML dropdown
// set it as Owner attribute of created User Story
eventArgs.item["Owner"] = userRef;
}
function onLoad() {
rallyDataSource = new rally.sdk.data.RallyDataSource('__WORKSPACE_OID__',
'__PROJECT_OID__',
'__PROJECT_SCOPING_UP__',
'__PROJECT_SCOPING_DOWN__');
var addNewConfig = {
types : ["HierarchicalRequirement"]
};
// Populate userRef from Rally for default selected user:
userSelectChanged();
var addNew = new rally.sdk.ui.AddNewArtifact(addNewConfig, rallyDataSource);
addNew.addEventListener("onAdd",onAddNewAdd);
addNew.addEventListener('onPreCreate', onAddNewPreCreate);
addNew.display("addNewDiv");
}
rally.addOnLoad(onLoad);
</script>
</head>
<body>
<div id="addNewDiv"></div>
<div id="userChooserDiv">
Select a Rally User as Story Owner:
<select id="userChooser" onChange="userSelectChanged()">
<option selected value="user1#company.com">user1#company.com</option>
<option value="user2#company.com">user2#company.com</option>
<option value="user3#company.com">user3#company.com</option>
</select>
</body>
</html>