tag:blogger.com,1999:blog-72326788212803764792024-03-13T11:58:11.348-07:00Will BillingsleyI teach technology design (particularly, software engineering, human-computer interaction, Scala, mobile, and web development) at the University of New England. I do research in how we can design smart useful systems and make sure that reasoning machines aren't unreasonable machines. Especially in technology education and education technology. I also re-invent far too many of my own wheels.William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.comBlogger84125tag:blogger.com,1999:blog-7232678821280376479.post-88633281818008694512020-01-01T21:53:00.001-08:002020-01-01T21:53:03.336-08:00New Year's TentationsHappy New Year. It's 2020. The year at the heart of so many "2020 vision" plans.<br />
<br />
On the last day of last year, I hastily scribbled down my new year's resolutions. This time, I tried to keep it down to a few I might reasonably actually do. But what of the ones I'm really <i>not so</i> committed to?<br />
<br />
Blogging a little more (to keep in the habit of writing) is something I'd kind of like to do, but not if it turns out to drain too much time from the things I do need to do. Especially as many of my blog posts are "thinking aloud" - thoughts I'm not especially committed to but seemed worth exploring (or at least getting out of my head) by writing down.<br />
<br />
What do we call those little things we <i>don't</i> expect to stick to like glue but would like to try?<br />
<br />
So, after thinking about antonyms for "resolute" I decided "tentative" seemed the most appropriate. And it so happens that "tentations" has a meaning too.<br />
<br />
Apparently, in 1877, the Astronomer Royal George Airy had a method for adjusting ship's compasses that involved experimentally placing boxes of iron chain and magnets, and adjusting them until they cancelled out the magnetic influence of the ship's hull. (via the Practical Dictionary of Mechanics 1877, via the OED).<br />
<br />
So, in honour of the tentative nature of the commitment (and George Airy's experimental method), one of my New Year's <i>Tentations</i> is to blog a little more.<br />
<br />
Even if what I'm putting out there isn't much more sophisticated than hulking iron chains (and with much less direction than a ship's compass!)<br />
<br />
<br />
<br />
<br />
<br />
<br />William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-29534101297792170932019-03-26T04:21:00.002-07:002019-03-26T04:21:39.536-07:00CompromiseOk, I'm getting a bit cynical on this whole Brexit debate...
<br />
<br />
<i>Once upon a time there was a family that happened to run a country.</i><br />
<i><br /></i>
<i>"Let's go for a walk along the north bank of the river. It's just down the road," said David. </i><br />
<i>"No", said Boris, "Let's cross the river and go for a walk along the south bank."</i><br />
<i>"I insist we compromise," said Theresa, "and walk down the middle."</i><br />
<i><br /></i>
<i>Two years later, having discovered that the compromise plan did not involve walking on top of the water after all, the country looked bemusedly at the assorted wetsuits, oxygen tanks, and weighted shoes they were being asked to wear to keep themselves on the firm bed of the river for their walk.</i><br />
<i><br /></i>
<i>"Theresa," piped up one from the back, "I'm wondering if this might not be the best idea? I don't even have a diving mask and won't be able to see underwater."</i><br />
<i><br /></i>
<i>"Oh for goodness sakes," replied Theresa. "You said you wanted to go for a walk, so we're going. Stop shilly-shallying, jump in, and I promise Michel will turn on your oxygen tank after you're underwater."</i>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-57501561484983470782019-03-22T18:24:00.000-07:002019-03-22T22:05:50.529-07:00The Pogo Stick Strategy and other storiesThis isn't meant to take a position on whether the UK should remain or leave (I live in Australia - it's pretty easy for me to say remain), or the merits of Brighton versus Skegness. More of a fable about political shenanigans that can run the debate off the road.<br />
<br />
<i>Once upon a time, there was an office outing. Some people in the office wanted to go to Brighton, and some people wanted to go to Skegness. The manager said he really doesn't like Brighton, but he'll put it to a vote. He lost and said he couldn't really organise a trip somewhere he doesn't want to go.</i><br /><br />
<i>"Who's going to lead us to Brighton?" the office staff asked. The Skegnessers scowled across the room at the Brightonites, still angry at the tone they'd taken in the debate.</i><br /><br />
<i>"I'll lead us to Brighton," said one of the directors, who'd originally wanted to go to Skegness, "so long as you let me decide how we get there."</i></br /></br />
<i>The office murmured a bit, but as it sounded like a good compromise to let her lead them to Brighton they agreed.</i><br /><br />
<i>"Right," said the director, pulling out a bullwhip and opening a cupboard. "Everyone take a pogo stick and lets get hopping down the road. No arguments at the back."</i><br /><br />
<i>Two miles down the road, amidst a crowd of scraped knees, twisted ankles, and battered pogo sticks, one office staff member turns to the other and says "I told you going to Brighton was a bad idea."</i>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-57116768995228312142018-04-14T20:29:00.002-07:002018-04-14T20:30:29.372-07:00JavaFX out of Java 11<p>In somewhat recent news Oracle <a href="https://blogs.oracle.com/java-platform-group/the-future-of-javafx-and-other-java-client-roadmap-updates">has moved JavaFX out of the standard Java install from Java 11</a>. There is a certain fun that Donald Smith describes this as <i>"making JavaFX easier to adopt by making the technology available as a separate download"</i> — don't you always find having to download two things instead of one makes things easier... but that's not really what I want to chat about here.
</p>
<p>
I teach a couple of university courses that have usually used JavaFX: one in distributed software development, and another in functional programming in Scala. Not because we're particularly teaching UI programming, but because there are plenty of occasions where you're <i>not</i> teaching UI programming but still want students to be able to put something on the screen easily. It's in that context I'd like to say a little about JavaFX — the good, the bad, and the ugly.
</p>
<p>
I should perhaps also mention that I think that realm — just being so much easier — was Java's best route to being a relevant client technology again. <i>I have this thing I need to knock up, do I set up npm, bower, etc, to do it in Electron -- nah, it's just a small thing at the moment, I'll just whip it up in JavaFX</i>. And then just having a smooth progression where there just isn't a point where it feels uncomfortable to write your program in this toolkit. This is also where JavaFX didn't quite get it right.
</p>
<p>
Ok, enough rabbiting about the space I'd like Java UIs to sit in. How did JavaFX fare in my experience?
</p>
<h2>
The good</h2>
<p>
The scene graph in JavaFX managed simplicity very well. I have a fairly common little demo that I write live in front of students for showing the concept of threads (as their Java unit doesn't always cover it). I put a spinning rectangle above a button, and have the button's action listener put the thread to sleep for 10 seconds. Instant frozen rectangle. Then we move the task onto a background thread, and the UI doesn't freeze. Five minutes, about twenty lines of code, gets the point across pretty well.
</p>
<p>
Hidden in there, of course, I'm doing some things Swing never really let you do. Mixing a shape primitive in a scene graph with controls (buttons). And as the rectangle <i>spins</i> its dimensions change but I don't have to worry about its drawing being clipped by its bounds. And the VBox I put them in just goes into the component graph, I don't have to deal with setting layout managers.
</p>
<h2>
The bad</h2>
<p>There's two places I think JavaFX fell down:</p>
<ul>
<li>
Both times they created it (the old JavaFX 1 in JavaFX Script, and JavaFX 2), they had far too particular a model for how you'd do your UI. JavaFX 1 dictated what language you worked in. JavaFX 2 didn't, but see below with the observable lists, it very much packaged the programming model into the controls. It didn't have the neat separation that HTML5 does -- that the scene graph just does displaying the scene graph, and you can chop and change different frameworks for how you tie your data model to that.
</li>
<li>
It tried to be too deep. Style your UI with CSS! Underlying every control there's a complex region model that lets you style multiple regions in every control! While this looked fun if you were really very interested in JavaFX, it meant that if you opened up the API documentation <i>there was just so much of it</i>. Separating what you could ignore from what you couldn't became a task for the reader, and it just looked scarily big for students that just wanted to whack some buttons, a canvas, and a graph on the screen.
</li>
</ul>
<h2>
The ugly</h2>
<p>
The part my students really found awkward was the controls. In 2017, I set the Scala students an assignment of writing a particle swarm optimiser. So that they could see it working, I also asked them to show various properties of the swarm at each frame of the simulation. That was supposed to be just a little prod in the assignment so students could easily see what was happening -- but it turned out they found writing the UI much harder than writing the particle swarm. Let's just highlight that for a moment:
</p>
<blockquote>
A functional programming assignment, in Scala (a famously large language for students to get to grips with), and it turns out the fiddliest part is getting the UI to show some simple graphs on-screen. Hmm...</blockquote>
<p>
I think the issue there was that JavaFX's controls model is quite so deeply married to its observable lists. For students who are quite happy calling <i>map</i> on a sequence of values, to update them in one fell swoop, having to deal with code that says:
</p>
<pre>series.getData().add(new XYChart.Data(1, 23));</pre>
<p>
starts to looks weird. <i>No, I don't want to get the data, I want to add a datapoint...</i>. It's not that it's a lot of code, it's just that JavaFX imposes an opinionated mental model for how data in a UI should be updated, and if that's different to how you write the rest of your code then it's asking you to jump mindsets when you touch the UI parts of your code.
</p>
<h2>
Where too next?</h2>
<p>
From outside Oracle, it does look like JavaFX has been put out to pasture. I used to have a cynical saying that when companies and governments fund projects, one of the things they really like about open source is that "we've released it to the open source community" sounds so much more successful end to a project than "we've dropped it like a stone".
</p>
<p>
Which is unfortunate, as in reverting to just supporting Swing until 2026, we've lost the simpler parts of JavaFX. The scene graph that just let you chuck shapes into VBoxes. Letting you paint outside your control's bounds if you want, so you can do things like have a row of buttons, but highlight one for some help text by circling it or making it wiggle. The slightly neater animation classes too. We've fallen back in time ten years to when the toolkit still got in the way for the simple but visually interesting stuff.
</p>
<p>
So what I'd like to see happen with JavaFX is for it to be modularised, rather than it being a big fat module put out to grass. For Oracle to back the scene graph to the hilt, and make sure you can add one dependency in gradle, and you'll get a really simple toolkit for putting together attractive little UIs, with the elementary controls such as buttons and text fields. Put the complex controls out to libraries, but putting some buttons and a canvas on screen, and making a button do a little dance so you can draw attention to it shouldn't be hard.
</p>
<p>
And, as seems to work for html, the frameworks can deal with different ways of tying data to code. The react-like ones that let your code appear relatively pure, and then do a diff on what's in the code. Or the reactive ones that treat every property like an observable that automatically updates its controls when you do. Or the d3.js like ones that treat it as sets of data that are new, changed, or removed. etc.
</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-28852101402873074342013-09-10T00:08:00.001-07:002013-09-10T00:36:41.750-07:00Building an assessment app in 2+ɛ days -- dealing with Heroku's slow Scala build times<p>(This post is retrospective -- students started using it yesterday.)</p>
<p>While Impressory is just running on an AWS instance, I put Assessory for our class on <a href="http://heroku.com" title="Heroku">Heroku</a> so that it would get an https address immediately *, and there wouldn't be a delay while I waited for DNS settings for a new domain name to propagate.</p>
<p>Heroku is a Platform-as-a-Service provider that uses a push to deploy mechanism. Add Heroku as a remote to the git repository, and then…</p>
<pre><code>git push heroku master
</code></pre>
<p>…and Heroku will build and deploy your code.</p>
<p>This works for a number of platforms, including Scala Play apps such as this one.</p>
<p>In theory.</p>
<p>In practice, Heroku can be very slow building a Scala Play app, even one as simple as this, and it would regularly take longer than the 15 minute maximum that Heroku allows. In which case, Heroku would reject the push, and even though the update might only have been seconds away from going live, I'd be frustratingly bunted back to starting the deployment again.</p>
<p>The modular nature of the app, while great for keeping the code tidy, also seemed to slow down Heroku's builds as it has to go through an update cycle (resolving dependencies) for each of the modules as it compiles them. These seem to take a bit of time on Heroku.</p>
<h2>Avoiding Heroku's long build times</h2>
<p>The short answer to avoiding Heroku taking an age to compile an app (and often having to compile the compiler interface before it starts), it turns out, is this: don't let it compile it at all.</p>
<p>There's two ways of "not letting it".</p>
<ol>
<li><p>Apparently there's an alternative build mechanism for Heroku called <a href="https://github.com/ddollar/heroku-anvil">Anvil</a>.</p>
<p>This uses Heroku's build packs on some other AWS servers. It seems to get around the timeout but still takes some time.</p>
<p>To be honest, I didn't get time to look at it until later, as I needed to get an update up quickly. When I did try it out, while writing this post, it did work -- though it took its merry time too.</p></li>
<li><p>The other is a hacky little workaround that involves almost no set-up, and is very much faster.</p>
<p> Don't push the Play app itself to Heroku. Instead, publish it as a JAR file to a Maven or Ivy repository. Play apps can be published as JAR files just by running this from sbt:</p>
<pre><code>+ publish
</code></pre>
<p>This tends to be very fast because you've already compiled your code locally before you decide you want to upload it. (And your development machine is probably quicker than the AWS machines that Heroku builds on.)</p>
<p>Then we create a second, essentially empty Play app that has our real app as its only dependency. We're treating our app as a library that's used by a trivial wrapper app. (We include only a very few files that we need to be a valid Play app: application.conf, plugins.sbt, build.properties. Perhaps one or two others, but they are straight copies of the files in our "real" app.)</p>
<p>We push our wrapper app to Heroku,
and Heroku will happily fetch our application code -- already compiled and packaged -- from the repository when it does its dependency resolution, as it would any other library. HTTP calls to the almost-empty outer app be served by the code in our JAR file -- including requests for the minified Javascript. Bingo, our app is up and running. Deployments take less than a minute because there is nothing for Heroku to compile.</p>
<p> If you're not keen on pushing your code to a public Maven or Ivy repository, then you can push it <a href="https://devcenter.heroku.com/articles/local-maven-dependencies">to a local repository that you include in the almost-empty Heroku app</a>.</p></li>
</ol>
<p>This second approach feels like cheating, but in practice the only downside I've noticed so far is that public assets (images) from our "real" app would now be served out of the JAR file -- which is slower than serving them straight from a file if the app wasn't packaged up as library.</p>
<p>But in Assessory there are only three images that get served anyway -- the cartoon drawings on the NotFound, Forbidden, and InternalServerError screens. (And as it happens I messed up the URL so they're not appearing in the dependent-JAR-file version because of a double // in the path. The image below is from the un-JARed version in a previous post.)</p>
<p><figure class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-zdAsgg3niT8/UiaTgfF2apI/AAAAAAAABFM/YF6u-Lm_r-I/s1600/Screen+Shot+2013-09-04+at+11.26.59+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-zdAsgg3niT8/UiaTgfF2apI/AAAAAAAABFM/YF6u-Lm_r-I/s320/Screen+Shot+2013-09-04+at+11.26.59+AM.png" /></a></p>
<p><figcaption>This is one of the only pages in the app with an image</figcaption>
</figure></p>
<p>So I think for a while, I'm just going to put my few static images up somewhere else on the web -- perhaps in <a href="http://cloudinary.com">Cloudinary</a>, and keep using this dependent app trick to make Assessory deployments fast.</p>
<p>* <em>If a user accesses a site using http rather than https on a passwordless WiFi network (such as UQ's visitor WiFi), then it's possible to intercept their session key over the air. This is fairly well-known, and it's why Facebook, Google and others have moved to https only. For Assessory, where students are marking each other, I'd like to ensure that an https URL is available.</em></p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-18321555395352550022013-09-09T23:55:00.000-07:002013-09-09T23:55:26.475-07:00Building an assessment app in 2+ɛ days -- students started using it yesterday. (Going retrospective)<p>Students started using the app to critique each others' projects yesterday, as planned. Though I hadn't done a demo in the previous lecture as I'd hoped. So it wasn't two days, but it got there in time to be useful.</p>
<p>The screenshot below is from the form for <em>editing</em> the questionnaire -- as I was struggling to find screenshots that wouldn't reveal student data I should keep hidden.</p>
<p>For instance, if I clicked on "Allocations" I'd get a neet little list of which students are allocated to review which groups, whether they've logged in and linked their GitHub accounts, and which reviews they've started writing. But I don't want students knowing who is reviewing them, so I can't publish a picture of that to the web!</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-wY_lWc0UvMg/Ui66kGxdYdI/AAAAAAAABGo/5njdA2uWFWI/s1600/Screen+Shot+2013-09-10+at+4.21.32+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-wY_lWc0UvMg/Ui66kGxdYdI/AAAAAAAABGo/5njdA2uWFWI/s320/Screen+Shot+2013-09-10+at+4.21.32+PM.png" /></a></div>
<p>I guess another one I can show you is this:</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-AiwilaXPkUM/Ui68GhJO_jI/AAAAAAAABG0/nSSxGao_Vsg/s1600/Screen+Shot+2013-09-10+at+4.27.26+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-AiwilaXPkUM/Ui68GhJO_jI/AAAAAAAABG0/nSSxGao_Vsg/s320/Screen+Shot+2013-09-10+at+4.27.26+PM.png" /></a></div>
<p>Those pictures down the bottom are the GitHub avatars of users in those groups who have logged in (and the pre-enrol system has spotted them and automatically added them to their groups). The pictures are funny blocky images because these ones have been generated by GitHub for users who haven't set their avatar picture.</p>
<p>Most of the groups appear to be empty. This just means I took the picture less than a day after advertising the app to students. The pre-enrol system means that students are automatically added into their groups when they visit the course page. When I took the picture, 23 students had already logged in, but I cropped the image just before the first student who had uploaded an avatar (to avoid publishing people's photos or drawings on my blog.)</p>
<h2>Going retrospective</h2>
<p>Anyway, the next few posts will be retrospective -- looking back on the app that's been built rather than blogging as I go.</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-35285112683839119702013-09-06T02:44:00.002-07:002013-09-06T02:44:33.037-07:00Building an assessment app in 2+ɛ days -- refining the concepts<p>The <a href="https://github.com/impressory/assessory/commit/c692c59209bf22f34963751f2f462c9d2089c8d2">nineteenth commit</a> is up, but it's high time I started discussing the design of the app itself.</p>
<p>(I think I've been through all the technical odds and ends. There's also support for server-sent events and websockets, but I'm not putting that in this app.)</p>
<h2>Screenshot at latest commit</h2>
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-fPXd_FtTW8A/UimgNPBSLuI/AAAAAAAABGY/xm8uSdJAj7A/s1600/Screen+Shot+2013-09-06+at+7.27.04+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-fPXd_FtTW8A/UimgNPBSLuI/AAAAAAAABGY/xm8uSdJAj7A/s320/Screen+Shot+2013-09-06+at+7.27.04+PM.png" /></a></div>
<p>Trying to keep it simple, the app centres around Groups and Tasks in a course. So that's what students will see on the course's Assessory page.</p>
<p>It turns out there are some interesting relationships between tasks and groups.</p>
<h2>The first task</h2>
<p>The first task I'm going to need to write is the group peer critique task.</p>
<p>Students are in two kinds of group:</p>
<ul>
<li><p>A Tutorial Group</p>
<p>There are two tutorial sessions, with approximately half the <br/>
class in each</p></li>
<li><p>A Project Group</p>
<p>Each project group has 3 to 5 students in it</p></li>
</ul>
<p>(These categories correspond to "Group Sets" in Assessory.)</p>
<p>Groups are going to be presenting their work in the tutorial on Monday. That means that the critique task has to care about both group sets -- it has to allocate each student to review another project from the same tutorial.</p>
<p>If it allocated them the same project group, well you can't assess yourself; and if it allocated them a group from the other tutorial, they wouldn't be there to see the presentation.</p>
<h2>The second task</h2>
<p>When students critique each other's work, they also get to critique the critique.</p>
<p>The second task we want is for each student to read the critiques their group has received, and mark whether or not they were constructive and useful.</p>
<h2>So back to it...</h2>
<p>So, now all the course and group pre-enrolments are in and working, it's time to get these tasks written.</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-56156056089435270802013-09-05T22:56:00.000-07:002013-09-05T22:56:58.334-07:00Building an assessment app in 2+ɛ days -- 16th commit<p>Right, back to it then… After the fun of the CEO's visit this morning, (and a big long sleep last night) back to work on the assessment app.</p>
<p>The students are going to be using it on Monday, so that's a deadline I can't let whoosh past me, as it's not just me making up a deadline for myself.</p>
<h2>User updates in a functional world</h2>
<p>The <a href="https://github.com/impressory/assessory/commit/1d52a3b589d7962dba24d67617ccfe9031160e8e">commit I just pushed</a> changes course pre-enrolments so they happen automatically when the browser asks for the user's courses.</p>
<p>That means this is a request that changes something about the user -- it changes the user's registration.</p>
<p>I've written the app in a way that is functional and typically works with immutable data. That means there's one small additional wrinkle in this request.</p>
<p><code>DataAction.returning.one</code> automatically converts an item to JSON for the requesting user. It uses an <code>Approval[User]</code> that typically has a <code>LazyId</code> for the user, fetched the first time it's needed.</p>
<p>But, I happen to have written this particular app in a functional style -- with immutable data objects. <em>(You don't have to write your app with immutable data types, I just did for this one.)</em> And this request modifies the user.</p>
<p>If your data types are immutable, you can find yourself with a small bug where this happens:</p>
<ol>
<li><p>We ask for the user, because to look up any pre-enrolments, we
need a list of their social identities</p></li>
<li><p>This triggers the lazy reference to the user to load.</p></li>
<li><p>We find a pre-enrolment in the database, and update the user's
registrations.</p></li>
<li><p>We return the course from <code>DataAction.returning.one</code></p></li>
<li><p>But the <code>Approval</code> in the request has an immutable
representation of the user that was fetched <em>before</em> we
registered them to the new course, and the JSON comes out as if
they weren't registered.</p></li>
</ol>
<p>The solution involves a change to one line of code, and the addition of two more:</p>
<ol>
<li><p>Change the method from <code>DataAction.returning.one</code> to
<code>DataAction.returning.json</code></p>
<p>(or from <code>DataAction.returning.many</code> to <code>DataAction.returning.manyJson</code>)</p></li>
<li><p>Create a new approval for the updated user.</p></li>
<li><p>Call the JsonConverter with the new Approval</p></li>
</ol>
<p>We can see this in <code>CourseController.myCourses</code></p>
<pre><code>def myCourses = DataAction.returning.manyJson
{ implicit request =>
val userAfterUpdates = for (
u <- request.user;
updated <- doPreenrolments(u)
) yield updated
// As we've updated the user, we'll need a new Approval
val approval = Approval(userAfterUpdates)
</code></pre>
<p>And at the end of the method:</p>
<pre><code> approved <- approval ask Permissions.ViewCourse(c.itself);
j <- CourseToJson.toJsonFor(c, approval)
) yield j
</code></pre>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-80918119458490176262013-09-04T21:05:00.002-07:002013-09-04T21:05:50.031-07:00Building an assessment app in 2+ɛ days -- 13th commit<p>
This is a little picture of where the app is up to at the moment. (This is the admin screen for a course.) After the lecture this morning, and a little more coding, I'm going to pop home and take a kip, and resume updates a little later.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-Q411M4noHrE/UigA5P-lUYI/AAAAAAAABGI/geVTAUqskV0/s1600/Screen+Shot+2013-09-05+at+1.55.04+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-Q411M4noHrE/UigA5P-lUYI/AAAAAAAABGI/geVTAUqskV0/s320/Screen+Shot+2013-09-05+at+1.55.04+PM.png" /></a></div>
<p>
The last few commits haven't been especially interesting to blog about -- just adding more of the DAO classes, services, and controllers, in much the same style as the previous ones.
</p>
<p>
However, I have been making a few design decisions for how the app will behave that might make interesting reading later on today.
</p>
William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-7945164844594847852013-09-04T14:33:00.000-07:002013-09-04T14:33:06.610-07:00Is that a whooshing noise?<p>A couple more commits have gone in, though I haven't blogged them.</p>
<p>Course pre-enrolments happen, but I haven't yet set up group pre-enrolments or the critique task itself. Those will need to happen later today instead.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://3.bp.blogspot.com/-1dkTK70zz3w/Uiemy0RDVKI/AAAAAAAABF4/aWOoL5L0czM/s1600/Screen+Shot+2013-09-05+at+7.31.28+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-1dkTK70zz3w/Uiemy0RDVKI/AAAAAAAABF4/aWOoL5L0czM/s320/Screen+Shot+2013-09-05+at+7.31.28+AM.png" /></a></div>
<p>So, it looks like it'll need to be an assessment app in three days.</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-20383550748266990802013-09-04T10:03:00.000-07:002013-09-04T10:03:05.353-07:00Building an assessment app in two days -- 8th commit <p>A few yawns are creeping in here, it's getting late…</p>
<p>The <a href="https://github.com/impressory/assessory/commit/3f0fed00f42b9a2eeac543532e2c3f69d93ba715">eighth commit</a> is up, and now we can create courses. The interesting part of this commit, however is security.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-LHJA7LOkV2c/UidfarB1Z6I/AAAAAAAABFo/TWGDZ5zWdsA/s1600/Screen+Shot+2013-09-05+at+2.23.44+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-LHJA7LOkV2c/UidfarB1Z6I/AAAAAAAABFo/TWGDZ5zWdsA/s320/Screen+Shot+2013-09-05+at+2.23.44+AM.png" /></a></div>
<h2>Security in Assessory</h2>
<p>If you have a look in <a href="https://github.com/impressory/assessory/blob/3f0fed00f42b9a2eeac543532e2c3f69d93ba715/app/com/assessory/play/controllers/CourseController.scala">CourseController</a>, you'll see controllers that look like this:</p>
<pre><code>/**
* Retrieves a course
*/
def get(id:String) = dataAction.one {
implicit request =>
val cache = request.approval.cache
for (
course <- cache(refCourse(id));
approved <- request.approval ask
Permissions.ViewCourse(course.itself)
) yield course
}
</code></pre>
<p>The permissions check is chained right there in the for loop (which is syntactic sugar for chaining <code>flatMap</code> calls on the <code>Ref</code>s)</p>
<p>The way of thinking about it is that at any stage you can ask for approval to do something. That approval might be given; it might take some time to work out (involve looking something up in the database) and it might fail or be refused. All those fit neatly into the functionality of <code>Ref</code>, so we treat is asking for a <code>Ref[Approved]</code>.</p>
<p>This also means it's independent of the database or Play classes, and I've declared the permission rules in the <code>assessory-api</code> module.</p>
<h2>Security is in the API</h2>
<p>If you look in <a href="https://github.com/impressory/assessory/blob/3f0fed00f42b9a2eeac543532e2c3f69d93ba715/modules/api/src/main/scala/com/assessory/api/Permissions.scala">Permissions</a>, you can see the different permissions that an <code>Approval[User]</code> can ask to be approved.</p>
<p>Sometimes these are straightforward objects:</p>
<pre><code>case object CreateCourse extends Perm[User] {
def resolve(prior:Approval[User]) = {
Approved("Anyone may create a course")
}
}
</code></pre>
<p>And sometimes they are approvals on an item:</p>
<pre><code> case class ViewCourse(course:Ref[Course])
extends PermOnIdRef[User, Course](course)
{
def resolve(prior:Approval[User]) =
hasRole(
course, prior.who,
CourseRole.student, prior.cache
)
}
</code></pre>
<p>Approvals on an item (<code>PermOnIdRef</code>) are clever enough to realise that if you ask for an approval on <code>Course(id=1).itself</code>, and you ask for an approval on <code>LazyId(classOf[Course], "1")</code>, those are the same approval and it doesn't need to look up the ID the second time.</p>
<p>And it can do that independently of what kind of database you've wired up, or whether or not it's in a Play app.</p>
<h2>Cache</h2>
<p>As well as remembering granted approvals, <code>Approval</code> also contains a cache for <code>Ref</code> lookups.</p>
<p>The <code>Approval</code> tends to be present in all three of the controller, the security check, and the JSON conversion -- and these are the three places where you typically need to look up <code>Ref</code>s. So having a cache attached to it is rather handy.</p>
<h2>JSON conversion is seemless with security too</h2>
<p>All this flatMapping on <code>Ref</code>s has another payoff in the JSON conversion.</p>
<p>If you have a look at <a href="https://github.com/impressory/assessory/blob/3f0fed00f42b9a2eeac543532e2c3f69d93ba715/app/com/assessory/play/json/CourseToJson.scala">CourseToJSON</a>, you'll see that it embeds a permissions block into the JSON it returns</p>
<pre><code>def toJsonFor(c:Course, a:Approval[User]) = {
val permissions = for (
view <- optionally(
a ask Permissions.EditCourse(c.itself)
);
edit <- optionally(
a ask Permissions.EditCourse(c.itself)
)
) yield Json.obj(
"view" -> view.isDefined,
"edit" -> edit.isDefined
)
for (p <- permissions) yield {
courseFormat.writes(c) ++
Json.obj("permissions" -> p)
}
}
</code></pre>
<p>This happens asynchronously -- the user reference in the approval is asynchronous and may or may not already have been retrieved.</p>
<p>The JSON block that this produces ends up looking like something this:</p>
<pre><code>{
"id":"52275e6a9acb3a4500d7c2ea",
"title":"Design Computing Studio 2",
"shortName":"DECO2800",
"shortDescription":"…",
"addedBy":"522732099acb3a4d00fedff9",
"created":1378311786259,
"permissions": {
"view":true,
"edit":true
}
}
</code></pre>
<p>Because the permissions block is in the item, on the client it is easy to enable and disable components using Angular.js.</p>
<p>Say, for instance, we might have an edit link that only shows if the edit permission is present:</p>
<pre><code><a href="edit" ng-show="course.permissions.edit">
Edit
</a>
</code></pre>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-38168767743380991182013-09-04T06:40:00.000-07:002013-09-04T06:40:40.990-07:00Building an assessment app in two days -- 7th commit<p>After another long delay, the <a href="https://github.com/impressory/assessory/commit/84ac60782b846e4795e7d33cd5d9e9ecb630be61">seventh commit</a> is up. This is the last of the plumbing commits, and adds OAuth login.</p>
<p><a href="http://3.bp.blogspot.com/-gaXRFrgGYVA/Uic1hnNFWLI/AAAAAAAABFY/cvVdusFj9VE/s1600/Screen+Shot+2013-09-04+at+11.25.27+PM.png" imageanchor="1" ><img border="0" src="http://3.bp.blogspot.com/-gaXRFrgGYVA/Uic1hnNFWLI/AAAAAAAABFY/cvVdusFj9VE/s320/Screen+Shot+2013-09-04+at+11.25.27+PM.png" /></a></p>
<p>The OAuth authentication is handled by <a href="https://github.com/wbillingsley/handy-play-oauth">handy-play-oauth</a>, which is about the smallest possible OAuth library. It doesn't itself do user management or any of that. All it does is:</p>
<ol>
<li><p>Forward the request to the relevant authentication service
(in this case, GitHub's OAuth URL)</p></li>
<li><p>Extract the returned OAuth repsonse</p></li>
<li><p>Use the authentication token to call the service's REST API
to get the user's details</p></li>
<li><p>Call whatever action you've configured</p></li>
</ol>
<p>In this case, the action I've configured is <a href="https://github.com/impressory/assessory/blob/84ac60782b846e4795e7d33cd5d9e9ecb630be61/app/com/assessory/auth/controllers/InterstitialController.scala">InterstitialController.onAuth</a>. It either logs you in (if you've already set up your account), or shows an "interstitial" confirmation page if you haven't.</p>
<p>The reason for the confirmation page is that you can attach multiple identities to your account. So before Assessory creates a new account for you, it asks you to confirm that's what you want to do -- in case what you really wanted to do was add the login to an existing account.</p>
<p><em>(The long delay was me having dinner and getting over a migraine, by the way. Plus staring for far too long at a stupidly trivial bug while my head ached.)</em></p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-23673498397603413592013-09-04T02:04:00.000-07:002013-09-04T02:04:44.532-07:00Building an assessment app in two days -- 6th commit<p>After a long pause while I fixed that bug, back to getting the app up and running.</p>
<p>The <a href="http://github.com/impressory/assessory/commit/59a97ce4cab8fe47fb23f145bf3d9313e186cebe">sixth commit</a> has basic sign-in, sign-up, and sign-out functionality working, which means we have our first proper controller on the server and our first proper service on the client.</p>
<p>Earlier I said I wasn't going put in email/password log in. Well, I then realised I would need to.</p>
<p>Later on, I'm going to need to try out creating a course (as staff) and then preenrolling a different user (as a student). That means I need two accounts, but I only have one GitHub log in.</p>
<h2>Handling users on the server</h2>
<p><a href="http://github.com/impressory/assessory/blob/59a97ce4cab8fe47fb23f145bf3d9313e186cebe/app/com/assessory/play/controllers/UserController.scala">UserController</a> contains the various concise actions that we've defined on the server:</p>
<ul>
<li><code>self</code> to return JSON for the currenlty logged in user</li>
<li><code>signUp</code> to sign up</li>
<li><code>logIn</code> to log in</li>
<li><code>logOut</code> to log out.</li>
</ul>
<p>At the top of the controller, there is the interesting line</p>
<pre><code>implicit val userToJson = UserToJson
</code></pre>
<p>This is used to convert the responses to JSON format.</p>
<p>If you then have a look at <a href="http://github.com/impressory/assessory/blob/59a97ce4cab8fe47fb23f145bf3d9313e186cebe/app/com/assessory/play/json/UserToJson.scala">UserToJson</a>, you'll see it's a slightly different strategy for converting to JSON than most libraries. The key function is:</p>
<pre><code>def toJsonFor(u:User, a:Approval[User]) = {
// etc
</code></pre>
<p>This is from recognising that very often we want to give different information depending on who's asking. For instance, in this case we only want to give out the JSON data for
the user if they are asking about themselves, not someone else.</p>
<p>And, it returns a <code>Ref[JsValue]</code> rather than just a <code>JsValue</code>, in case it has any other work it needs to do before it can return a result.</p>
<h2>Handling users on the client</h2>
<p><a href="https://github.com/impressory/assessory/blob/59a97ce4cab8fe47fb23f145bf3d9313e186cebe/app/assets/javascripts/services/UserService.coffee">UserService</a> is the corresponding component on the client. Again, it has <code>self</code>, <code>signUp</code>, <code>logIn</code>, and <code>logOut</code> actions which are all neatly concise.</p>
<p>Angular.js has built into it <a href="http://docs.angularjs.org/api/ng.$q">promises</a>. These are very much like the way <code>Promise</code>s and <code>Future</code>s work in Scala on the server.</p>
<p>Which is perhaps why I quite like working with Angular.js in the browser!</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-88620842368418227492013-09-04T00:30:00.000-07:002013-09-04T00:30:31.150-07:00Building an assessment app in two days -- bug resolved<p>Ouch that was a painfully long delay...</p>
<p>The pause in committing code was because I hit an issue between DataAction and Play that took a little while to resolve. So there's a <a href="https://github.com/wbillingsley/handy/commit/70ac86267d10ffb2c46187671402f53e3698630e">commit on the handy project</a> that's just been pushed to resolve it.</p>
<p>The gory details can wait for another time, but the short version is that Play has a distinction between <code>EssentialAction</code> and <code>Action</code>, and these two Play classes don't work quite as nicely together as I naively assumed. This was causing problems in <code>DataAction.one(parse.json)</code>. It was a bit of a fiddly workaround that was needed.</p>
<p>So, back to it, but having taken a few hours out to deal with that, I might be having a late night after all.</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-60482398770266390012013-09-03T18:59:00.000-07:002013-09-03T18:59:24.427-07:00Building an assessment app in two days -- 5th commit.<p>So, it's late on Wedndesday morning and I probably should get a wriggle on if this is going to be done by tomorrow.</p>
<p>The commits I've been making so far are mostly basic plumbing -- wiring together the different components we're going to use. At some point, I'll publish these as a template project (using <a href="https://github.com/n8han/giter8">giter8</a>), so that future projects can start from a pre-plumbed base.</p>
<p>But, someone has to plumb the first one.</p>
<h2>Fifth commit: The basics of the Angular.js app</h2>
<p>The <a href="https://github.com/impressory/assessory/commit/16e702a29b0dd5286dfbfa0ba7bc77b6cb68a466">5th commit</a> sets up the basics of our Angular.js app.</p>
<p>Here's a little 404 pic for evidence:</p>
<div class="separator" style="clear: both; text-align: center;"><a href="http://1.bp.blogspot.com/-zdAsgg3niT8/UiaTgfF2apI/AAAAAAAABFI/8plKQ-fhAbU/s1600/Screen+Shot+2013-09-04+at+11.26.59+AM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-zdAsgg3niT8/UiaTgfF2apI/AAAAAAAABFI/8plKQ-fhAbU/s320/Screen+Shot+2013-09-04+at+11.26.59+AM.png" /></a></div>
<p>It might look a little odd that a 404 Not Found is telling me that I've got that part of the app wired up correctly, but it's because that error page is part of our Angular.js app -- it's not just plain HTML from the server.</p>
<h2>Angular.js app</h2>
<p>The Angular.js app is set up in two parts</p>
<ul>
<li><p>app/assets/javascripts/modules/base.coffee</p>
<p>This declares the Assessory module, and is the first thing we call</p></li>
<li><p>app/assets/javascripts/modules/app.coffee</p>
<p>This configures the app and declares the routes, and will usually be the <em>last</em> thing
we call (because it's going to be using a few controllers we haven't created yet)</p></li>
</ul>
<p>We use <a href="http://requirejs.org">require.js</a> to enforce the order in which all the scripts
are run, and also to combine and minify the scripts in production so that the browser
doesn't have to make so many requests for javascript files.</p>
<p>The rest of this post is gory details about how we're getting Angular.js to render the error page, and how the header at the top is our first Angular directive.</p>
<p>(It's here because I feel I ought to explain what's going on, but the chances are you'll want to skip it for more interesting posts later.)</p>
<h2>That 404 page comes from the client</h2>
<p>That friendly little fellow with the map (sorry for my poor drawing) is also being rendered by our Angular.js app.</p>
<p>What happens is this: the <code>/</code> route (the home page) is configured to use a template that
I haven't written yet. So, Angular.js gets a 404 error when it requests it.</p>
<p>Our app has configured Angular.js that when it has a routing error (such as this), it should set an error variable in <code>$scope</code>.</p>
<pre><code># Handles route change errors so that the user doesn't just see a blank page
Assessory.angularApp.controller('ErrorController', ['$scope', ($scope) ->
$scope.$on("$routeChangeError", (event, current, previous, rejection) ->
$scope.error = rejection
)
$scope.$on("$routeChangeSuccess", () ->
$scope.error = null
)
])
</code></pre>
<p><code>$scope</code> is an Angular.js concept -- it contains the data for rendering a fragment of the page. There are usually quite a few scopes, corresponding to different parts that are showing.</p>
<p>The error in this scope (the root scope) causes Angular.js to show the error portion of the main template:</p>
<pre><code> <div ng-show="error">
<ng-include src="'client-error-template'"></ng-include>
</div>
</code></pre>
<p>The client error template is pre-embedded in the first HTML we sent the browser, and includes some switches to change what's shown depending on what the error was.</p>
<h2>Site header directive</h2>
<p>At the top of the image, you can see a site header. That's our first directive. The template is just a fragment of HTML in <code>views.components.directive_siteHeader.scala.html</code>.</p>
<p>If you look in <code>includeDirectives.scala.html</code> you can see how we use Play's templating to insert the template into the HTML that we initially send the browser -- this means that Angular.js never has to make a request to the server to fetch that template, because we've already provided it.</p>
<pre><code><!-- Site UI components -->
<script type="text/ng-template"
id="directive_siteHeader.html"
>
@views.html.partials.components.directive_siteHeader()
</script>
</code></pre>
<p>(includeDirectives is in turn embedded into the main HTML in <code>main.scala.html</code>.)</p>
<p>The other side of the directive is the Javascript code that declares it to Angular.js</p>
<p>That is in <code>assets.javascripts.components.SiteHeader.coffee</code> and looks like this</p>
<pre><code>define(["modules/base"], (l) ->
Assessory.angularApp.directive("siteHeader", () ->
{
restrict: 'E'
templateUrl: "directive_siteHeader.html"
}
)
)
</code></pre>
<p>Around the outside, we have a <a href="http://requirejs.org">require.js</a> call that makes sure modules/base is loaded first. (That's going to create Assessory.angularapp)</p>
<p>And then we create a directive that tells Angular.js to replace <code><site-header></site-header></code> with the content of our site header template.</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-23866597769595380292013-09-03T07:54:00.002-07:002013-09-03T08:09:44.720-07:00Building an assessment app in two days -- 4th commit.<p>The <a href="https://github.com/impressory/assessory/commit/ac7ff989ae5e9d1cfda683f094a3407700fe6a58">fourth commit</a> has been pushed to GitHub.</p>
<h2>Starting to set up the client</h2>
<p>This sets up the beginnings of the Angular.js / Play app. Though the app doesn't do anything yet.</p>
<p>A quick run-down on its contents:</p>
<ul>
<li><p><strong>Global.scala</strong></p>
<p>This contains the application start-up code for Play. At the moment, it has three
things to set up:</p>
<ul>
<li><p>Set up the database, looking in <code>application.conf</code> for connection details
(which in turn looks up some environment variables)</p></li>
<li><p>Set the home action for <code>DataAction</code></p>
<p>DataAction is particularly designed for "single page apps" such as this one.</p>
<p>When the app is running in the client, Angular.js is going to request data in JSON
format from the server, and render it appropriately in HTML.</p>
<p>But at any stage, the user might click "Refresh", in which case the browser will
make a request to the server on that URL. And in that case, we don't want to return
JSON data, but the HTML and Javascript that will bootstrap our Angular.js app.</p>
<p>DataAction uses the <code>Accepts</code> HTTP header to tell the difference between a request
from our Angular.js app, and a request from a browser asking for HTML.</p>
<p>If a request is made with an <code>Accepts</code> header looking for a JSON response, then the
DataAction will run and return (asynchronously) data in JSON format. But if the
request is made with an <code>Accepts</code> header asking for HTML, then it returns the
home action (delivering our Angular.js app to the browser to get started).</p></li>
<li><p>Set the look up method for <code>RefById</code> and <code>RefManyById</code>.</p>
<p>This configures how items are retrieved from the database. The basic handy library,
(where <code>Ref</code>, <code>RefById</code>, <code>LazyId</code>, and <code>RefManyById</code>, amongst others, are defined) is
database agnostic, so this is how we wire it up to our ReactiveMongo database layer.</p>
<p>The <code>DAO</code> classes we have defined automatically inherit partial functions for
looking up the classes they handle. (The <code>DAO</code> trait in handy-reactivemongo defines
an appropriate partial function). So, the lookup method just has to call the partial
functions from the DAO classes to see which one applies.</p>
<p>(If you're new to Scala, a "partial function" is essentially a function that can say
<em>"actually, no I can't handle that argument after all"</em>. So, we can keep a set of
partial functions for looking up different classes of item by their ID, and the
partial functions themselves can decide whether or not they apply to the reference we
gave them.)</p></li>
</ul>
</li>
<li><p><strong>Application.scala</strong></p>
<p>This contains a few key "actions" for our app.</p>
<ul>
<li><p>index</p>
<p>This action delivers the HTML for our Angular.js app</p></li>
<li><p>The default action</p>
<p>Angular.js includes its own routing (its own handling of URLs) in the browser. In a
single page app, the routing on the client side is somewhat decoupled from the
routing on the server side.</p>
<p>This means it's perfectly possible that there will be a valid URL in the client
that isn't defined as an explicit action on the server.</p>
<p>But we still need to handle the request if the user hits "refresh" and causes their
browser to make an HTTP GET request to the server for that URL.</p>
<p>The default action causes those requests to return the index action (containing our
Angular.js app).</p></li>
<li><p>The partials action</p>
<p>Periodically, Angular.js needs to request the HTML for a new template to show.
As we define new views, we will be defining new partial templates.</p>
<p>If we gave each partial template its own entry in the <code>routes</code> file, then in
development, every time we add a new partial template, Play would want to recompile all the controllers as the routes file (containing all the controllers' action URLs) would have
changed. If we instead put all the partials into this one action, then adding a new
partial template only causes the Application controller to recompile.</p></li>
</ul>
</li>
<li><p><strong>main.js</strong></p>
<p>We're going to use require.js to combine and minify our Javascript (because we're
going to have a lot of small Javascript files by the time we're finished).</p>
<p>At the moment, there are no Javascript files to load, so we're "requiring" an empty
array of files.</p>
<p>We have set it up, though, so that when all the (zero) libraries have loaded, require
should tell Angular.js to bootsrap.</p></li>
<li><p><strong>includeDirectives.scala.html</strong></p>
<p>This is where we'll embed the HTML for Angular.js template directives.
But at the moment, there aren't any.</p></li>
</ul>
William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-77035341766321290232013-09-03T01:15:00.000-07:002013-09-03T01:16:50.800-07:00Building an assessment app in two days -- third commit.
<p>The <a href="https://github.com/impressory/assessory/commit/06735e38447b600007927d3ccea3056d33592080">third commit</a> puts in place the first Data Access Object for the database: <code>UserDAO</code></p>
<p>This uses <a href="http://reactivemongo.org">ReactiveMongo</a>, which is a non-blocking database driver for MongoDB. That means that when the database is processing a query, the thread is not left waiting, but can get on with other tasks.</p>
<p>Each of the methods returns <code>Ref</code>.</p>
<p>You're probably getting a bit tired of seeing Ref everywhere, but if you glance over to the <a href="https://github.com/impressory/assessory/blob/06735e38447b600007927d3ccea3056d33592080/modules/reactivemongo/src/test/scala/com/assessory/reactivemongo/UserDAOSpec.scala">test</a>, you should see how they can be chained together in a fairly easy to read style. (If you're familiar with Scala.)</p>
<p>For instance, in this snippet, you can read those <code>for</code> statements as sequential actions that happen one after another. They happen asynchronously, but in order. First we save the user, then we push a new session, then we fetch the user again using the session, and then we extract the name from the user we got back.</p>
<pre><code>"push sessions correctly" in {
val u = UserDAO.unsaved.copy(name=Some("Cecily Cardew"))
val returnedName = for (
saved <- UserDAO.saveNew(u);
pushed <- UserDAO.pushSession(
saved.itself,
ActiveSession(key="mysession")
);
fetched <- UserDAO.bySessionKey("mysession");
name <- fetched.name
) yield name
returnedName.toFuture must be_==(
Some("Cecily Cardew")
).await
}
</code></pre>
<p>There are a few design decisions taking place in this commit.</p>
<ul>
<li><p>User contains a sequence of active sessions.</p>
<p>This is going to let us list your active
sessions in the browser, and remotely log yourself out if you've left yourself logged
in on another computer.</p></li>
<li><p>There's no <code>save</code>, only <code>saveDetails</code> and a lot of <code>push..</code> methods.</p>
<p>When we save a user, we don't want to overwrite the sessions -- in case a concurrent
request is updating them while we're processing this save. (Whether or not it's
concurrent within the database, there might be two requests from the browser in flight.)</p>
<p>Accordingly, <code>saveDetails</code> does not update the active sessions or any of the other
lists.</p></li>
<li><p>Password hashing is already in there</p>
<p>Although we're not supporting log-in with passwords, the default classes in
<code>handy-appbase-core</code> already create a salt and a hash method, so that it's easy to
store passwords salted and encrypted. (This uses encryption that comes standard in
the JVM)</p></li>
</ul>
William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-41355730964904076322013-09-02T23:04:00.002-07:002013-09-02T23:04:19.553-07:00Building an assessment app in two days -- second commit.<p>The <a href="https://github.com/impressory/assessory/commit/724a3ac7846e234287dfab96c83ecefff084617a">second commit</a> is where I need to start thinking about the app a bit more. We're going to establish a few basic data model classes. They'll change as the rest of the code is written, but this commit will make a start on it.</p>
<p>Our situation gives us a slightly unusual need:</p>
<ul>
<li>The students are all in groups already</li>
<li>The software hasn't been written yet, so the students are not in the database</li>
<li>We can't use their UQ logins (institutional policy reasons)</li>
<li>But we have all the students' GitHub usernames in a spreadsheet</li>
</ul>
<p>When students log in, they need to be able to get their groups — which means the groups need to be entered before the students have created their accounts in the database.</p>
<p>That means we're going to need a concept of a "pre-enrol", so that when a student has logged in, they can automatically find both the course and their group, in this case using their GitHub username.</p>
<p>Although I'm writing this initially for our course, I'll try to make the data model reasonably general.</p>
<p>Things you'll notice about the data model</p>
<ul>
<li><p>There are lots of <code>Ref</code>s. This is part of my <code>handy</code> library. A ref is a reference to
a data object. It might be the object itself; it might be a <code>Future</code> that will return
the object when a database fetch has completed. It might be a <code>LazyId</code> if all we have
so far is the ID of the object. There are quite a few possibilities for what a <code>Ref</code>
can be.</p>
<p><code>Ref</code> is a monad, which means that it has <code>flatMap</code> and <code>map</code> methods that I'm going
to use extensively, and ensures that at the end of an algorithm I'll still have
something that meets Ref's contract.</p>
<p>It's what I call an "ad-hoc" monad, however, because we haven't predetermined the
specific kind of Ref we're going to end up with.</p>
<p>More information will go up on
<a href="http://wbillingsley.github.io/handy">handy's documentation site</a>
(And there's a paper I want to write on this style of app development soon.)</p></li>
<li><p>Each of the data classes extends <code>HasStringId</code>.</p>
<p>This means a little more than just that
it has a string id. <code>Ref.getId</code> looks for an "implicit argument" that can produce a
canonical id for an object. (For instance, so that if you use Integer ids,
<code>Ref(classOf[Foo], 10:Int)</code> and <code>Ref(classOf[Foo], "10")</code> resolve to the same item.)</p>
<p>Within the handy library, there's an appropriate object <code>GetsStringId</code> for handling
this for objects whose canonical IDs are Strings.</p>
<p>(In the database layer, however, we're going to be converting to and from MongoDB's
<code>BSONObjectID</code> class. I just don't want to expose object IDs in the API.)</p></li>
</ul>
<p>So, in this commit we have some incomplete data classes:</p>
<ul>
<li><p><strong>User</strong></p>
<p>A user in the system. This inherits from one in handy-appbase-core, and includes
types for Identity and PasswordLogin (though we won't be using the PasswordLogin for
now)</p></li>
<li><p><strong>Identity</strong></p>
<p>A social login identity, such as a GitHub account. (Or, in time, LTI for logging in
directly from Blackboard, but university policy prevents us from doing that yet.)</p></li>
<li><p><strong>Task</strong></p>
<p>Well, if we're asking them to do a peer critique, maybe we'll have other tasks for
students in time.</p></li>
<li><p><strong>Course</strong></p>
<p>A course (we'll try to open this up for others too)</p></li>
<li><p><strong>Preenrol</strong></p>
<p>A way of pre-registering students for courses if all you have is a social identity</p></li>
<li><p><strong>Group</strong></p>
<p>A group, for the group critique</p></li>
<li><p><strong>GroupSet</strong></p>
<p>Sometimes, in courses, students are in more than one group. For instance, they may have
a tutorial group as well as a project group. Or their group may change every so often,
but you'd like to preserve the historical groups they've been in in the past so you
can look back on their previous work.</p>
<p><code>GroupSet</code> will support this</p></li>
<li><p><strong>GPreenrol</strong></p>
<p>Preenrol for groups</p></li>
<li><p><strong>Question</strong></p>
<p>The critique is going to need a survey form of some sort, which will be made up of
questions. We'll establish the different kinds of question in a later commit.</p>
<p>(Likewise, there's an <code>Answer</code> trait)</p></li>
<li><p><strong>Critique</strong></p>
<p>A critique</p></li>
<li><p><strong>CritTask</strong></p>
<p>The task of doing a critique</p></li>
</ul>
William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-6237668233521730192013-09-02T20:26:00.000-07:002013-09-02T20:26:28.706-07:00Building an assessment app in two days -- first commit.
<p>So it's Tuesday lunchtime, and now that the meetings of yesterday evening and this morning are done, I can get started.</p>
<p>(I've got another meeting in an hour.)</p>
<p>The repository is now up on GitHub at <a href="http://github.com/impressory/assessory">http://github.com/impressory/assessory</a></p>
<p>The <a href="https://github.com/impressory/assessory/commit/ee2b29ef7d27e01c03041bc00d56b4a45940fbf2">first commit</a> just establishes the basic structure of the project. (The link takes you to github's page showing the committed code.)</p>
<p>We have three projects:</p>
<ul>
<li>Outermost, the Play app itself</li>
<li><code>asserssory-api</code>, which will contain classes for the data in the app.</li>
<li><p><code>assessory-reactivemongo</code>, which will contain our database layer. The project will use ReactiveMongo, which is a non-blocking driver for MongoDB.</p>
<p>And when I get to writing the database components, it'll also use <code>handy-reactivemongo</code>
that contains some useful common code, and wraps the returned data as a <code>Ref</code>.</p></li>
</ul>
<p>There are no tests yet, because there's no code to test. But on my machine at least, the
Play project starts.</p>
<p>If you check it out, you might find that sbt isn't wholly content about the "SNAPSHOT" dependencies and keeps trying to re-resolve them every time you run a command, even if it only just did so. This might make your build slower than ideal. This doesn't happen on my machine because I've built those dependencies and run <code>sbt publish-local</code> on them, so that sbt only has to look as far as the <code>~/.ivy2</code> directory on my laptop.</p>
<p>I'll resolve that in due course.</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-73791741371734714302013-09-02T01:40:00.001-07:002013-09-02T01:40:46.892-07:00Building an assessment app in two days without rushing (hopefully)<p>At the University of Queensland, I'm helping to teach a software engineering studio course ("<a href="http://www.uq.edu.au/study/course.html?course_code=DECO2800">Design Computing Studio 2</a>") where students are all collaborating, in groups, on 170-person project. It's a somewhat unique course, as it has a very large amount of collaboration between groups, and we've presented papers on it at two well-regarded international conferences: <a href="http://www.nicta.com.au/pub?id=5386">ICSE</a> and <a href="http://www.nicta.com.au/pub?id=6606">ITiCSE</a>.</p>
<p>This year, my colleague Jim has introduced peer critiques into the course — something that was missing in previous iterations. Groups will be asked to present their plans, and students will be asked to critique them. The groups in turn get to rate whether or not the critique feedback was useful.</p>
<p>I've also been writing the interactive teaching software <a href="http://impressory.github.io">"Impressory"</a> for use on the course, and have a number of other software projects on the go. So, I wrote <a href="http://wbillingsley.github.io/handy">handy</a> to make it easy and concise to write interactive, functional, webapps (generally, with <a href="http://angularjs.org">Angular.js</a> and <a href="http://playframework.com">Play</a>).</p>
<p>It's time to find out how easy it really is.</p>
<h2>The challenge</h2>
<p>This week, I'm going to write the critique tool, as the first piece of functionality for an open source assesement app. And I'm going to blog it as I go — each post describing what code I've written, and how and why it does what it does.</p>
<p>This isn't going to be a hackathon — I'm not planning on staying up nights. In fact the next thing I have to do after posting this is head out the door to a meet-up about something completely different. And then I've got another meeting tomorrow morning. (So you might not see the next post until tomorrow afternoon.)</p>
<p>But the next lecture for the course is at 8am on Thursday, and I'd like to have it done by then so I can show it in class. Right now, it's 6pm on Monday. So that leaves Tuesday and Wednesday to get it done (in between other things like writing lecture content).</p>
<p>Let's see how we go.</p>
<h2>For the students on the course</h2>
<p>On the course, we teach highly collaborative ways of building software — part of our ethos is that even when we think we're working individually, we are still collaborating because we work with each others' code. There are tests, continuous integration, ticket management, collaboration tools, and all the fun of the fair.</p>
<p>If I'm writing this app in public and blogging it, then the chances are at least one or two students will glance at my code and the blog posts. And, if they're anything like I was as a student, they will be particularly delighted to see what I get wrong! (I'm sure there will be some things.)</p>
<p>So, I'd better to make the disclaimer up front that there will be some short-cuts.</p>
<p>Things that will be the same:</p>
<ul>
<li>Version control, using git on github</li>
<li>There will be some tests (though using <a href="http://etorreborre.github.io/specs2/">Specs2</a> rather than JUnit)</li>
<li>Automated builds (though using <a href="http://scala-sbt.org">sbt</a> rather than gradle). At each stage, you should be able to run it at home if you want.</li>
</ul>
<p>Things that will be different:</p>
<ul>
<li><p><strong>Scala, not Java.</strong></p>
<p>Later in the course, we introduce students (a little) to Scala. But for the moment, it
might look a little alien.</p>
<p>Using Scala means a lot of the code I write can be much more concise.</p>
<p>It can also be written in a functional style that makes it easy to use some
powerful abstractions, and to avoid some mistakes
(by making it less likely that a mistake will compile without errors).</p></li>
<li><p><strong>Some unfamiliar libraries.</strong></p>
<p>In <a href="http://wbillingsley.github.io/handy">handy</a>, I hopefully have a bit of a head
start.</p>
<p><code>handy-appbase-core</code> contains a few classes that have been hived off from Impressory to
mean I don't need to repeat myself about functionality I've already written.</p>
<p>And that includes dealing with security, asynchronous databases, returning model
objects as JSON, log in with GitHub, dealing with whether it's a JSON request for data
or a browser hitting a data URL, etc.</p></li>
<li><p><strong>Probably fewer tests</strong></p>
<p>On the course, having fewer tests would be a very bad habit — there's 169 other programmers who
could break your code at any moment, and we'd like a test to show they haven't please.</p>
<p>But over the next few days I'm not going to have 169 collaborators to keep me honest.</p>
<p>The style I'm writing this in, however, tries to make sure that the type checker is
less likely to let a mistake through than for a typical Java program.</p></li>
<li><p><strong>No CI server.</strong></p>
<p>I haven't yet set up a continuous integration server for the project. As I'm a lone
developer, so there's nobody to continuously integrate with, I might not.</p></li>
</ul>
William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-85807959570057059582013-09-01T20:25:00.001-07:002013-09-01T20:25:12.320-07:00Play, Angular.js, and Handy<p>For a few recent projects, I've been using <a href="http://angularjs.org">Angular.js</a> at the client and the Scala <a href="http://playframework.com">Play</a> framework at the server.</p>
<p>I also have a <a href="http://wbillingsley.github.io/handy">handy</a> library that helps make a lot of things quick, easy, and tidy.</p>
<p>Normally, every framework has its good points but the occasional gotcha. But in this case, I found them to be a very virtuous combination, each making the others more useful.</p>
<h2>Play and Handy</h2>
<p>Play is a non-blocking framework, and with my <a href="http://wbillingsley.github.io/handy">handy</a> library, it's nice and easy to write code that can include as many asynchronous calls as it likes and returns the result to the client. Even if the JSON conversion involves more asynchronous calls to fetch other information you'd like to include.</p>
<pre><code>def get(contentId:String) = DataAction.one {
implicit request =>
for (
content <- contentRef(contentId);
approved <- request.approval ask
Permissions.ReadContent(content.itself)
) yield content
}
</code></pre>
<p>In our hypothetical example here, <code>DataAction.one</code> will call <code>content.toJsonFor(request.approval)</code> to produce JSON to return. In our hypothetical example the "content" is sometimes a sequence — a playlist of other pieces of content to show in order — and converting it to JSON fetches and embeds them in the response. And it all happens aynschronously in a compact monadic style.</p>
<p>(This example is a slightly butchered version of what happens with content sequences in <a href="http://impressory.github.io">Impressory</a>)</p>
<p>I'm gradually improving the documentation at <a href="http://wbillingsley.github.io/handy">handy</a>, if you want to find out how it makes things easier.</p>
<h2>Play's snag with HTML</h2>
<p>If you want to Play's templating system for HTML, it has the annoying gotcha that while you can embed an Option straight into the template,</p>
<pre><code><span>@{object.myOptionValue}</span>
</code></pre>
<p>you can't embed a Future straight into the template.</p>
<pre><code><!-- not supported -->
<span>@object.myFutureValue</span>
</code></pre>
<p>Of course that's where "gotcha" means "has the same limitation that every other templating system on the planet has too".</p>
<p>Play's templating, like just about every other templating framework I know of, doesn't have the smarts to turn the template itself into a Future if it's got a Futures embedded into it.</p>
<p>This is perfectly fair enough, but means that if you're working in a non-blocking way, you end up needing to resolve all your Futures and only then call the template, passing them in as explicit parameters.</p>
<p>Which always made me grumpy, as it was unnecessary overhead making things look messy. Thanks to my handy library, I could convert to JSON in an asynchronous way, with embedded Futures in the conversion, but not to HTML.</p>
<h2>Angular.js to the rescue</h2>
<p>The obvious way that Angular.js helps Play is that the templating happens on the client. You're no longer embedding data into the HTML, so you don't end up needing to resolve those futures.</p>
<p>A problem not encountered is as good as a problem solved!</p>
<h2>A snag with Angular.js</h2>
<p>The first gripe I had about Angular.js was perhaps an unusual one. Not the documentation (that I've coped ok with), but that inlining reusable snippets is a little clunky.</p>
<p>There are directives, and there's <code>ngInclude</code>, but the template to include either has to either:</p>
<ul>
<li>be fetched in another http request,</li>
<li>or embedded into a <code><script></code> element in the HTML. (Which doesn't itself solve how to split it into a different file.)</li>
</ul>
<h2>Play's templating to the rescue</h2>
<p>Well, this is of course is where Play's templating solves the problem.</p>
<p>An Angular.js template can be broken up into as many fragments as you like, and pieced together on the server in a single request.</p>
<p>So for instance, Impressory has an <code>includeDirectives.scala.html</code> file, whose sole responsibility is to pull in all the directive templates</p>
<pre><code><-- etc -->
<script type="text/ng-template"
id="directive_ce_edit_tags.html"
>
@views.html.partials.viewcontent.directiveEditTags()
</script>
<script type="text/ng-template"
id="directive_ce_edit_settings.html"
>
@views.html.partials.viewcontent.directiveEditSettings()
</script>
<-- etc -->
</code></pre>
<h2>In the end</h2>
<p>In the ends, this gives me a way of writing Single Page Apps that is:</p>
<ul>
<li>very compact</li>
<li>scalable and non-blocking</li>
<li>typesafe</li>
<li>looks neat and clean (a simple monadic style)</li>
<li>separates concerns well (database, security, JSON conversion, etc are all handled tidily)</li>
<li>above all, fast to write</li>
</ul>
<p>This last part is particularly important — I'm employed as a research engineer, and there's only so long I can get away with cranking out code. Most of the time I want to be deciding <em>what to build</em> and inventing interesting solutions to interesting problems, rather than spending too much time writing the code.</p>William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-72647104863884570292012-08-02T00:13:00.003-07:002012-08-09T20:25:18.293-07:00Turning into a gibbering idiot at interview...I am really bad at interviews. Suddenly, my brain flips into a mode where I'm worrying about my potential answers will appear to each of the panelists and their competing opinions and interests, and while I'm making up my mind what's coming out of my mouth is a long stream of woolly waffle.<br />
<br />
Asked what are the emerging trends in software engineering research that we ought to be teaching students, my brain runs at a mile a minute thinking the following:<br />
<ul>
<li>Most of the things courses are struggling to adjust to -- cloud computing, platform as a service, polyglot programming, etc -- aren't "emerging", they've long since bloomin' well emerged. Some courses have even been struggling to get agile practices in there, and that's been around for ten years now.</li>
<li>Actually the bigger problem I see students having isn't the lack of "emerging research" on their course, it's that most of their studies are devoid of context and in software engineering context can change everything (the real-world problem we're trying to solve and all the real-world constraints on the team). So they leave uni still strangers to many of the real problems of software development.</li>
<li>For your course? I'm not sure what to answer yet, because I'm still getting to know your teaching program. For ours, I know the problems the course I helped redesign had before my colleague and I redesigned it, which were...</li>
<li>The answer's probably going to be different for each of the students -- what are the paths your cohort of students tend to take? Do they try to form the next great start-up, go into big data, build safety-critical software for railway signalling?</li>
<li>And of course trying to think of what the favourite research topics of the panel might be.</li>
</ul>
<div>
Unfortunately, while it's thinking that (mostly thinking "would that be an ok answer?"), what's actually coming out of my mouth is a time-filling ramble, and I never quite say any of it.</div>
<br />
<br />William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-58111735237213836582012-06-01T01:57:00.001-07:002012-06-01T02:04:59.575-07:00Does Oracle v Google put a hole in the GPL?<br />
I had an odd thought (a nagging worry) that with <a href="http://www.groklaw.net/article.php?story=20120531173633275">Oracle v Google's API argument resolved</a>, there might be a gaping hole in the GPL, even version 3. I'm not a lawyer, so I hope someone will correct me on this and allay my concern. Let me tell it as a story....<br />
<br />
<blockquote class="tr_bq">
One day, NastyCo decided it wanted to use nicelibrary in a proprietary software product. Nicelibrary is open source under GPLv3, but NastyCo don't want to give their source code away.<br />
<br />
So NastyCo decide to use <a href="http://maven.apache.org/">Maven</a> to build their product. NastyCo don't give their customers a compiled binary; they give them a build script, and tell them "all you need to do is run the script; you can even just double click on it". When a customer runs that build script, their computer then fetches a compiled jar of NastyCo's code from NastyCo's servers, but fetches all the open source GPL libraries from Maven Central Repository and other public open source repositories. And their computer running the build script assembles the proprietary product automatically there and then.<br />
<br />
Mr Nasty, CEO of NastyCo, reasons thusly:<br />
<ul>
<li>NastyCo's proprietary source code isn't a "derived work" of nicelibrary because after Oracle v Google it seems to have been made clear that there's no copyright in an API.</li>
<li>The compiled and linked program together is a "derived work", but NastyCo are not distributing that -- they are just distributing the binary of their own code, and a build script. Their customers (running the build script) are fetching the rest from somewhere else and building the "derived work" themselves.</li>
<li>Because NastyCo aren't distributing ("propagating") a derived work, the GPL doesn't require them to publish their source code, so they're not in breach of the GPL.</li>
<li>Although the customers are running a derived work, they built it themselves (their computer built it using the build script), and the GPL explicitly allows them to run and modify private derived works, so they're not in breach of the GPL either.</li>
<li>Because those privately-run derived works are allowed under the GPL, NastyCo can't even get in trouble for condoning or enabling infringement.</li>
</ul>
So, thinks Mr Nasty, neither NastyCo nor their customers are breaching the GPL, but nicelibrary has now ended up in NastyCo's proprietary product that can end up on millions of customers' machines without NastyCo ever having to publish a line of sourcecode.</blockquote>
<br />
So, I dearly hope there is a flaw in Mr Nasty's reasoning, but what is it?William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com1tag:blogger.com,1999:blog-7232678821280376479.post-64654635814915123852011-12-21T22:43:00.000-08:002011-12-22T07:58:15.331-08:00Stanford AI ClassI was one of the many thousands who took part in the online <a href="http://ai-class.com/">Stanford AI class</a> -- in my case as much to find out about how they'd make the class work as in order to learn some of the AI topics I missed out on as an undergrad way back when. Now that it's over, here are a few thoughts:<br />
<br />
I'll put my conclusion first. Large online classes like these won't replace local university courses; they will transform them. More and more, university lecturers are going to become content curators and facilitators, and they're going to need to write less and less of their own presentation material.<br />
<br />
Of course, mine is a slightly biased view as <a href="http://www.theintelligentbook.com/">the Intelligent Book</a>, the interactive cloud teaching software I've been developing, makes it very easy to incorporate third party material like this into a lecture course. And as you read through this, you'll sense a certain "this is why we need Intelligent Books" theme in my comments!<br />
<br />
Anyway, into detail on what I thought of the course...<br />
<br />
The video lectures, which were like video-recorded personal tutorials, worked very well indeed. They were clear, concise, engaging, and had the feel of being in a small class rather than a large one. Thrun and Norvig are excellent communicators and very interesting to listen to. The fact that it was an ongoing course (everyone working to a schedule), was good motivation to make time to watch the videos and do the quizzes. That's the good news, and it really is very very good news indeed.<br />
<br />
But every class has its flaws. So what were this one's?<br />
<br />
Well, the class interaction and quizzes were simplistic, both in style and content. For instance, some of the final exam's questions on computer vision weren't about artificial intelligence at all, but were simple early high school physics questions about the optics. An object that's yay big is yay distant from a camera with a focal length of such-and-such, what's the size of the image on the image plane? Here are three objects in a scene; this camera sees them in this order, what order do they appear to be in to these other cameras that are looking at the scene from different angles?<br />
<br />
I tend to think that while the videos are very effective for presenting a topic, they aren't so efficient for quizzes and reference. For reference, looking up that formula just to check you've got it right, seeking within a video to find the point it was on-screen is much slower than flicking back through text. For quizzes, the format they used only supported tick-the-box and fill-in-the-box questions, but nonetheless required the lecturers to spend time recording a video introduction for each question.<br />
<br />
(So, this is already one area where I see the Intelligent Book bringing an advantage -- it helps courses to use a plurality of different kinds of content. Hop from the video to the notes, to the quiz, to the advice...)<br />
<br />
The interaction between class-members was essentially limited to forums and whatever students organised off-line. The videos were pre-recorded, so of course there wasn't much in the way of to-and-fro between the lecturers and the class, except in the "office hours" on Google Hang-outs.<br />
<br />
This is unfortunate, as interactive teaching is very beneficial and is starting to gain traction in universities. <a href="http://mazur.harvard.edu/emdetails.php">Eric Mazur</a>, <a href="http://www.ncsu.edu/PER/beichner.html">Bob Beichner</a>, <a href="http://www4.ncsu.edu/unity/lockers/users/f/felder/public/Rich.html">Rich Felder</a>, and others in science and engineering education have been trying to encourage lecturers to interact with their classes more, and move beyond simple one-way transmission of material. Having taught a class last semester using the Intelligent Book, with the students chatting, discussing, and giving feedback <a href="http://theintelligentbook.blogspot.com/2011/05/just-gave-lecture-with-live-chat-on.html">live on the lecture screen</a>, and <a href="http://theintelligentbook.blogspot.com/2011/10/social-practice-questions-part-1.html">answering and discussing questions as a class</a>, I genuinely missed the interaction.<br />
<br />
So what do I think will happen next -- how do I think/hope this will change university engineering and science education?<br />
<br />
Well, the videos really are excellent. So the first thing that will happen is that other universities will want to use these videos and others like them in their courses. Rather than Dr Joe Bloggs spend another two hours working on his PowerPoint slides for a class, he might be better off finding and showing an excellent video by famous presenters, and then spending his energy interacting with the class to further their understanding.<br />
<br />
And I think that trend -- to use more third party prerecorded material and spend more time interacting with the class rather than preparing material -- will grow very quickly. Lecturers won't just enjoy easy access to good material; they'll realise that the lecturers who recorded these videos get a great deal of exposure and can become famous teachers -- producing the next great teaching video will become another route to increasing your academic profile. I think we'll quickly see lecturers competing to get their videos used in other people's classes.<br />
<br />
And that, I think, means that traditional lectures will change. Short videos punctuated by class discussions and exercises, and linked to rich sets of notes and social material, will become far more common than they are now. But then, I'm biased, because that's just the sort of thing that <a href="http://www.theintelligentbook.com/about">the Intelligent Book makes easy</a>.<br />
<br />
<br />
<br />
<br />William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0tag:blogger.com,1999:blog-7232678821280376479.post-83065162314410323942011-12-14T23:43:00.000-08:002011-12-14T23:43:54.362-08:00Metrics for Intelligent BooksThere's a post on metrics in Intelligent Books gone up over on the <a href="http://theintelligentbook.blogspot.com/2011/12/metrics.html">Intelligent Book's blog.</a> William Billingsleyhttp://www.blogger.com/profile/12602757296157663004noreply@blogger.com0