Monday, November 23, 2020

My Toothbrush Timer: On the Cusp of Going Mobile

A couple of days ago I had a few extra minutes in the morning to work on my app. I thought I would quickly add the option to pause the timer. This sounded simple enough and I figured I could add this feature in 15 or 20 minutes. Well, about three hours later I had it more or less figured out. You just never know where app development will take you.

The concept sounds simple enough - just stop everything with one click and restart it all with another click. But, this proved to be a more thornier challenge than it would seem at first glance. It stems from the fact that I'm using LiveCode's function the seconds as the way to keep the timer and the intervals accurate. (I explained how the seconds works in LiveCode in my previous blog posting. I recommend reading that first before continuing here.)

Here's a short explanation:

When the timer begins it saves the current time (in seconds) in a variable. For simplicity sake, let's just say that the seconds is equal to 0. The app then continually checks the current time and puts those seconds into the display. When the Stop button is pressed, the current time at that moment is saved into another variable and the timer is stopped. Of course, the seconds - time, in other words - just keeps going. When the Start button is pressed to start the timer up again I can't just restart the timer based the seconds at that moment because the time elapsed includes the time that the timer was paused. So, I have to subtract the paused time from the total time and only show the "running" time in the display. Here's the solution I programmed into the app:

0 seconds: Start the timer

50 seconds: Pause the timer. At this point, 0:50 remains on the display.

150 seconds: Start the timer back up. 150-50 is 100 seconds. So the display needs to show the following:

Current Time (150) - Original Starting Time (0) - Total Time Paused (100) = Total timer seconds to be displayed

And, every time the user pressed the Stop button, the total time paused must be increased accordingly.

All I can say is that I couldn't get it to work for about three hours. Somewhere my logic was failing me. In the end, I think the problem was that I had this algorithm sitting in the wrong place, namely inside the LloydStopWatch procedure I explained in my previous post. After I moved it and a few more tweaks, things seemed to work, more or less. I say "more or less" because there is still a small bug. Somehow one second is sometimes added to the total time elapsed. The problem is minor, but annoying. Here's what can happen. Imagine the timer with two intervals, each two seconds apart. The first interval is signaled with a bell and the second with chimes. So, the pattern is a timer going off every two seconds, first with a bell and then with chimes. So, an alert should be given only when an even number appears on the display. However, sometimes when pause is pressed, the alert sounds moves to odd numbers instead, but at two second intervals. It seems to resemble a kind of "rounding" error, but I can't find the source. So, after those three hours, I decided to just "let it go."

Displaying the Time Correctly

I made one other improvement to the app, namely showing the timer not just in seconds, but in minutes and seconds using the conventional display of Minutes:Seconds. This was a fun challenge to program. So, consider that a total of 147 seconds have gone by. What should the display read? A quick mental calculation results in 2 minutes and 27 seconds, or 02:27. That same mental calculation is shown in this code:

1:    put trunc(varTimeElapsed/60) into varDisplayMinutes  
2:    put (varTimeElapsed)-(varDisplayMinutes*60) into varDisplaySeconds  
3:    if the length of varDisplaySeconds < 2 then put "0"&varDisplaySeconds into varDisplaySeconds  
4:    put varDisplayMinutes&":"&varDisplaySeconds into line 1 of field "my time"  

In line 1, we divide the total seconds elapsed (varTimeElapsed) by 60. Dividing 147 by 60 gives you 2.45. Well, we just care first about the number of minutes. The function trunc truncates, or chops off, any decimal that remains. So, we are left with just two. That goes into the variable varDisplayMinutes.

Now we have to figure out the remaining number of seconds. All we need to do is take the total seconds elapsed (again, varTimeElapsed) and subtract from it the number minutes we just calculated.  Of course, we have to convert those minutes back into seconds first (varDisplayMinutes*60). The result is line 2: 147-120 = 27. We put the 27 into the variable varDisplaySeconds.

Lines 4 pretties everything up before displaying the time elapsed in the field "my time." Using the concatenation symbol "&" we stitch together minutes, a colon in the middle, followed by the seconds.

Adding a Fourth Interval Option and Alert Sound Options

In my last blog post, I alluded to the fact that having at least four intervals is preferred. So, this version of the app added that as well. I also had some fun finding and adding a menu of alert sounds. I downloaded several sound effects using the .wav sound format from this web site:

https://www.wavsource.com/sfx/sfx.htm

Here's a screen shot of the current version of the app paused after 97 seconds:



One More Important Step to Go

The app works well enough for me to brush my teeth, except for one thing. I don't want to put my laptop on the bathroom sink. So, the obvious next step is to "go mobile." This will require quite a few changes to the app's interface, such as replacing all of the text input fields with selector wheels. 

I also will need to move the app to my iPhone for testing. I am not looking forward to this. It will first require me to relearn how to create a "provisioning profile" in my Apple Developer Account using my iPhone's serial number and then relearning how to get the app from my computer to the iPhone. I've done it before and I'm sure I can do it again. But, it's been a few years and my memory of doing it isn't very pleasant.

Thursday, November 19, 2020

My Toothbrush Timer: Adding Initial Functionality

I've made progress on this app over the past few weeks, despite only being able to work on it now and then. As usual, my progress in building the app is outpacing my blogging about its construction. So, this blog post is at least the first of two in an effort to catch up. Here I give a report of the design of the app as of about two weeks ago.

Here is a screenshot of the app while running:


One obvious thing I need to work on is showing the time elapsed in the format of minutes:seconds. Using the example of brushing my teeth for 2 minutes, these intervals alert me half-way through (to switch from lower to upper teeth), then a warning 50 seconds later that I only have 10 seconds left, followed by the 2 minute bell. Really, this last alert is not necessary because the toothbrush I use just turns off automatically at 2 minutes. If I wanted to switch between lower and upper every 30 seconds, then just one interval setting of 30 would be needed. In the example of giving myself alerts while making a 12-minute conference presentation - a typical time limit - I would likely add alerts at intervals of 6 minutes (half-way point), 4 minutes (the 10-minute mark), 1 minute (just one minute remaining), and 45 seconds (to give myself 15 seconds to thank everyone and get off the stage). Of course, that would require four intervals, not three, so I would need to add one more interval to the app. (Hint hint: That's a clue about what's coming up in next blog post.)

Telling the Time in LiveCode with the Seconds


Having an accurate timer is fundamental to many applications. The best way to develop a timer in LiveCode is with the function the Seconds. My timer app starts by checking to see what the time is now in seconds. As I write this, it is 7:48:11 am on Wednesday, November 18, 2020. The idea is to make that the starting time when the Start button is pressed. Then, all timers are checking to see how much time has passed since then. So, if the first timer is set to go off 30 seconds later, the time will need to be 7:48:41. But if you think about it, this standard format for telling time is based on a past starting point using an arbitrary system we call the Julian calendar. Today's date and time uses as its starting point the likewise artibrary big "changover" from Before the Common eEa (BCE) to the Common Era (CE), which is just the secular version of BC and AD. The key word there is "arbitrary." Really, any starting point would do as long as we all recognize and accept it as the starting point. In LiveCode, the seconds is based on a similar starting point called the eon, defined in the LiveCode dictionary as follows: 
LiveCode uses midnight, January 1, 1970 as the start of the eon. Date and time functions are computed from that date.
So, the function the seconds gives you the current time in seconds since since very arbitrary starting point. As I write this sentence, the current time, in seconds, according to LiveCode is 1605704460. But now, as I write this sentence the time is 1605704492. So, if you subtract the first from the second you get 32 seconds. That interval is the basis for how my app works.

Doing Two (or More) Things at Once

The important thing to note is that while the timer is running, the app is constantly checking to see if the user presses any buttons. In contrast, many apps work by waiting until the user presses a button to perform a task. The app does the task, then waits for the user to press another button. If the task takes a long time to complete, the user has to wait until the task is completed before any other buttons can be pressed. Doing one thing at a time versus doing multiple things at the same time may sound like a subtle difference, but it requires a special programming technique to pull this off.

Here is the code for the app's engine called MainEventLoop, a procedure I wrote to run everything in the background while the user chooses timing intervals and clicks buttons to stop and stop the app's timer:

1:  on MainEventLoop  
2:    if varRunMainLoop is true then  
3:     lock screen  
4:     put the seconds into varSecondsEnd  
5:     LloydStopWatch  
6:     unlock screen  
7:    end if  
8:    put the pendingMessages into tMessages   
9:    repeat for each line aLine in tMessages   
10:     if item 3 of aLine is "mainEventLoop" then cancel item 1 of aLine   
11:    end repeat    
12:    send "mainEventLoop" to me in 50 milliseconds  
13:  end MainEventLoop  
14:
15:  on LloydStopWatch    
      The magical code goes here
16:  end LloydStopWatch  

A procedure called MainEventLoop just runs and runs by looping forever while the app is running. This works by having it "call itself" in line 12 in a very short amount of time (i.e. 50 milliseconds). I know, that's a very weird idea. It's usually very hard for new programmers to grasp. I know it was for me. In that short amount of time, it completes any assigned task while "watching" to see if the user does something, such as clicking a button. There is nothing special about 50 ms. It could be called in one second, but 50 ms is plenty of time for the app to do its work.

Another procedure, LloydStopWatch, has all of the code that actually computes how many seconds have transpired since starting the timer. This code also keeps track of the intervals and knows when play a chime at the end of each interval. Note that line 5 is where the LloydStopWatch procedure is triggered, but only if the variable varRunMainLoop is true. This variable controls whether the timer is active. If the user clicks the Start button it becomes true. If the user clicks the Stop button, it switches to false.

So, how does one get MainEventLoop to start running. The answer is in the code for the button "Start:"

1:  on mouseup  
2:    put 0 into varTimeCurrentLoop  
3:    put 0 into varIntervalTotal  
4:    put 1 into varIntervalTurn  
5:    put false into varRestartInterval  
6:    put empty into field "notes"  
7:    put empty into field "Interval Totals"  
8:    put empty into varTimeLog  
9:    put field "Iminutes1" into varIminutes1  
10:    put field "Iseconds1" into varIseconds1  
11:    put field "Iminutes2" into varIminutes2  
12:    put field "Iseconds2" into varIseconds2  
13:    put field "Iminutes3" into varIminutes3  
14:    put field "Iseconds3" into varIseconds3  
15:    put true into varRunMainLoop  
16:    put the seconds into varSecondsBegin  
17:    put varSecondsBegin into varSecondsBeginInterval  
18:    MainEventLoop  
19:  end mouseup  

Lines 2-18 initiate all of the main fields and variables, essentially getting them ready for the start of a new timing session. Line 15 sets the variable varRunMainLoop to true and line 18 initiates the MainEventLoop procedure.

Lines 9-14 takes whatever interval values the user had entered and passes them on to unique variables for each for use within the LloydStopWatch procedure.

Line 16 essentially checks the current time and becomes the moment in time from which the time elapsed is figured. This value is held by the variable varSecondsBegin. Line 4 in the MainEventLoop procedure continually checks the current time, puts it in a variable named varSecondsEnd, then calls the LloydStopWatch procedure. The time elapsed in seconds is simply the difference between these two values. This simple subtraction problem is happening every 50 ms.

Interestingly, I first put the line "put the seconds into varSecondsEnd" into the LloydStopWatch procedure. But, I experienced some weirdness in the operation of the app. After the app had been running for about 10 minutes, the app started skipping a bunch of seconds which, of course, ruined the reliability of the app. I really couldn't figure out why. I tried all sorts of programming hacks, but none worked. So, in a bit of desperation, I moved this line to the MainEventLoop procedure and it solved the problem. All I can say I had a hunch it would work. I think the problem had something to do with the recursive nature of this MainEventLoop procedure. I've experienced weirdness in this coding technique in previous apps. I actually asked for help in one of the LiveCode forums a couple of years ago about this. Lines 8-11 and the lock/unlock screen code were suggested to me by an expert LiveCode user which solved the problem for me back then. So, I've continued to use this code anytime I use this looping technique. In short, you start building up a backlog of calls for the MainEventLoop procedure and eventually all hell breaks loose. Lines 8-11 clear out all previous calls for the procedure.

Finally, the timer stops when the user presses the "Stop" button - here is the code for the Stop button:

1:  on mouseup  
2:    put false into varRunMainLoop  
3:  end mouseup  

By changing the value of varRunMainLoop to false, it stops the loop within MainEventLoop that had been triggering the LloydStopWatch procedure.

Short List of Things to Do

Actually, the list is quite long, but here are some of the obvious revisions and updates that are needed:

  • Add a fourth timer interval
  • Change the elapsed time format
  • Allow the user to pause the timer
  • Add a choice of alert sounds
  • Add a mobile interface design, including spinner wheels to select time intervals and a vibrate option for alerts


Friday, October 30, 2020

My Toothbrush Timer App

It's been a long time since I started a new LiveCode project. Well, the truth is that I've been very preoccupied with my Q sort project for the past year. Fortunately, my "Lloyd's Q Sort Tool" app has been quite stable for some time now. I've only been making small tweaks to fix small bugs and to add various improvements. So, I've been wanting to do something completely different and have some fun doing it. 

Fortunately, I'm teaching a doctoral course this semester on the topic of design thinking. One of the books we've been reading is Creative Confidence by the brothers Tom and David Kelley. David is one of the co-founders of both the IDEO design company and the d.School at Stanford University. Here's the full reference: 

Kelley, T., & Kelley, D. (2013). Creative Confidence: Unleashing the Creative Potential Within Us All: New York: Crown Business.

I've been using this book for a few years in my teaching and it get great reviews from students. The book's main theme is to inspire everyone to believe that the ability to be creative is in all of us. Rereading the book for the umpteenth time always puts me back into a very creative design mood. The book encourages you to look around for design problems, challenges, and opportunities - big and small. You can't help but to look around your personal world to see things you otherwise take for granted.

Last Sunday, I was reading the book in preparation for class on Monday. So, I was thinking about design a little later on as I was brushing my teeth. Suddenly, an idea hit me. I have an older Oral-B toothbrush that I bought about 10 years ago at the recommendation of my dentist. This battery operated toothbrush has a built-in timer that runs the motor for two minutes - the recommended amount of time to spend brushing one's teeth. In order to make sure I brush evenly - north and south - I've developed a habit where I carefully monitor my Casio wristwatch's stopwatch as I brush to make sure I switch from my lower to upper teeth precisely from time to time in order to brush evenly between both. It suddenly dawned on me that it would great to have a timer app with audio prompts that allow me to punch in various time intervals for a bunch of reminder chimes. The timer option on my iPhone's clock app doesn't meet the need because I need a reminder at each intermediate interval without having to touch the phone (given that my hand is covered in toothpaste "runoff"). My first thoughts of how to design a timer app with the option to add flexible intervals began that moment. 

I also thought of other uses for such a timer app. For example, a favorite exercise of mine is to walk briskly for two or three minutes followed by jogging for two or three minutes. It would be great to have a timer that would sound a chime at two minutes, then three minutes later, and then repeat the sequence. Another use would be when I'm making a presentation in class or at a conference. If my presentation lasts 15 minutes, it would be nice to get a reminder at the 10 minute mark, then three minutes later, then one minute later. And, if I'm presenting with multiple people, the app can signal when the "torch needs to be handed" over to the next presenter.

I quickly created a mock-up using LiveCode. My goal was to show my design thinking students a visual example of my design idea - the first prototype. This first version had no functionality, but it provided a nice visual representation of what I was doing:


This took about 30 minutes. Although the screen looks a little busy, those smaller buttons below the "Start" button would actually come and go as needed for a clean design. As I created this mock-up, the need for those button functions became clear, so adding them acts as notes for my next iteration. For example, the "Stop" button would replace the "Start" button when the app was running. A "Pause" button would also need to appear as soon as the "Start" button was pressed and would be replaced with a "Resume" button when the "Pause" button was pressed. (Honestly, I don't remember why I thought I needed the "Restart" button.) So, all of these are "placeholder" buttons that I'll integrate into the design at some point.

Beginning to Code

On Monday, as I was finalizing my class lesson plans, I couldn't help but start to code some initial functionality into the app. I started by coding how to start and stop a timer using "the seconds" function in LiveCode. This also required me to relearn how to set up a repeating loop in the background while the user interacts with the app, something I learned years ago in one of my earlier gaming/simulation projects. But, I'll save any details about coding for my next post. The main point here to celebrate finding a problem to be solved and beginning to design a solution that offers ways to improve many other problems as well. That's the great thing about design thinking -- even small design ideas are valued. You never know where they might lead. 

Final Thoughts

This is actually a mobile app I could use. I don't plan on keeping the "toothbrush" title, but it's a fun title to keep for now. (I've always struggled with choosing good app titles.) It has also been a long time since I created a mobile app, so I thought this project would also be a good way to reacquaint myself with the process, particularly on the all-to-complicated Apple iOS side.

Oh, one more thing ... I've since learned that the newer Oral-B toothbrushes models now have a 30 second chime reminder. Maybe it's time I buy a new toothbrush.

Monday, December 31, 2018

Are You With Me? A Role-Playing Simulation for First Year Agricultural Education Teachers


New Year's Eve is always a good time for reflection. As the year ends I find myself reflecting a great deal on my research and design work from the past year. I think this is because I've spent this holiday break trying to catch up on my writing for each. In doing so, I've noticed I've not focused nearly as much attention on this blog as I have in the years since I began it. I think the reason for this can be explained by the title of the blog - Learning LiveCode. It turns out I've devoted very little time in 2018 to learning new programming skills with LiveCode and instead I've spent most of the year using my LiveCode knowledge and skill to work on two fairly large software projects. The first is my Q sort tool project, something I've written about extensively here since I started the Q sort project in 2015. The second is a project I've been working on in collaboration with colleagues in UGA's College of Agricultural and Environmental Sciences, specifically the Department of Agricultural Leadership, Education & Communication.

I've led the development of a game for the project titled "Are You With Me?" This game is about helping new agricultural education teachers be successful in both their teaching and their interactions with the community while maintaining a balance in personal and work life in order to avoid teacher burnout.  This game is probably best thought of as a role-playing simulation of being a first year agricultural education teacher in a community that is also new to the Ag Ed teacher.

The game is designed to help new Ag Ed teachers to understand the importance of first coming to know their community and community leaders and then how to think strategically to make decisions that will lead them to earning credibility, respect, and trust of everyone in the community. The goal of the game is to build community engagement while maintaining enthusiasm for teaching. If successful, the teacher will begin securing important structured agricultural experiences for students.

This project has been fun and challenging. First, the project consists of a wonderful group of people. I won't name them here because I don't have permission from them to do so yet (I'll update this posting when I do). Everyone respects each other and everyone brings creative ideas to the table, while also being willing to do a reality check when necessary. The project has been fun for me because I've been able to practice my preferred way of doing design, namely to build an initial prototype of a design idea and let the group react to it. Using this approach, we've slowly arrived at a design that is very solid. I built the first prototype almost exactly two years ago and this is the first time I've written about it.

How the Game Works


Here is a screen shot of the game's main screen:
The game consists mainly of making decisions and dealing with the consequences. Five possible decisions choices are presented to the new teacher at each round, along with information about how much engagement will be created in the community if that decision is a good one. However, the best decisions must match the agricultural profile of the community in which the school resides. If the community's agricultural is largely based on crops, then choosing to form a dairy or cattle club aren't advised. Each decision also has a "credibility threshold." This means that even a decision that is well-suited to the community will not work if the teacher hasn't at least attained as much credibility as shown. It takes time and effort to gain credibility in a community, especially if you are new to it. A new teacher's enthusiasm and high hopes must be tempered with reflective, strategic action. The player can track their credibility in the "Teacher Dashboard" in the bottom portion of the screen.

As engagement in the community increases, as shown in the "Community Involvement Dashboard" in the top portion of the screen, opportunities to create a structured agricultural experience, or SAE, are presented. These SAE opportunities can best be thought of as internship experiences for the teacher's high school students. An example would be a dairy farmer willing to mentor a high school student in the dairy business by having them come to the dairy farm on a regular basis and help out in some way.

If poor decisions are made, or if the credibility threshold of good decisions is ignored, the teacher will slowly lose their enthusiasm. Wouldn't we all if our enthusiastic attempts at making a difference in the community are met with resistance or, worse, indifference? If this happens the player will slowly see their "Teacher Quality of Life" diminish, going from enthusiastic to optimistic to wary to frustrated, and finally to burned out. At that point, they may very likely leave the teaching profession. This is the worst possible outcome of the game.

But, if they are able to maintain their teacher quality of life and begin to gain credibility, followed by increasing community engagement, they will end the round successfully. The player can gauge their success by seeing how long it took them to achieve 100% community engagement - as denoted in "teacher time" - and also how many SAE opportunities they were able to secure. These results become the "score to beat" during the next round. Most important, the teacher/player can reflect on their game performance by consulting the decision log (on the left). This shows each of the decisions they made along with the consequences of each. You might notice some other features, such as the opportunity to consult an expert from time to time to offer guidance and counsel at critical times. The player can also "Seek Community Support" from time to time. This is akin to reaching out to someone in the community for help. Reaching out like this in a humble way automatically garners a little bit of community support. However, getting this kind of "just in time" expert or community assistance is offered only on a limited basis. One cannot - and should not - dip from these wells too often.

We also have created a total of five different community profiles, with the potential to create an unlimited number, in order to keep the game fresh and appealing no matter how many times one has played it.

I should note that this game is but one of several elements to the project, so there are many other resources available to support these new teachers.

That's Great, But...


Yes, I'm sure you can quickly see a serious flaw in the project, namely the poor graphic design. Well, true to my design style, I always focus on the functionality of the project first - in this case, the elements of the game play - and then come back to work on the graphic design. Fortunately, this is a funded project and we have just hired a graphic designer to help us. I've be revising the project with the graphic designer's help in the next few weeks.

I hope to write more about this project in the months ahead.

On that note, I look forward to many rewarding design experiences in 2019 - most of which I'm sure will include LiveCode.

Tuesday, October 2, 2018

Playing NPR's Sunday Puzzle: My First Successful Submission Thanks to LiveCode

I'm a big fan of National Public Radio (NPR) here in the United States. Every Sunday morning on Weekend Edition there is a segment called the Sunday Puzzle hosted by Will Shortz, the puzzle master. Will is a very famous puzzle guru (and ping pong player) who is probably best known for editing the New York Times crossword puzzle.

I'm usually in the kitchen cooking or doing the dishes when the Sunday Puzzle segment airs. Each week it features a "lucky listener" who Will challenges to solve some puzzles on air. Will then challenges all listeners with a weekly puzzle to be solved by Thursday. Will then randomly chooses one of the winning entries to join him on air the next Sunday. I'm really terrible at solving these puzzles. But, I just submitted my first correct answer to a recent puzzle. I solved it largely due to LiveCode.

Here's the puzzle, quoted from the show's transcript:

It comes from listener Jim Levering (ph) of San Antonio, Texas. Think of an affliction in five letters. Shift each letter three spaces later in the alphabet. For example, A would become D. B would become E, et cetera. And the result will be a prominent name in the Bible. Who is it? So again, an affliction in five letters. Shift each letter three spaces later in the alphabet. And the result will be a prominent name in the Bible. Who is it?

This seemed straightforward to me and I quickly determined it was easier to start with names from the Bible and work backwards. What constituted an affliction seemed too vague to me. The few names I thought of did not pan out. Interestingly, the first name I thought of was Judas. (You would think the first name I thought of was Jesus, given my Catholic school upbringing. But, I personally think Judas is one of the most interesting characters in the Bible.)

Following Will's directions above, here is what you get when you transpose Judas:

Judas > graxp

This is certainly not an affliction I'm aware of. After trying a few more names, I gave up and went on to other things. But, for some reason, this puzzle stuck with me over the next few days. Then, as I was falling asleep Wednesday night, it occurred to me that I could easily write a program in LiveCode to transpose every name in the Bible if I could find a web site listing all of the names. The next morning, I quickly found all the Bible names on the Internet (I forgot to write down the link) and wrote the following program in LiveCode.

Here's a screen shot of the app:

The names of the three fields, from left to right, are "alphabet," "names," and "afflictions."

Interestingly, I found one web site with male names and another with the female names. The female names listed the information in this format:

Abigail – mother of Amasa, Sister of David. I Chronicles 2:15-17[1]
Abigail – wife of the wicked Nabal, who became a wife of David after Nabal's death. I Samuel 25[2]
Abihail #1 – mother of Zuriel. (Zuriel was the chief of the house of Merari). Numbers 3:35[3]
Abihail #2 – wife of Abishur and mother of Ahban and Molid. I Chronicles[4]
Abishag – concubine of aged King David. I Kings[5]


But, this posed no problem as I knew I could easily extract just the first word in the line.  And, you might notice that some of the names in the screen shot have a comma in them. I needed to look for that and strip it out if found.

Here is the complete script in the button "Search:"


1:  on mouseup  
2:    put empty into field "total tries"  
3:    put the number of lines in field "names" into L  
4:    put the number of lines in field "alphabet"into A  
5:    put empty into field "affliction"  
6:    repeat with i = 1 to L // Check each word  
7:     put empty into varAffliction  
8:     put line i of field "names" into varLine  
9:     put the first word of varLine into varName  
10:     if the last character of varName is comma then delete the last character of varName  
11:     if the length of varName <> 5 then next repeat  
12:     repeat with j = 1 to 5 //check each letter of word  
13:       put char j of varName into varLetter  
14:       repeat with k = 1 to A // check the alphabet  
15:        if varLetter <> line k of field "alphabet" then next repeat // find the matching letter  
16:        put line k-3 of field "alphabet" into varNewLetter  
17:        if k = 1 then put line 24 of field "alphabet" into varNewLetter  
18:        if k = 2 then put line 25 of field "alphabet" into varNewLetter  
19:        if k = 3 then put line 26 of field "alphabet" into varNewLetter  
20:        put varNewLetter after varAffliction  
21:       end repeat // end check the alphabet  
22:     end repeat // end check each letter of word  
23:     put varName&comma&varAffliction&return after field "affliction"  
24:    end repeat // End check each word  
25:    put the number of lines in field "affliction" into field "total tries"  
26:  end mouseup  

Here are just a few choice explanations of key lines in the script:

There are three repeat loops:

  • Lines 6 to 24 repeat everything for each name found in field "names." 
  • Lines 12 to 22 work on each letter in the name, so this always repeats 5 times.
  • Lines 14-21 do the three letter transposing. 

Here is an explanation of other key lines:

  • Line 9 puts the first word of the line into varName.
  • Line 10 looks to see if there is a comma as the last character. If there is, it deletes it. 
  • Line 11 checks to see if the length of the name is five characters. If it is not, the rest of the code is aborted and the next name in the list is chosen.
  • Line 16 is the transposing engine - once the matching letter is found in field "alphabet," it finds the letter that is three spots ahead of it.
  • Lines 17-19 are hacks. I got lazy - I hadn't had my first cup of coffee yet - and just manually figured out which letter went with a, b, and c.
  • Line 20 builds the newly transposed word.
  • Line 23 puts the Bible name and the transposed word, with a comma in-between, in a new line in the field "affliction."
After clicking the button "Search," it takes LiveCode a few seconds to process the 1964 lines in field "names." Again, I didn't know exactly what affliction I might find, but I figured I could scan the list easily to find one (or more). Here's a screen shot showing the correct answer:

I missed it the first time I scanned the list of 409 matches. But, I found it on the second pass:

Herod > ebola

I was one of 1100 people who submitted the correct answer. Thankfully, I was not chosen as the "lucky listener" to appear on next Sunday's show.




Wednesday, March 21, 2018

Using LiveCode as Pseudocode for Coding in PHP

In previous posts I've commented occasionally about integrating my LiveCode apps with HTML, PHP, and mySQL. I wish all programming environments were patterned after LiveCode. But, alas, they are not. Too bad because I've been programming in LiveCode long enough that I actually think in LiveCode. I need to get much more proficient in PHP and it dawned on me recently that I could leverage my LiveCode skills to do so. My idea is to first build a working prototype of functions or procedures I need to program in PHP with LiveCode. The idea being that I can work out the hard part - the logic - much more easily in LiveCode, then "convert" that logic to PHP code.

The idea of just creating an algorithm in everyday language is often just called pseudocode. The code doesn't actually work in any programming language, but it allows you to get to the heart of the logic without getting bogged down in syntax or rules. I have come to naturally write out my logic with LiveCode so these algorithms not only act as pseudocode, they are also work when plugged into LiveCode.

Here's a very simple example of some PHP code I wrote recently that took all of 10 seconds to write out first in LiveCode:

 if varDatabaseColumn is empty then put "Nothing entered." into varDatabaseColumn  
 put varDatabaseColumn&return after field "text"  


Stated in English: If the variable containing the information from the column (or record) downloaded from the database does not contain any information, then enter the phrase "Nothing entered." into that variable. Then, display or print that value on the screen with a line return after it.

Here is the PHP code I eventually wrote:

 if (empty($row_rsUser['rating_statement'])) {  
      echo "Nothing entered."."\n";  
 } else {  
      echo $row_rsUser['rating_statement']."\n";  
 }  

The LiveCode and PHP code aren't exactly equivalent in that I didn't actually need to use an if-then-else construction with LiveCode. But, just writing out what needed to happen in simple logic led me to the right PHP construction. What took me 10 seconds in LiveCode took about an hour in PHP because I am not fluent in PHP. I had to relearn that a period is used as the concatenation symbol. I had to relearn that /n is used to trigger a return (i.e. new line) on the screen. I had to relearn that a dollar sign is used to connote a variable. And so on.

Interestingly, in PHP there are several functions that address the idea of "emptiness" in a variable:
  • empty($var)
  • isset($var)
  • is_null($var)
Long story short, the empty command address the two situations that matched my need: Is the string just empty (nothing in it), or does it contain the value of NULL? I'm not prepared to explain the difference between nothing and NULL, other than to say that NULL is actually a value that can be entered into a database record. So, yes, that took some time to learn too.

I have several other immediate PHP programming jobs that are much more sophisticated. I will again start with building a working version with LiveCode as a way to figure out the logic, then "convert" that logic into PHP code.

I think it will be a long time before I start to think in PHP.


Saturday, March 17, 2018

Lloyd's Q Sort Tool is Now a Viable Option for Q Researchers

It is going on three years since I began creating a digital Q sort tool. Over the past few weeks, I've been on a very "deep dive" into this project thanks to having a research leave this semester - my first leave of any kind in 30 years. My goals this semester have been to finalize the app's initial development, create a web site for other teachers to use my tool at no cost, do a lot of reading about Q methodology, write about its development, and submit a grant proposal for some modest funding to keep the work going.

The three readers of this blog know that I actually released beta version 1.0 back in August. I used the term "beta" because this was a preliminary and cautious release. Anyone interested in getting an instructor account to use my tool needed my approval. It's not that I was expecting thousands of people to start using it, but I deliberately wanted to keep the number of users very small in order to have them help to spot problems and bugs, while also suggesting ideas for further development. As an unfunded project with me as chief designer,  programmer, web developer, statistician, PR department, and marketer, there are likely to be a lot of as yet undetected errors. I didn't want too many people cursing my name from afar as the errors were revealed.

As it turned out, very few instructors have signed up to be users, which has been disappointing, even though I've made three presentations about this project since the release of version 1.0, including one at the annual conference of the Association for Educational Communications and Technology (AECT) in November and another at the Conference on Higher Education Pedagogy (CHEP) at Virginia Tech in February. I've also sent out two announcements about my app to the Q-Method listserv run out of Kent State University. I actually had quite a few inquiries from the Q-Method folks, but only about whether my app could be used for research projects. I explained that my main goal and purpose in developing the app has been for instructional, not research, applications. These folks wished me well, but obviously had little interest other than the app's potential for conducting Q methodology research.

Time For Me to Sit Up and Pay Attention


So, for the last month or so I've started to rethink the goals of my Q sort project with an eye toward expanding it to include the needs of Q researchers. Obviously, that is the group who has shown the most interest and encouragement. So I decided to go for the best of both worlds by making my Q sort tool viable for Q researchers and people like me who want to tap the power of Q for enhancing instruction. To that end, I've made some important updates to the tool, many in just the past week (thanks to this being spring break here at the University of Georgia). This set of revisions mark an important milestone in the ideation and iterations of the design, especially as I write a draft design article about this project for presentation at the upcoming AERA conference in New York.

Here is a short summary of those updates.

Easy Exporting of the Data into Q Analysis Statistical Tools


Even though I was careful to make the raw data of a Q sort completed with my app readily accessible to a researcher, it was, well, really raw. With the help of one of my doctoral students, Tong Li, I added an export option in the instructor version of my app. This option now makes it very easy to export the data to the popular Q analysis applications, such as PQMethod an Ken-Q. I've become a big fan of the Ken-Q Analysis Tool, mainly because it's a web site, not a standalone app (and it's free), so I made a quick video on how to export data from my app into it:


I don't try to explain anything about the analysis, but only to demonstrate to a Q researcher how easy it is to export the data.

Two Other Features that Q Researchers Have Asked About, and One They Didn't


Embedded End-of-Q Sort Survey


Q researchers have repeatedly asked about whether my app can conduct and collect some survey data after the Q sort has been completed. Asking the user to explain their high and low sort choices along with any other comments that help to explain their thinking is a typical procedure in Q methodology. My response has been no, but also that it shouldn't be a big deal to collect these data through other means, such as through a Qualtrics survey. But, I also know that having the user jump out to yet another application at the critical moment when they are asked to do the hard work of reflection runs a huge risk of the user just quitting. So, I've now made asking three open-ended questions a feature in my app as well, along with giving the researcher control over the wording of the three questions. Given my hunch that instructors who use my Q sort tool for teaching will not want to ask these three questions, this feature can be turned off when the Q sort definition is created.

Making the Summary Survey Question Optional


Interestingly, another feature of my app that I've included from the very beginning is one that Q researchers don't like. After a user completes the Q sort, my app asks them a simple summary survey question in the style of a Likert question. For example, after completing a Q sort about ice cream, the user is asked to rate the statement "I like ice cream." from strongly agree to strongly disagree. Asking a question like this has been very important and useful in using Q sorts for instruction because it lets a class know how the entire group feels about the topic in a single data point. This helps to start the conversation. Well, Q researchers really don't seem to like this. Even though I explain that this question comes after the Q sort and therefore doesn't interfere to invalidate the Q data, they just don't want to ask a question of this sort. So, I've now made the asking of this summary survey question optional when a Q sort definition is created.

Creating these two features - and the option to use or not use either in a Q sort - have extended my understanding of how to have a standalone app created with LiveCode communicate with a mySQL database. Indeed, a great deal of my work over the past week has been exclusively on the mySQL database and the PHP code on the web site I created months ago to support the tool.

Kiosk Mode


Interestingly, one of my colleagues who is interested in using my Q sort tool in her teaching matter of factly mentioned how she planned on setting up a computer or two in a corner of her classroom for students to complete the Q sort at some point during the class. The reason is that she didn't expect students to bring a laptop with them for downloading the app. I thought this was a great idea, but it quickly dawned on me as she walked away that this wasn't possible because my app doesn't allow someone to complete a Q sort a second time, except for practice without any data being submitted to the database. I solved this problem by adding a feature to the instructor version of my app that I call Kiosk mode. As the name suggests, you can run my app like a kiosk on a dedicated computer somewhere and have lots of people come by to complete the Q sort. There is the risk of one person completing a Q sort multiple times, so it will be up to the instructor (or researcher) to police this. And, given that this feature is only available in the instructor version of the app, there is no chance some "rogue participant" could use the regular version of the app to stuff the ballot box.

Immediate Next Step: Lots of Testing


Any time a new version is rolled out, there is a good chance that bugs and errors remain. Testing is a time-consuming endeavor and, as I already mentioned, my time is divided between all of the roles needed on the project. So, before I release the version of the app with all of the updates discussed here, I'll first be turning my attention over the week or so to the mundane task of creating lots of sample Q sorts while playing the role of both interested and uninterested participant, all in the mission of uncovering the bugs. If anyone wants to join me, just let me know.

Eventual Next Step: Posting a Note to the ITFORUM Listserv


One place I haven't disseminated information about my Q sort project is the ITFORUM listserv. This listserv remains one of the best ways to communicate to professors, researchers, designers, and students in the field of learning, design, and technology (aka instructional technology). I'm not sure why I've been hesitating. I think one reason is that I wanted the app to be very solid before announcing it there. But, time may be almost ripe to post something to the ITFORUM listserv, especially because it might spur a few people in my field to seriously consider using Q sorts in their teaching or Q methodology in their research.