New Instructor Dashboard

I’ve been working on a new instructor dashboard the last week using Plotly + Dash. Its taken a while to wrap my head around the declarative style, and to learn the ins and out of plotly. But I’m getting there. Let me know if you want a closer look and would like to give me feedback! In the meantime here is a preview.

Screenshot 2022 12 30 at 1 40 59 PM

Understanding Entrepreneurship -- Part II -- New Beginnings

Whenever I teach this JTerm course on entrepreneurship, friends and colleagues often ask me “do you miss it?” Meaning do I miss the business world. The answer has always been, “yes, but I love teaching even more.” This year the answer is different, this year the answer is… “yes, and I’m excited to tell you that I’m leaving Luther to start a new company!” Spring Semester of 2018 will be my last semester as a full time faculty member at Luther. I have accepted the separation agreement offered by Luther, and I’m really looking forward to the next chapter in my life and career!

This was not an easy decision – I have enjoyed my time at Luther, and Jane and I could not have asked for a better place to raise our family. JTerm is always a powerful reminder of how great our students are, and how much I enjoy getting to know them beyond the classroom. But, Kaia and Josh have both graduated from college, Kaia is married, in grad school, with a house, and three dogs in Sioux Falls, Josh has a great girlfriend, and a job he likes in Wisconsin. In short, they are launched (yay us!).

We are ready for the next phase of our lives. So, the house in Decorah is for sale and we have a down payment on a condo in downtown Minneapolis! We close and move in September of 2018! The bike trails are out the back door, we have a view of the Stone Arch bridge, the Guthrie is three blocks away, the light rail to the new MNUFC (Loons) stadium is a four block walk! It is a 15 minute bike ride to our friends Brian and Holly for happy hour! What could be better? – Well, if we could convince a few Decorah friends to move to Minneapolis that would be perfect, but we try not to be greedy.

Runestone Interactive, LLC

So, this January I’ve been listening very closely and thinking about how all of these great stories and advice apply to a guy in his mid-50’s. I’m leaving a tenured faculty position, at a college I love dearly to start a new company. Here is what I take away.

We heard the following from a lot of different people: “follow your passion!” Well, I admit I’ve told my students this many many times (although I could write a full post on the perils of telling 19 year olds that they need a passion.) But I have to say that for as much as I enjoy teaching at Luther College, Runestone Interactive has definitely become a passion. Runestone has grown from a small project for 30 Luther students few years ago, to supporting over 20,000 students each day from over 600 institutions around the world. This growth has all happened through word of mouth. No advertising, No booths at trade shows, no sales force, no full time development team! I really think that Runestone could be 2,000,000 students a day with focus and full time effort. – Yes, former students, that is what I think about in the shower in the morning!

The separation incentive offered by Luther gives me the opportunity to start a new company, incorporating what I learned from all the mistakes I made the first time around with Net Perceptions, and all of the experience I’ve gained in the last 15 years of teaching. It gives me one year to figure out if there is a business model that will keep the basic features and the books of Runestone free to everyone, while allowing me to build a small business that pays a salary, and hopefully allows me to bring others on board to work on this with me. It allows me to (1) follow a passion, (2) make the world a better place and (3) to play my part in “righting the wrong” that is textbook publishing today. Yes, $300 for a paperback textbook is a wrong! Wow, count them, that is three awesome reasons to start a company!

Democratizing Textbooks for the 21st Century

In our discussions of Guy Kawasaki’s books during JTerm we talked about companies needing a mantra. I think that the Mantra for Runestone is “Democratizing textbooks for the 21st century”. This could be another whole post, but I’ll just say that there is a huge need for computer scientists and there are a lot of well meaning, (but potentially under-prepared) teachers out there who we can help teach computer science. This is true at the high-school level where computer science is getting traction again, as well as many small colleges where it is almost impossible to find a computer science PhD who is willing to work for what small colleges can pay.

The second piece of Runestone is in the interactive nature of the books. Textbooks should not be static, read-only, words. Textbooks in the 21st century should engage the reader in interactive learning. Textbooks should also give instructors insight into what their students understand and what their students are struggling with… Textbooks should help answer the question “How can I maximize my time in the classroom today?”

Get Uncomfortable

We heard a lot about seeking opportunities that make you uncomfortable. It is through these uncomfortable experiences that you grow. For the last 15 years this has happened through teaching (especially new courses), and traveling to new places (many times with students) and experiencing new cultures. Leaving my faculty position – where I have almost total job security – is definitely a move in an uncomfortable direction. Especially for someone over the age of 50. But, I am convinced this is the right thing to do.

Creating a culture, charting your own course

We heard from a lot of people that had worked in both large and small companies about how great it was to be part of a small organization. in a startup, everything you do is important. From making the coffee in the morning, to setting strategic direction. I get that. I remember those early days from Net Perceptions. I miss those days. I miss that sense of ownership for the whole enterprise. The “shared governance” model used by most small colleges is about as far from agile as you can get.

This course reminded me, yet again, why I admire Jeff Bezos and the culture he has built at Amazon. I was blown away that all of the young people we spoke with at Amazon understood and could talk about many, if not all of, the 14 leadership principles. If you don’t know them here they are:

  • Amazon’s Leadership Principles

    • Customer Obsession

    • Ownership

    • Invent and Simplify

    • Are Right, a Lot

    • Learn and be Curious

    • Hire and Develop the Best

    • Insist on the Highest Standards

    • Think Big

    • Bias for Action

    • Frugality

    • Earn Trust

    • Dive Deep

    • Have Backbone; Disagree and Commit

    • Deliver Results

You can get more detail on each of them here. But I can only fantasize what a great company Runestone would be if I put these into action. I also think that higher-ed could benefit from all of these principles, but the conditions are so hard to make this happen!

So as I finish up this post, I have a week until Spring Semester classes begin. Am I sad that this is my last semester? Of course! Luther College is such an integral part of my life – I’m an alumni, a parent of an alumni, a faculty member, and a donor. I’ll only be losing one of those four tags. I look forward to finding new ways that I can serve the college in the future. I’ll also miss the students and all of the energy they bring. I’ll miss watching them grow and develop into the successful entrepreneurs, developers, and managers that so many of them have become. In the meantime, I’m going to enjoy my final semester to the max. I get to teach two of my favorite classes, and I’m eliminating non-essential committee work to go out on a high note!

adventures with flask-cors

  • I want people to be able to write and host the static parts of any book on any server. You can think of each page in a book as its own single page application.

  • I want to provide back-end services so that students using any book hosted anywhere can save their programs and answers to quizzes etc.

  • I want to continue to gather research data on how students learn computer science.

  • I want to make the registration and login process as easy as possible.

Since the static parts can be hosted anywhere (including a site like The interactive parts are going to involve making cross-domain XMLHttpRequests (xhr). Of course the first thing that happens when you have a page hosted on static-site that makes an xhr request to ajax-server is that you get an error. Browsers and sites work together to disallow cross-domain requests to prevent a variety of nasty behaviors. But, there are many times (wlike now) when you have a legitimate reason for doing this. So, the w3c created the Cross Origin Resource Sharing (CORS) standard to help developers get around this. Cory Dolphin has created an excellent plugin for Flask developers called Flask-CORS. The plugin is a great example of the brilliant design behind Flask and in fact the entire WSGI stack.

The Really Simple Approach

The first thing you find when you start googling about this problem is that there is a seemingly simple solution. If you have control over your AJAX response you simply need to add an HTTP header Access-Control-Allow-Origin: * problem solved. Now everyone in the world can make xhr requests to your server and use the results in their page.

Adding a header is pretty simple in Flask. All you need to do is use response.headers.add("Access-Control-Allow-Origin", "*" Problem solved, moving right along to the next programming challenge.

Or maybe not. Minutes later you realize that this is not all that great because you have decorated some of your requests to require a login. That wont be a problem if the static page is served from the same domain because you will automatically get the session cookie, and the Flask-Security extension will eat that cooking and validate things for you. BUT if your static page is not served from the same domain you will not even get the session cookie. Oh Bother. But you also have a second problem. You have probably violated the CORS specification without even meaning to. Really, if I had to read the spec for every web standard I wanted to use I would seriously think about changing careers. But, here is the important part you may not return a CORS header unless the request contains an origin header! Chances are you tested you change with a quick curl call to your endpoint, saw the Access-Control header and were happy. But you sure didn’t give it an origin header on the request when you did that. So to summarize, we have two problems we need to solve:

  1. We want to incorporate authentication into our cross origin strategy.

  2. We want to be good citizens and follow the spec.

The Smart Approach

The smart approach is to use a nice extension where other people have figured this out, and presumably followed the specification. Enter Flask-CORS. You can enable CORS support with a simple decorator @cross_origin This will automatically add the Access-Control-Allow-Origin: * to responses. As long as your test request includes an Origin. If you are like me you will forget that part, and then wonder why the extension must not be working. So this solves problem 2.

To solve problem 1 here is a snippet of code that works just fine.




def test():

    return jsonify({'foo':'bar'})

The above responds to the url /ajax/page I have all of my API calls in an ajax blueprint with ajax as part of the url. I’m requiring that the user is logged in before I allow them to access this endpoint. I also want it to be allowed cross origin. This is where the parameter to the @cross_origin comes into play. Supports credentials sets up the CORS response to return an additional CORS header: Access-Control-Allow-Credentials: "true". For one final twist, you need to know that when you have supports_credentials=True you may NOT set Access-Control-Allow-Origin: * You need to be specific and set the origin to the origin that comes in the request headers. To Make this work and try it out from the client side, here is a bit of HTML/Javascript.

<button onclick="corsTest();">TestCORS</button>


    function corsTest() {

        var xhr = new XMLHttpRequest();

        xhr.withCredentials = true;

        xhr.onload = function () {



        xhr.onerror = function () {



"GET", "", true)




Note that you need to set xhr.withCredentials in order for your session cookie to be sent along. By default cookies are NOT sent with cross origin requests.

Now, I may end up adding more to this as I discover the intricacies of so called “Non-Simple” requests. That is requests beyond simple GET and POST, as I work on moving my API toward a RESTful API which uses PUT and others. This will nodoubt enlighten me about preflighted requests. Which I can only assume means something different than sitting around in an airport bar waiting for your flight to be called.

There is a lot more detail and background on using CORS at the following two sites:

writing a runestone lab the easy way

As part of the grand reorganization of the various tools and software associated with the Runestone Interactive project I am planning to also write a series of tutorials to help people get started. The major aspects of this reorganization are discussed in detail in the Project Roadmap, but for the sake of some context I can summarize the major efforts as follows:

  1. Separate the distribution and development of writing tools from the server.

    1. Make a pip installable runestone package.

    2. Remove the interconnectedness between the components and Sphinx. In other words support the user of runestone tools in environments like Markdown, and even wysiwyg html editors.

  2. Re-architect the server side focusing on services for the writing tools

    1. Create an authentication service that supports CORS for cross domain AJAX use.

    2. Create a standard REST API for logging and storing student data

  3. Create a web application (or integrate with another) for grading

Since I just completed Part 1.1 I thought it was a good time to talk about how easy it is for you to now use the runestone tools for creating a lab for your students, or lecture notes and presentation materials for your class.

Getting Started

The major steps in getting started are

  • Installing Python

  • Installing the runestone tools

  • Building your first lab

Install Python

  1. If you are on a Mac you are already done with this step.

  2. If you are on Windows you will need to go to and download Python3.x. The windows installer is a typical installer and you can just click your way through it.

If you are an advanced Python user you may want to may want to create a virtualenvironment for this project but it is not a requirement.

If you are on Windows you may want to edit your PATH environment variable following the instructions here. Again, mac users can ignore this.

Installing the Runestone Components

You are going to need to use the command line for the rest of this tutorial, so start up a Terminal (/Applications/Utilities on a Mac or run PowerShell or cmd.exe on Windows) I will repeat myself here. These commands need to be run from the command line, not from the Python shell.

Run the pip command

$ pip install runestone

Or on Windows if you have not modified your PATH try:

C:\\Python34\Scripts\pip.exe install runestone

From now on I’m only going to give the Mac way of running the commands. If you are on windows you will need to add C:\\Python34\Scripts to the beginning of the command and add .exe to the end.

You can watch as a lot of text goes scrolling by. But as long as you don’t get any errors you should be good to go. You only need to do these first two steps once. Once you have installed Python and runestone you will not have to do it again.

Starting your first Runestone Project

Here is a session of me on my computer creating a simple project.

$ mkdir mynewproject

$ cd mynewproject

$ runestone init

This will create a new Runestone project in your current directory.

Do you want to proceed?  [Y/n]: y

Next we need to gather a few pieces of information to create your configuration files

Project name: (one word, no spaces): myhello

path to build dir  [./build]:

require login   [false]:

URL for ajax server  []:

Your Name  [bmiller]: Brad Miller

Title for this project  [Runestone Default]: My Hello World

Log student actions?  [True]: False

Done.  Type runestone build to build your project

At this point you will have the following files and folders:




  • The _static folder is for things like images or javscript files.

  • The _sources folder is where you will put your own writing. To start with there are a couple of examples files for you.

  • The _templates folder is for styling. There is a default set of templates that match the runestone interactive look and feel. That is a good thing to start with. Once you become more familiar with the system you may want to customize the templates or even make your own.

  • The file is used by Sphinx, and contains information from some of the questions you answered when you initialized your project.

  • The file is used for building and setting build parameters.

All of these files are important, and you should not delete any of them.

Next run runestone build This command will create a build/mynewproject folder with an index.html file in it. If you want you can now run runestone serve and then go to your browser and open up the following URL http://localhost:8000/index.html Yay! You have a webpage. Feel free to explore a bit to get an idea about some of the components you can use in your lab.

Writing your Own Lab

OK, lets edit _sources/index.rst Initially it looks like this:


This Is A New Project


SECTION 1: Introduction


Congratulations!   If you can see this file you have probably successfully run the ``runestone init`` command.  If you are looking at this as a source file you should now run ``runestone build``  to generate html files.   Once you have run the build command you can run ``runestone serve`` and then view this in your browser at ``http://localhost:8000``

This is just a sample of what you can do.  The index.rst file is the table of contents for your entire project.  You can put all of your writing in the index, or as you will see in the following section you can include additional rst files.  those files may even be in subdirectories that you can reference using a relative path.

The overview section, which follows is an ideal section to look at both online and at the source.  It is pretty easy to see how to write using any of the interactive features just by looking at the examples in ``overview.rst``

SECTION 2: An Overview of the extensions


.. toctree::

   :maxdepth: 2


SECTION 2: Add more stuff here


You can add more stuff here.

If you are not familiar with markup languages, this file should still be quite readable to you, and you can probably easily guess what most things do. Runestone uses a markup language called restructuredText. There is a very nice, short tutorial here.

To give you an idea of what you see in the example above, the section that starts with .. toctree:: is called a directive and it creates a table of contents for you. the maxdepth part sets the table of contents to show sections and subsections. And the line with overview.rst indicates that it is a file that should be included in the overall web page. More on all of this later. Our first task is simply going to be to wipe everything out, and start over. Using a plain text editor change index.rst to look like this:


My Sample Lab


Part 1: Turtle Graphics


In this section we will do the following:

* Create a turtle

* Make the turtle draw a box

.. activecode:: turtle1

   import turtle

   timmy = turtle.Turtle()

   for i in range(4):



Now it is your turn.  Can you modify the program to make timmy draw an octagon instead of a square?

Now save the file and rerun the runestone build command. Everything should build without a problem and you can now run runestone serve and open up http://localhost:8000 from your browser. Notice that you can change the program and rerun it right from your browser.

It is probably obvious that you can create headings and subheadings. Unordered lists are created using * and the runnable code examples are created by the .. activecode:: directive. The name turtle1 must be unique on the webpage, other than that it is not used for too much at this point. The rest of the activecode directive contains plain old python code, but it must be indented to line up with the a in activecode. All indented lines are included as the body of the activecode directive, regular text processing starts at the first unindented line.

There you have it. You have created a very nice little lesson without a lot of hassle. The Runestone and Sphinx tools take care of all of the formatting for you!

Giving Students Browser Access to the Lab

If you have your own webpage hosted on a school server that you normally use for class you can make your Lab available to the students by simply taking the folder mynewproject inside the build folder and putting that on your website. The folder is self contained and can be hosted on any web server.

If you know the IP Address of your own computer and you simply want to give let students bring up the webpage from your computer you can do that too. For example, lets suppose you know that your IP address is Your students can get everything they need from

Coming Soon

There are many free web hosting solutions out there and you can also choose one of them and upload your project folder for hosting there. I’ll cover at least one of them in another tutorial. In fact I think I see a whole series of tutorials in the future on topics such as:

  • Making an online quiz for class

  • Making a lecture or presentation

  • Hosting your lab or quiz on github pages or another online service

  • Using your lab with runestone services

easy publishing with runestone interactive

During my January travels, I also converted this blog from tubmlr, which had been frustrating me for a while, to Octopress, with which I have been very happy. Nothing like hacker level control of your own blog. But more, than just the switch in tools, the move to Octopress inspired me to make it easier for people to publish small or large works using the Runestone tools.

Yesterday, at the Learning @ Scale conference we demoed this new capability. See the demo here. To make it super easy to publish:

  • Lecture slides

  • Demonstrations

  • A Tutorial

  • Lab Instructions

  • In class exercises

  • A short module on your favorite topic not covered elsewhere

  • An entire book


You can simply follow the instructions at this new repository: In a nutshell:

  1. Install Sphinx, paver, and paverutils using pip.

  2. Clone the repository

  3. Edit the index.rst file in source, and add any additional rst files you may want, depending on how complex your project is.

  4. run paver build


Now you have a choice. In the build directory you have a nice self contained set of html files, these files are set up to make use of the runestone server invisibly in the background. The static html can be served from any web server. Just drop in the build directory and you are ready to serve. OR, you can now host and deploy your project using GitHub Pages. To host on github pages you need to do three things.

  1. Create an empty repository in your github account.

  2. run paver setup_github_pages and paste in the URL of the new account.

  3. run paver deploy

Now your pages will be available at:

If you want to host these pages behind a custom domain name, you can follow the instructions on github for doing so. Hint: Its really easy.

I hope this new capability will inspire lots of people to give these tools a try. I also hope that we can build a repository of resources built with the tools, so that we can all share our teaching ideas. Stay tuned for more on that.


All of the features, activecode, codelens, assessment questions, parson’s problems, and more work just fine. The major thing that will not work (yet!) is the login/logout. I need to rework our authentication system in order for this to work. This will for sure need to happen before the end of summer.