Designing E-Learning 3.0 in gRSShopper - 4



E-Learning 3.0 - Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7 - Part 8 - Part 9 - Part 10 - Part 11 - Part 12 - Part 13


Activities

Each week of the course will feature some events. These are mostly live video conferences with guests. I also want to be able to show slides and to include the activity feed. I've been creating these using xSplit and then broadcasting the entire screen, but it's a bit unreliable. It's how I have been creating my presentations on my main website, and I'm not happy with it. Plus, using the backchannel with the rest actually crashed my web server recently, so I need to fix that if I want to use it for the course.

Also, I want to avoid the problem of it being difficult to find the right page. I just want the event to show up in an activities page at the right time, so people don't have to do anything but go there. Then I'll handle all the logistics behind the scenes.

So, basically, I want an activities page. I want to keep it really simple; I also want it to be (reasonably) responsive, so people can attend events on their mobile phones. Here's the basic structure I have in mind:

Figure 40 - Activity Page
Now I know there are applications that probably do this for me. The problem is that they're proprietary. For example, the Google website will show chat comments, but only those entered into YouTube. And if I'm using one system for video it's hard to change half way through the course to support something different. But if I can define my own activity page, then I can do this.

The basic Activity Page is just some CSS without a header or footer, using the grid CSS definition used previously.

Figure 41 - Activity Page Grid
Then I added a mini-header to the Chatter pane:

Figure 42 - Activity Centre mini-header
I could have made that a box to make the layout cleaner, but I don't know if I'll ever reuse it, so there's no real point.

Next, I created three pages, one for each iframe in the ActivityCentre:
   - course_activity_video.htm
   - course-activity_slides.htm
   - course_activity_chatter.htm

These are just plain pages, but I'm free to define them however I want, and to reload them and publish them whenever I want. So there's only one URL for activities:

   https://el30.mooc.ca/course_activity_centre.htm

For the video page, I'll embed a YouTube live video (or archive video, if nothing is currently happening) using the standard YouTibe embed command (which is in fact an iFrame).

For the slides page I thought about using Slideshare, but I'd like to be able to advance slides automatically. So What I'll do is have this page auto-reload every few seconds, and use an iFrame to display a slide. So when I want to change a slide, I republish the page with the new slide, and then everybody will see the new slide when it reloads.

I'll do the same for the chatter page, except I'll reload an updated version of the most recent comments submitted to the course. More on that in a bit.

Figure 43 - Course Activity Slide page in the Page Editor

The meta http-equiv="Refresh" is what reloads the page each ten seconds.

For the chatter page I define the page in exactly the same way, except I load the latest chat contents into the page. Like this:

Figure 44 - Course Activity Chatter page in Page Editor

Each time the chatter page is published, it generates the most recent list of chat contributions. Viewers access the course_activity_chatter.htm page in their Activity Centre, and when this refreshes it shows the most recently published version of the page (but again, does not generate it from scratch, because that's too hard on the CPU).

So the people viewing the event through the viewer are always viewing cached versions of the activities; only the event administrator (that's me!) actually regenerates the pages (of course, I need to ensure that these pages block attempts by other people to regenerate them, because that will crash the server, which is what happened to me in Mississauga the other day).

Now there is, however, a problem. When I went to run the chatter page it displayed nothing because this is a brand new course and no chats have been created yet. But when I turned on the chatter in the gRSShopper PLE, it generated an error:

Figure 45 - Software Error?
That's not good. What happened?

When I created this version of a gRSShopper website I didn't create a 'channel' table. Chats are organized into channels; these channels can be used to create multiple events or chat streams using chat. When I went to create a new chat channel for the course, I got this error.

Just to verify, when I look at the list of forms, and the list of tables in the database manager in the Admin function, there is indeed no channel table.

Figure 46 - Channel table doesn't exist

I could rebuild it from scratch, but I'm lazy. So lazy...

I go to my gRSShopper PLE at www.downes.ca where I do have a channel table defined, and export it as a JSON file:

Figure 47 - Export Channel table as JSON
I save the exported file as channel.json on my computer in a location I can find again. Then I return to the course PLE and this time I'll import the data:

First, I add a new table clalled 'channel' so the can have some place to go. (After adding the table I need to reload gRSShopper because the new table doesn't automatically get added to the dropdowns - this is something I need to fix).

Figure 48 - Cteate channel table

Now I import the JSON file I just created:

Figure 49 - Import Channel Data

Now I cross my fingers and hope this works as intended, because I haven't used this in a while...

OK, that didn't work. Obviously something I need to fix (it was close - but it didn't actually save the fields and data properly).

So I try again using Tad-Delimited files (which for no particular reason I call .tsv (or Tab-Separated Values, I guess). Export and import work exactly the same, except the file is channel.tsv and the format is TSV.

And it still doesn't work. What should happen is that gRSShopper should create new fields as defined by the imput data, and then fill those fields with the data from the other site. But instead it's creating a bunch of numbered fields. So I have a small change I need to make in the code to fix this error.

But you can see what I want here, right? I want different instances of gRSShopper to be able to shared data with each other directly. Instead of downloading and saving the file, you should be able to save it directly from a JSON feed generated by the gRSShopper API. You might say - but this is dangerous! Sure - if you're doing it with strangers, and it's just random data. But you do it with friends, and the data is cleaned before it goes into the database. If if you think it's too dangerous, you don't need to use it.

Anyhow, I need channels to work for the Activity Centre to work, so I'll have to define the form and the table elements manually. *sigh* I will attend to the data exchange protocols in the future.

(time passes)

OK, I've crated the table manually and Chatter is now working. However I hit this error (it shows up in Chatter in my PLE):

Figure 50 - gRSShopper Channel Twitter Error
What's happening is that the channel wants to input data from both Twitter and Mastodon. I could turn that off (though I'm not sure how just now - I should really add a simple command right at the error message; something else to fix). But I don't want to. So how do we set this up?

In the right hand pane, in the Users-Options tab, you can set the account values for all your social networks (Facebook is legacy - the code stills supports Facebook, but it has been more than two years since I used it, and things may have changed).

Figure 51 - Social Network Accounts
As you can see, we need several values from Twitter: an Account Consumer Key, Consumer Secret,   
Token, and a Token Secret.

This takes us to one of my pet peeves. Twitter's tokens are defined to work with an app, as compared to Mastodon's (and most APIs), which are defined to work with a person (Google's API also works with an app, which makes it as cumbersome as Twitter).

I'm going to just try to use the values I previously generated for www.downes.ca - I don't expect this to work, because it's coming from a different website, el30.mooc.ca. But I'll try.

And it doesn't work. This is the new error it generates:

Figure 52 - Second Twitter Error

OK *sigh* it seems clear that Twitter is not sending back an access token when I make my connection request. So I'll have to do this from scratch.

Step One - Apply for a a Developer accopunt. Sign in to Twitter and go here:
https://developer.twitter.com/en/apply-for-access

So this is me filling in the forms: I'll associate this with my @Downes profile (my www.downes.ca website is associated with @OLDaily). I'm requesting for my own personal use (I don't have the authority to request on behalf of an organization). I'll give it the name 'gRSShopper'.

Now they want to know about my application. I'm interested in academic reserach and publishing and curating tweets.Here's what I said I'm building (it's a text area):

    I’m using Twitter’s APIs to connect my personal learning environment to twitter in order to make a Massive Open Online Course.
    I don't plan to analyze, except to search for a specific hashtag (I expect the API will do this for me).
    Yes, I will be Tweeting content when I'm responding to one of those tweets.
    Tweets will be displayed on my personal learning environment and in my MOOC during live events.
It asks, "Will your product, service, or analysis make Twitter content or derived information available to a government entity?" Well - nothing that isn't already available to those entities. So I'm going to answer "no" to this.

This all really bothers me. I just want to use my Twitter account in another application. Why is data locked down like Fort Knox?

I scroll through the developer agreement and click the box to signify I have read it. I submit my application.

Finally, "To complete your application, please check your inbox to confirm your email address."

And that's where I have to stop for the day. @OLDaily uses stephen@downes.ca and @Downes uses my work email address. So I have to wait until I'm in the office to read the email (this never used to be a problem, but NRC locked down it's email, making it inaccessible to me).

(time passes)

(next day)

I clicked on the link in the confirmation email and was taken to the getting started page. So on to Step Two.

Step Two: Create an App. Fill out the form.
- Name of Application: E-Learning 3.0 MOOC
- Application Description: gRSShopper PLE support for E-Learning 3.0 course.
- Application URL: https://el30.mooc.ca
- Allow Twitter Sign-In: Yes
- Call-back URL: https://el30.mooc.ca/cgi-bin/admin.cgi
- Terms of Service: https://el30.mooc.ca/terms_of_service.htm
- Privacy Policy: https://el30.mooc.ca/privacy.htm
- Organization Name: downes.ca
- Organization URL: https://www.downes.ca
- Tell us how this app will be used (required) This field is only visible to Twitter employees:
Hiya Twitter people. This application hosts a massive open online course (MOOC). The MOOC sends tweets and looks for tweets using the course tag. This is used to facilitate backchannel communication in the course. For more please see http://grsshopper.downes.ca for a detailed description of the application. 

I had to stop while filling this form to copy the privacy policy page from downes.ca, making sure I used , and to fill in the website name and publisher (tehse are defined in the Admin-General tab).

I click create and Twitter comes back with a terms of service message. I actually think it's pretty good (though I don't think it's really being enforced, which is too bad):

Figure 53: Twitter Developer Terms

Step 3 - Create Keys and tokens.

I'm now in the main application section. I click on the 'Keys and Tokens' tab. I can see the API key and API secret key. And I can generate an access token and access token secret. I now have the new version of the four things I need to post into my account form, so I go back to the Users-Options tab, and can set the account value.

And.... I'm still getting the same error. I don't know why.

Is it the Twitter API or is it the cchat.cgi script? I don't know, so I create a test post and try to publish it to Twitter. I get a response: Twitter result: Twitter turned off. I go back to the potions and turn 'Post to Twitter' On. Then I try a test post again. It still says Twitter turned off. So there must be something wrong with the way I created the new tables. I'm going to have to dive into PHPmyAdmin on Reclaim to figure this out.

Social network information is kept in the config table. The 'use account' option is a row where 'config_noun' = 'tw_post' and 'config_value' is 'yes'  (it's all a bit awkward but it's worked well up to now). I think what's happening is that my standard yes-no buttons in the form editor don't work for this type of data storage.

Hm. Trying to edit the item directly in PHPmyAdmin I see that the config table has no unique element defined, so I can't edit it. I fix that. Then I go back to the Users-Options tab and turn 'Post to Twitter' on. I try to publish to Twitter again and voila! That fixed it.

Back to the Chatter page and - bleah. Same error.

OK, here's the full txt of the error (it's also in Figure 52 above, but it's truncated in the image):

Must not be empty (in $args->{"access_token"}) at (eval 499) line 63
    "NonEmptyStr" is a subtype of "Str"
    "Str" is a subtype of "Value"
    "Value" is a subtype of "Defined"
    Undef did not pass type constraint "Defined" (in $args->{"access_token"})
    "Defined" is defined as: (defined($_))
For help, please send mail to the webmaster (webmaster@el30.mooc.ca), giving this error message and the time and date of the error.

This is pretty vague. It doesn't even tell me which script is complaining. But I know that Chatter is run by cchat.cgi, and this in turn calls the Net::Twitter::Lite::WithAPIv1_1 module. And it looks like $args->{"access_token"} doesn't have a value. I test and it does have a value. So I need to spend time doing some debugging. Back in a bit...

(time passes)

So I ran a couple of tests and it seems that everything in Twitter is working fine. Then I realize: it might be a problem with Mastodon. (I really need a way to turn Twitter and Mastodon on and off for the Chatter searches.) Well OK then. Mastodon is easy to fix.

OK, so back to the  right hand pane, in the Users-Options tab (Figure 51, above). Scroll down to the Mastodon config settings. Mastodon has a really easy API for getting your values, so easy I'll just build it right into the software at some point. It's all done in Javascript. But for now I provide a link in gRSShopper. Here it is:

Figure 54 - Mastodon API access token generator

Login to Mastodon before generating this form; it will request permission from you later. My Mastodon URL is https://mastodon.cocial, my client name is E-Learning 3.0 (or gRSShopper, or whatever), my web site is https://el30.mooc.ca and my scopes are 'read' and 'write'. I fill in the form, publish access-token, and paste the three values into the config settings and submit. That's Mastodon set up (there Twitter, that's how you do it).

OK, that eliminated the error. I made a test tweet and toot to see if it was working. It isn't posting them. I checked on downes.ca, changing the tag to el30 in an existing channel, and it worked just fine. So there's probably something wrong with the way it's trying to save the data. I checked the database and the chats are coming in. So the chat view must not be there. But they all seem to be there. Maybe the chats aren't associating with the channel properly.

Yup. That's what's happening. Each chat row has a chat_channel field, which should be the ID number of the channel. But this isn't being captured. So the chats aren't being displayed. I'm going to try deleting the channel and recreating it; maybe it will load properly now that everything is set up. I'll also deleted all the harvest chats (there were only 10 or so).

Nope. Didn't work. Why does it work on downes.ca but not on el30.mooc.ca ??

Found it. Tables were slightly different; elso was using an older version of the table. I had renamed chat_thread to chat_channel.

That did it. It's harvesting properly. Yay!

OK, now to make it update in the activity centre. My course activity chatter (Figure 44, above) should be set to go as it. But I'll test it just in case.... yes, it displays the chat posts beautifully, and it updates every ten seconds.

It's just updating a plain HTML page (that way hundreds of people can watch it without killing the server). But I need to republish that HTML page whenever a new chat comes in. This will take a minor edit to the Perl cote in cchat.cgi (actual elapsed time to write and test the code below: 15 minutes or less).

First, a shot function to republish the page:

Figure 55 - Publish Activity Chat Function

Now you will never have to do this, since it's not a part of gRSShopper. I'm just showing you in case you're curious.

Note that the page 'activity_page.htm' is hard coded in there. That's bad practice. What I'll do to fix it in the future is create an option in the Activity Centre Control Panel so you can set any page as your chat page.

Now I need to call the function. I only want to call it when I get a new comment added. So I'll put it here:

Figure 56 - Trigger Publish Function with new Chat

OK, that's it. Quick test and - yes. Works perfectly.

So, I also spent some time getting ready to do Google Live Hangouts. I was using xSplit but it was bogging my computer down. So I think I just want to use Hangouts. I looked it up online and got the following instructions:

Figure 57 - Google Live Hangout Instructions
This took me a half hour to work out so it shows nicely in the Activity Centre. The URL in the instructions is https://www.youtube.com/my_live_events and everything can be done from there. Note that the URL Google gives you for the embed link is 'http' but you must use 'https' because otherwise it won't play (mixed secure and non-secure content types).

That leaves just the slides. I could just throw a SlideShare embed in there, and that's my short-term fix. But I want to set it up so viewers don't need to advance their own slides, which is a bit trickier.

Well that's it for now; time to publish this article and call it a day.






Comments

Popular Posts