readRecords issue

my project - https://studio.code.org/projects/applab/xEEOqp8zpOJ2g-YyCntajlhvh0crb2ysLvP1Pg5LCF8

Hi! Thanks for reading!

The readRecords on line 71 isn’t doing anything. In the debugger, it passes by it, with numStudents staying at 0, even though there are records in the data table. This happens when I limit the records being asked for, and when asking for all of them.

If you have another minute - is there a more elegant way to represent table data in memory? AppLab doesn’t have two-dimensional arrays or structures, that I can tell. The examples I find all use the table data right away, without storing it outside of readRecords.

Carol

Unfortunately readRecords, createRecord, or any other data manipulating function is asynchronous. That is why a callback must be supplied. On line 91 and 92, it appears like the code expected readRecords to either be instant or suspend execution until the callback was called. A simple fix would be making currentNameIndex=-1 and learnNextName() a part of the callback.

Since numStudents does work after the fix, it seems the problem is solved. However the text does not display anything so there appear to be other problems. Since I am unsure of what the expectations are and what actually happens, I can’t really help with that.

As for the other ways to store data, I don’t have much experience with createRecord but I suppose it is possible to stringify json and then parse it afterwards.

Thank you for your time!

I made the change, but the behavior is the same. I still get numStudents = 0. Did I misunderstand your fix? (Lines 71-93)

Carol

Mostly this works now. I uncommented all the code that you took out to debug. I also removed event as a parameter to get rid of all those yellow triangles. While it is true they are just warnings, it is also true they are sometimes useful and best to remove them all.

I moved the 4 text labels around so it doesn’t overlap the 4 radio buttons. Testing shows it can put things into the database. That’s good.

You have a bit of an initialization problem. One programming proverb I like is: Once and only once. People forget the first part a lot, the once part. So let’s include something you didn’t say and create a function.

function learnNames () {
  currentNameIndex = -1;
  score = 0;
  tries = 0;
  learnNextName();
}

This function says that the first name is different than the following name. And it is because we need to do some initialization. So we call this function to start the guessing game instead of learnNextName.

Let’s look at the guessing game portion.

else {  
          randomIndex = i;
          while (randomIndex === i)
            randomIndex = randomNumber(0, numStudents-1);
          setText (nameLabels[i], goesBys[randomIndex]+ " " +lasts[randomIndex].substring(0,1)+"."); 
        }

There are some problems here. First off if there is only one student in the database you have an infinite loop. The second thing I see is you expect there to be 4 and only 4 students. I think maybe you meant to have randomIndex = currentNameIndex. So we can try that.

Next we see if((correctNameIndex == 1) && (getChecked (nameRadios[correctNameIndex]))) { That first clause is only going to work 25% of the time so lets take that out.

Let’s also get rid of a couple lines and create a function with the duplicated code:


function shortName(n) {
  return goesBys[n]+ " " +lasts[n].substring(0,1)+".";
}

Now we see an error. setText(nameLabels[i], shortName(i)); When we substitute the function we see that i is not the correct index and we change it to setText(nameLabels[i], shortName(currentNameIndex)); And that seems good, but…

Now we have an out of bounds problem. Looking at the code we see: if (currentNameIndex == numStudents). That is a problem because currentNameIndex starts at 0. I know that you know this because you accounted for that elsewhere in the program. So change that to if (currentNameIndex === numStudents - 1)

I think you have a naming problem. You have correctNameIndex, currentNameIndex, and randomIndex. If you change these to correctRadioButton, currentStudent, and randomRadioButton you wouldn’t have the same kind of confusion.

I think that is enough for you to chew on for now. Let us know how it is going.

Don,

Thanks for all the info! This is awesome! I made a lot of changes based on your feedback.

There is something basic wrong with the code before learnNames, in the onEvent for choosePeriodNext (line 71), with readRecords.

Is the debugger supposed to work within readRecords, or does the callBack functionality not work with the debugger? (I can’t step through this code.)

When I break immediately after readRecords (line 89), numStudents == 0 and there is no content in the arrays. The data table exists, with 12 records. A much earlier version of the code was able to read the table and the console.log statement printed the names.

Am I using readRecords properly? Is there a timing issue with the callBack?

Thank you. I appreciate your time very much!

Carol

You can set a breakpoint in the callback function. When and if it is called it will break. Callbacks are never guaranteed to run. In the case of database calls, if access to the database is somehow blocked it will never run.

When I got your code to run I had to make the code where you fill the arrays with data not commented out. Is that your problem? Sometimes it is the simple stuff.

You should also be aware of how the select photo widget works. You select a photo and press open. What happens is that photo is transferred to the studio.code.org server and given a random name. That random name is available within the session data for your browser connection. What that means is that when you close your browser be it Chrome, IE, Firefox, or Edge those photos are gone.

Don,

Oh no! I thought I was super lucky when I saw the photo element. I had assumed that security wouldn’t allow a file upload, but then imagined that Code.org found a secure method. When I tested it, I didn’t close my browser. Bummer. I will have to upload the photos manually.

I agree it is something simple with the function that contains readRecords (lines 71-92). The lines in this function aren’t in comments anymore. When I add a break before readRecords, the debugger passes right over it. Do you see anything?

onEvent(“choosePeriodNext”, “click”, function() {
// var chosenPeriod = getText(“period2”);
readRecords(“Students”, {}, function(records) {
numStudents = records.length;
if (numStudents>0) {
for (var i =0; i < numStudents; i++) {
console.log (records[i].goesBy);
goesBys[i] = records[i].goesBy;
pronounss[i] = records[i].pronouns;
periods[i] = records[i].period;
firsts[i] = records[i].first;
lasts[i] = records[i].last;
games[i] = records[i].game;
jobs[i] = records[i].job;
goodAts[i] = records[i].goodAt;
toKnows[i] = records[i].toKnow;
photos[i] = records[i].photo;
}
learnNames();
} else setScreen (“home”);
});
});

Here is the project link, if that is easier - https://studio.code.org/projects/applab/xEEOqp8zpOJ2g-YyCntajlhvh0crb2ysLvP1Pg5LCF8

Thank you for your time. I’m new to AppLab and getting good practice!

Carol

That actually works.

readRecords has a function expression for the callback parameter that the debugger will pass over because it isn’t called till later. To break inside that loop you need a breakpoint inside the function.

Looking through your code I do have a so called pro-tip for you. Break the code down into smaller pieces. App Lab doesn’t really encourage you to do that. If anything they tend to promote that bad habit. One thing to start with is turning all those function expressions into named functions.

So try something like this:

onEvent("choosePeriodNext", "click", getStudentsFromDatabase);

function getStudentsFromDatabase () {
  readRecords("Students", {period: getText("period2")}, recievedStudentsFromDatabase);
}

function recievedStudentsFromDatabase (records) {
  numStudents = records.length;
  if (numStudents > 0) {
    for (var i = 0; i < numStudents; i++) {
      goesBys[i] = records[i].goesBy;
      pronounss[i] = records[i].pronouns;
      periods[i] = records[i].period;
      firsts[i] = records[i].first;
      lasts[i] = records[i].last;
      games[i] = records[i].game;
      jobs[i] = records[i].job;
      goodAts[i] = records[i].goodAt;
      toKnows[i] = records[i].toKnow;
      photos[i] = records[i].photo; 
    }
    learnNames();    
  } else setScreen ("home");
}

The blocks tend to push you toward using function expressions, but when you get to a program of this complexity it is time to switch to text and write it out with names that describe what it does.

Now let’s look at those arrays. You don’t need them. I know you know how to use objects because you write statements like goesBys[i] = records[i].goesBy; So instead of taking all the values out of the objects just use the objects.

While we are at it change numStudents to numberOfStudents we started that whole abbreviation thing in the 60s when FORTRAN only let you have 8 letters to name a variable. With auto-complete we don’t do that anymore.

Let’s also create a function to help us understand what is happening. This is called an explaining message. Why not 2?

var students; //move this to the top

function noStudents () {
  setScreen ("home");
}

function chosenPeriod () {
  return getText("period2");
}

onEvent("choosePeriodNext", "click", getStudentsFromDatabase);

function getStudentsFromDatabase () {
  readRecords("Students", {period: chosenPeriod()}, receivedStudentsFromDatabase);
}

function receivedStudentsFromDatabase (records) {
  students = records;
  numberOfStudents = students.length;
  if (numberOfStudents > 0) {
    learnNames();    
  } else {
    noStudents();
  }
}

One Javascript best practice is to always use a block after if and else. So I wrapped a block around noStudents();. App Lab is just an old version of Javascript.

learnNextName still has a bug in it try breaking it down into smaller pieces with functions that explain what the code is doing. When you get to small enough pieces that you can just look at it and see it does what you want then you are done.