Grails - Cannot invoke method getAt() on null object - exception

I am getting an error when trying to append a book to a list of classes list of books. Any ideas as to why or what I could do to fix it?
//For each book in a class...
for (int k = 0; k < rows.size(); k++) {
Book book = new Book()
//Assign the values to a new book object
book.id = rows[k].getProperty("ISBN")
book.title = rows[k].getProperty("title")
book.author = rows[k].getProperty("author")
book.required = rows[k].getProperty("required_optional")
book.purchaseType = rows[k].getProperty("rental_purchase")
//book.purchasePrice = rows[k].getProperty("purchase_price")
//book.rentalPrice = rows[k].getProperty("rental_fee")
//Append the book to the books list object in the particular class
classes[i].books[k + 1] << book
}

Instead of accessing by [] use getAt, then the ? Operator will work:
classes?.getAt(i)?.books?.getAt(k+1) << book
or
classes?.getAt(i)?.books[k+1] << book

rows.each { row ->
Book book = new Book(...)
classes[i].books << book
}

without the full code it's hard to know what happens. try this code, it will show you which value is null:
// first check if classes[i] is null, add error handling if it is
if (classes[i]!=null) {
//For each book in a class...
for (int k = 0; k < rows.size(); k++) {
def books = classes[i].books[k + 1]
if (books!=null) {
// you can set values directly here
def row = rows[k]
Book book = new Book(
id: k.getProperty("ISBN"),
title: k.getProperty("title")
// ...
)
//Append the book to the books list object in the particular class
books << book
} else {
println "books is null, do something clever"
}
}
} else {
println "classes[i] is null, do something clever"
}
I didn't test it, and you can probably make it shorter with some groovy goodness. this is just to get you started

I am not sure what 'classes' is, but if its a list containing 'Book' you can just do
classes << book
OR
classes.add(book)

Related

resource for overall weighted mark or assignment category can't find

I have looked in the various resources for coursework and cannot find the specific categories. The workType will just return that it is an assignment, but no category that I have set up for that assignment to assign grades into.
Also I have tried to check to see if there is a stored value for the overall weighted grade for each student, but cannot find this entry either. This may be autogenerated based on category/grade calculations and put into each slot on page loadup.
Any help with these would be greatly appreciated.
I have found this Issue Tracker post, there is no direct function to retrieve the average grade for each student. As you can read in the Google's reply, this can be done by programming with the API.
In this case, it consists in iterating over the courseWork.studentSubmissions. The response returns, beside others, the studentSubmissions resource which is grouped into each assignement and their respective submissionHistory.
You have to get each assignedGrade and do the average for each student. For example:
var courseWorks = Classroom.Courses.CourseWork.list(courseId);
var courseWorkIds = [], list_Grades = [];
var list_Students = Classroom.Courses.Students.list(courseId).students;
var list_Topics = Classroom.Courses.Topics.list(courseId).topic;
for (var i = 0; i < courseWorks.courseWork.length; i++){
courseWorkIds.push(courseWorks.courseWork[i].id);
var topicId = courseWorks.courseWork[i].topicId;
getSubmissions(courseId, courseWorks.courseWork[i].id, list_Grades, topicId);
}
getAverage(list_Grades, list_Students, list_Topics);
}
function getAverage(list_Grades, list_Students, list_Topics){
for (var r = 0; r < list_Students.length; r++){
Logger.log("===================================");
Logger.log("Student: " + list_Students[r].userId);
Logger.log("===================================");
for (var s = 0; s < list_Topics.length; s++){
var total = 0;
var counter = 0;
Logger.log("Topic: " + list_Topics[s].name);
for (var t = 0; t < list_Grades.length; t++){
if (list_Topics[s].topicId == list_Grades[t].topicId && list_Students[r].userId == list_Grades[t].studentId){
total += list_Grades[t].grade;
counter++;
}
}
Logger.log("Average: " + (total / counter));
}
}
}
function getSubmissions(courseId, courseWorkId, list_Grades, topicId){
var list_submissions = Classroom.Courses.CourseWork.StudentSubmissions.list(courseId, courseWorkId).studentSubmissions;
for (var j = 0; j < list_submissions.length; j++){
var subs = Classroom.Courses.CourseWork.StudentSubmissions.get(courseId, courseWorkId, list_submissions[j].id);
list_Grades.push(new Grades(topicId, subs.assignedGrade, subs.userId));
}
}
function Grades(topicId, grade, studentId){
this.topicId = topicId;
this.grade = grade;
this.studentId = studentId;
}
With the Id of the student you can get the rest of the information, like the name or the email address.
You will also need to authorize the scopes: https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.topics.readonly
https://www.googleapis.com/auth/classroom.rosters
References:
Courses
CourseWork
StudentSubmissions
Note: This generates the Average grade for each Student and Topic.

JSON contains array of nulls with three objects - expected only three objects

Using a standalone Google Apps Script and a Google Spreadsheet. I have this script which returns as JSON an array of nulls and three objects, but I expected only to get three objects. Its a search, and when a zipcode is searched, the script is to return any matches. The thing is, it returns the matches successfully, but it also returns a null for each row that was not a match, in the order the rows appear on the google sheet. To make it work, the function testDoGetWithZipcode() should be run.
I don't know if I'm supposed to get those nulls, if they matter, or how I can fix it. It doesn't seem to go with anything I've learned about JSON so far but before even asking this I did an hour and a half Lynda.com course on Javascript and JSON and read the JSON.org website and read the documentation on Mozilla about JSON. I've adjusted variables in all of the functions because at first I thought it was in the function formatOrganization() but now I'm completely stumped.
s = SpreadsheetApp.openById("1280aUAvFoUDP2rtpCFS2JYR7TuQNYcd5gm8QudukiGc");
var sheet = s.getSheetByName("RAP - Data");
var data = sheet.getDataRange().getValues();
var headings = data[0];
function zipcodeQuery(zipcode) {
zipcodeArray = [];
for (var i = 1; i < data.length; i++){
if (zipcode === data[i][4].toString()){
zipcodeArray.push(data[i]);
}
}
return zipcodeArray
}
function formatOrganization(rowData){
var organization = {}
for (var i = 0; i < headings.length; i++){
Logger.log('Headings: ' + headings[i]);
organization[headings[i].toString()] = rowData[i];
}
return organization
}
function executeZipcodeQuery(request) {
zipcodes = request.parameters.zipcode;
// The object to be returned as JSON
response = {
organizations : []
}
// Fill the organzations dictionary with requested organizations
for (var i = 0; i < zipcodes.length; i++) {
sheetData = zipcodeQuery(zipcodes[i]);
if(sheetData !== undefined) {
for (var orgIndex = 0; orgIndex < sheetData.length; orgIndex++) {
var org = formatOrganization(sheetData[orgIndex]);
if(org !== undefined) {
Logger.log('Org object: ' + org);
if(typeof org === 'object') {
//FIXME
var orgId = parseInt(org.Id);
Logger.log('Org Id: ' + orgId);
response.organizations[orgId] = org
//response.organizations.push({orgId : org});
}
}
}
}
}
if (response.organizations.length > 0)
{
return ContentService.createTextOutput(JSON.stringify(response.organizations));
}
else
{
return ContentService.createTextOutput('Invalid Request. zipcode(s) not found.');
}
}
function testDoGetWithZipcode() {
var testRequest = {"parameter":{"zipcode":"19132"},"contextPath":"","contentLength":-1,"queryString":"zipcode=19132","parameters":{"zipcode":["19132"]}};
var textResult = doGet(testRequest);
textResult.setMimeType(ContentService.MimeType.JSON);
Logger.log('Mime Type: ' + textResult.getMimeType());
Logger.log('Result content: ' + textResult.getContent());
}
The return I get is this (abridged because there's over a 180 rows in the spreadsheet and they're all represented in the return by either null or an object):
[
null,
....
null,
{
"Id":61,
"Category":"Day / Drop in Centers",
"Organization Name":"Philadelphia Recovery Community Center (PRCC)",
"Address":"1701 W Lehigh Ave, Philadelphia, PA 19132",
"Zip Code":19132,
"Days":"Mon, Tues, Thurs, Fri: 12-8pm, Wed: 9-5pm, Sat: 9-1pm",
"Time: Open":"",
"Time: Close":"",
"People Served":"Women, Men, Families",
"Description":"Case management, outpatient treatment, youth programs, training programs",
"Phone Number":"215-223-7700"
},
....
null,
{
"Id":81,
"Category":"Emergency Shelter",
"Organization Name":"Station House",
"Address":"2601 N Broad St, Philadelphia, PA 19132",
"Zip Code":19132,
"Days":"",
"Time: Open":"",
"Time: Close":"",
"People Served":"Men",
"Description":"After hours reception for single men\n 2601 N. Broad Street\n After 4 pm",
"Phone Number":"215-225-9230"
},
null,
...
]
Your original object is this:
response = {
organizations : []
}
The value of the key/value pair for organizations is an array. But you are using notation as if organizations was an object.
response.organizations[orgId] = org
You could push a value into the array with:
response.organizations.push(org);
I'd probably try something like this:
var tempObject = {}; //Reset every time
tempObject[orgId] = org;
response.organizations.push(tempObject);

Error msg on linq to dictionary-"an item of the same key value has already been added"

I'm trying do make a collection using linq based on ID which is a GUID.On using dictionary I'm getting error "An item with same key has already been added" Any suggestion?
foreach (Guid i in ar)
{
var prod = (from r in datacontext.item_tables's where r.itemID == i select r);
Dictionary<Guid, item_tables> tempdata =prod.ToDictionary(s => s.itemID);
Facet[] ftemp = new Facet[tempdata.Count];
string s1 = "";
ftemp[0] = new Facet("descriptiob", FacetType.Text, tempdata[i].Description);
ftemp[1] = new Facet("date", FacetType.Text, tempdata[i].uploaddate);
for (int iv = 0; iv < tempdata.Count; iv++)
{
s1 += tempdata[i].ProductName + " \n";
}
ftemp[2] = new Facet("ProductName", FacetType.Text, s1);
}
You're basically building your dictionary using this code:
datacontext
.item_tables
.Where(r => r.itemID == i)
.ToDictionary(s => s.itemID);
The Where is filtering the results to where itemID is a particular i (Guid) at a time, but can return zero, one, or more, results. Clearly the error you are getting says that for at least one value of i you are getting more than one record returned.
This means the issue either that your database isn't properly normalized or that you've got a logic error in your code.
It sounds like the latter to me.
Further down in your code you have this loop:
for (int iv = 0; iv < tempdata.Count; iv++)
{
s1 += tempdata[i].ProductName + " \n";
}
This says to me that you're expecting the tempdata dictionary to have more than one value - but you're building the dictionary using itemID as the key and this should only put one value in the dictionary based on your query. So something is wrong here in your logic.
Can you describe in more detail what you're trying to do?
Based on your comment below (without the clarification from my comment) this appears to be what you want:
var query =
from r in datacontext.item_tables
group r.ProductName by new
{
r.itemID,
r.Description,
r.uploaddate,
r.ItemName,
};
foreach (var items in query.ToArray())
{
var f0 = new Facet("descriptiob", FacetType.Text, items.Key.Description);
var f1 = new Facet("date", FacetType.Text, items.Key.uploaddate);
var f2 = new Facet("ProductName", FacetType.Text, String.Join("\n", items));
collection.AddItem(items.Key.ItemName, null, null, f0, f1, f2);
}

HTML5 SQLite Db questions

I've got a couple of questions regarding the Sqlite implementations for HTML5 website.
First of all, I'm trying to use the Synchronous Database calling openDatabaseSync method, but it doesn't seem to work... Someone used it already and could help me ?
Also, I'm struggling a bit trying to process the result return by my database query. I'd like my function to return an array of book, like this :
function searchByKeywordId(kw_id, element) {
cleanSearch();
element.innerHTML = "No result...";
var books = new Array();
db.transaction(function (tx) {
tx.executeSql("SELECT b.BK_TITLE,b.BK_URL, b.BK_THUMBNAIL_URL FROM KEYWORDS k INNER JOIN CATALOG_ITEMS c on k.KW_ID = c.KW_ID INNER JOIN BOOKS b on c.BK_ID = b.BK_ID WHERE k.KW_ID = ? GROUP BY b.BK_TITLE,b.BK_URL",[kw_id], function (tx, results) {
if (results.rows.length > 0) {
var html = "";
for (var i = 0; i < results.rows.length; i++) {
var bookId = results.rows.item(i).BK_ID;
var bookUrl = results.rows.item(i).BK_URL;
var bookTitle = results.rows.item(i).BK_TITLE;
var bookThumbnailUrl = results.rows.item(i).BK_THUMBNAIL_URL;
var book = new Book(bookId,bookTitle,bookUrl,bookThumbnailUrl);
books.push(book);
/*html += "<div class='x_container' id='calibre:book:" + bookId + "'>";
html += "<div class='cover'>";
html += "</div></div>";*/
html += "<a href='" + bookUrl + "' title=\"" + bookTitle + "\" target='_new'><img src='" + bookThumbnailUrl + "'></a> ";
}
//html += "</div>";
element.innerHTML = html;
}
});
});
return books; }
obviously, adding books within the callback methods doesn't work ... Do you see a way I could achieve that ? So that I would not have to write in the document from my database methods ...
Thanks !
On stackoverflow a question with javascript and "doesn't work" in it is usually a missing paren :) However I didn't find one in your code. I see some suspicious looking syntax around
,[kw_id], << did we really mean an array here, or are we de-referencing something...
In any case if that's not a mistake I would start by simplifying things, and not multipurposing your functions.
function searchByKeywordId(kw_id, element) {
cleanSearch();
var books = new Array();
db.transaction(function (tx) {
tx.executeSql("SELECT b.BK_TITLE,b.BK_URL, b.BK_THUMBNAIL_URL FROM KEYWORDS k INNER JOIN CATALOG_ITEMS c on k.KW_ID = c.KW_ID INNER JOIN BOOKS b on c.BK_ID = b.BK_ID WHERE k.KW_ID = ? GROUP BY b.BK_TITLE,b.BK_URL",[kw_id], function (tx, results) {
if (results.rows.length > 0) {
var html = "";
for (var i = 0; i < results.rows.length; i++) {
var bookId = results.rows.item(i).BK_ID;
var bookUrl = results.rows.item(i).BK_URL;
var bookTitle = results.rows.item(i).BK_TITLE;
var bookThumbnailUrl = results.rows.item(i).BK_THUMBNAIL_URL;
var book = new Book(bookId,bookTitle,bookUrl,bookThumbnailUrl);
books.push(book);
} // end for loop
} // end if block
} // end execute callback
); // end executeSql call
} // end transaction function argument
); // end db.transaction call
return books;
}
Then somewhere that you called this function do something like this:
var html="";
for (i=0; i<books.length; i++) {
html += "<a href='" + books[i].url + "' title=\"" + books[i].title + "\" target='_new'><img src='" + books[i].thumbnailUrl + "'></a> ";
}
if (html == "") {
html = "No result...";
}
element.innerHTML = html; // consider using jQuery here for browser compatability reasons
This will simplify debugging your code in firebug or whatever and be more readable. Later IF you need the performance, you can try to recombine and use the existing loop as an optimization... Premature optimization is usually a bad idea. Write clear code that works. Even if you know you should optimize it, get it working and then optimize it after it works (preferably after you've demonstrated that you do in fact need to optimize it).
http://www.flounder.com/optimization.htm

Scroll to alphabet in a List (ArrayCollection dataProvider) (Alphabet Jump)

Hopefully this is easy but that sometimes means its impossible in flex and I have searched quite a bit to no avail.
Say I have a list (LIST#1) of artists:
2Pac
Adele
Amerie
Beyonce
Jason Aldean
Shakira
The Trews
I also have a list (LIST#2) that has the values #,A-Z - how would I create an alphabet jump?
So If a user clicked on "A" in LIST#2 that would automatically scroll to "Adele" at the top of LIST#1 - not filter so he/she could scroll up to view 2Pac or down to view The Tews if they were not in the view yet.
Its a standard Flex Spark List with an ArrayCollection as the dataProvider - the artist field is called: "title" along with a unique id field that is not visible to the user.
Thanks!
Please see comments on marker answer for discussion on Dictionary that may be faster in some cases. See below for code (HAVE NOT CONFIRMED ITS FASTER! PLEASE TEST):
private function alphabet_listChange(evt:IndexChangeEvent) : void {
var letter:String;
letter = evt.currentTarget.selectedItems[0].toString();
trace(currentDictionary[letter]);
ui_lstLibraryList.ensureIndexIsVisible(currentDictionary[letter]);
}
public function createAlphabetJumpDictionary() : Dictionary {
//alphabetArray is a class level array containing, A-Z;
//alphabetDictionary is a class level dictionary that indexes A-z so alphabetDictionary["A"] = 0 and ["X"] = 25
var currentIndexDict:Dictionary = new Dictionary; //Dictionary is like an array - just indexed for quick searches - limited to key & element
var searchArray:Array = new Array;
searchArray = currentArrayCollection.source; //currentArrayCollection is the main array of objects that contains the titles.
var currentIndex:Number; //Current index of interation
var currentAlphabetIndex:Number = 0; //Current index of alphabet
for (currentIndex = 0; currentIndex < searchArray.length; currentIndex++) {
var titleFirstLetter:String = searchArray[currentIndex].title.toString().toUpperCase().charAt(0);
if (titleFirstLetter == alphabetArray[currentAlphabetIndex]) {
currentIndexDict[titleFirstLetter] = currentIndex;
trace(titleFirstLetter + " - " + currentIndex);
currentAlphabetIndex++;
} else if (alphabetDictionary[titleFirstLetter] > alphabetDictionary[alphabetArray[currentAlphabetIndex]]) {
trace(titleFirstLetter + " - " + currentIndex);
currentIndexDict[titleFirstLetter] = currentIndex;
currentAlphabetIndex = Number(alphabetDictionary[titleFirstLetter] + 1);
}
}
return currentIndexDict;
}
private function build_alphabeticalArray() : Array {
var alphabetList:String;
alphabetList = "A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z";
alphabetArray = new Array;
alphabetArray = alphabetList.split(".");
return alphabetArray;
}
private function build_alphabetDictionary() : Dictionary {
var tmpAlphabetDictionary:Dictionary = new Dictionary;
for (var i:int=0; i < alphabetArray.length; i++) {
tmpAlphabetDictionary[alphabetArray[i]] = i;
trace(alphabetArray[i] + " - " + i);
}
return tmpAlphabetDictionary;
}
private function buildCurrentDictionary() : void {
trace("Collection Changed");
currentDictionary = new Dictionary;
currentDictionary = createAlphabetJumpDictionary();
}
The Flex Spark list has a very convenient method called ensureIndexIsVisible(index). Check the Flex reference documentation. All you have to do is to find the index of the first artist for the corresponding selected alphabet letter:
public function findAlphabetJumpIndex():Number
{
var jumpToIndex:Number;
var selectedLetter:String = alphabethList.selectedItem;
for (var i:int=0; i < artists.length; i++)
{
var artistName:String = artists.getItemAt(i);
var artistFirstLetter:String = artistName.toUpperCase().charAt(0);
if (artistFirstLetter == selectedLetter)
{
jumpToIndex = i;
break;
}
}
return jumpToIndex;
}
You can iterate your artist list data provider and check if artist name starts with selected alphabet from list two. When corresponding artist is found, set artist list selected index a value what you get from iterating data.