Posts

DocuTube Dev Update

Some quick notes of my progress on updating DocuTube in the Docs Addon store:

Search by Default




  • Search videos in the addon popup window
  • Video, channel, and playlists can be specified in the search types
  • Results are paginated 15 videos at a time

Preview and Embed

Preview and choose an embed method in the popup window.

With this update, you'll be able to determine what kind of embed you'd like to make. If you choose text, it defaults to the title of the video, but you can also type in custom text ("Watch this video") and it will be linked automatically.

Known Issues

One of the major problems I'm going to face is the YouTube Data API quota. Applications are restricted, by default, to 10,000 'units' of data used per day from the API. Each call to the API has a cost that needs to be managed.

Right now, each pagination step is using the YouTube API to pull data down, which uses some of the allotted data. To make sure the app doesn't run out of resources, I'm probably going to implement some kind of simple cacheing, either in the script itself or in the browser's sessionStorage or localStorage cache.

This came up because I hit my quota limit while testing. Now, I'm calling a lot of videos to make sure it's all working - way more than a normal user would during normal use (preseumably). But, it's still a concern because the quota can get used up very quickly if I don't include some kind of cache mechanism.


The featured image is a screenshot from DocuTube. It is featuring Paul Andersen of Bozemanscience on YouTube.

The Boot Loop Returneth

I have an aging Macbook Pro. It's older than my children and starting to show it's age. Before I remove Mac OS and replace it with Linux, I'm trying to squeak a couple more years out of her.

Insert comment about planned sunsets and hardware longevity.

Unfortunately, my model is one where the AMD GPU was poorly built and designed and will eventually fail. Luckily, the Mac has two GPUs on board and you can disable the failing AMD chip by default and run on the Intel chip also included. This is fine for me because I'm not doing any heavy lifting with that computer anymore.

There's already a site detailing the steps below, but as with all things on the Internet, I'm creating my own backup in case that site goes down.


Boot into single user mode with:

Command + S after the first boot chime.

mount root partition writeable

/sbin/mount -uw /

make a kext-backup directory

mkdir -p /System/Library/Extensions-off

only move ONE offending kext out of the way:

mv /System/Library/Extensions/AMDRadeonX3000.kext /System/Library/Extensions-off/

let the system update its kextcache:

touch /System/Library/Extensions/

wait for the kextcache process to finish
then

sudo reboot

The system doesn't know how to power-management the failed AMD-chip. For that you have to either manaully load the kext after boot by:

sudo kextload /System/Library/Extensions-off/AMDRadeonX3000.kext

Automate this with the following LoginHook:

sudo mkdir -p /Library/LoginHook
sudo nano /Library/LoginHook/LoadX3000.sh

with the following content:

#!/bin/bash
kextload /System/Library/Extensions-off/AMDRadeonX3000.kext
exit 0

then make it executable and active:

sudo chmod a+x /Library/LoginHook/LoadX3000.sh
sudo defaults write com.apple.loginwindow LoginHook /Library/LoginHook/LoadX3000.sh

In the Terminal (or in single-user mode if you can't boot to the desktop in Safe or Recovery modes)

sudo nano /force-iGPU-boot.sh

Enter the following:

#/bin/sh
sudo nvram boot-args="-v"
sudo nvram fa4ce28d-b62f-4c99-9cc3-6815686e30f9:gpu-power-prefs=%01%00%00%00
exit 0

Now make that executable:

sudo chmod a+x /force-iGPU-boot.sh

The boot loop (or a boot hang) can be solved with a PRAM reset (Opt + Cmd + P + R, wait for a double chime), but this wipes the GPU modifications from memory. If you can't boot to the desktop, boot to single user mode:

<Cmd>+<s> after the first chime

And after mounting your boot-volume read-write to execute just:

sh /force-iGPU-boot.sh

This will reset the GPU settings to ignore the failing AMD chip. Reboot with sudo reboot in the command line.


Featured image is Spiral stair (21st century) by alasdair massie is licensed under CC BY-NC-SA

Automating Things Worth Reading

The more I use Tiny Tiny RSS (TTRSS) for keeping up with blogs, the more I find it can do.

For instance, I can publish specific feeds of curated items using tags, which is sweet. I acutally used that method to gather standards-based grading articles for teachers at school to read as they have time.

But what about others? Well, TTRSS also allows you to re-publish articles to a general feed. They can come from anywhere, not just tagged or labeled items. Hooking this up to IFTTT, I can now reshare articles to Twitter - linked to the original source - without so much clicking.

If you want to subscribe right to posts I find extra interesting that's your choice and you can do whatever you want.

Spit and Tape to Get Some Data

We're using Canvas LMS in our district. There are some things I appreciate and things I appreciate...less. One of which is when a main feature doesn't work.

Luckily, the Canvas API gives me some leeway in fixing things that Instructure doesn't seem know is broken.

I'm supporting teachers using Outcomes in their courses. Assignments can be tagged with relevant standards and recorded in a nice color-coded chart over time. I came across a big problem where students couldn't see their aggregated scores. There's no point in giving rubric feedback if students can't see those scores.

I thought it might be a fluke, so I tried reaching the student results through the API. It works as a teacher, but not as a student. There's a pipe broken somewhere on the Internet.

[File large bug report, crack knuckles.]

The approach

The easy fix would have been to hit the Canvas API to get this fixed, but student accounts came back with no data. So, I needed an intermediate, server-side account to do the work.

Earlier this semester, I set up an internal web app to manage reconciling grades. This was built in Flask so it was easy to add an endpoint as an API. I've been really digging the canvasapi Python package from UCF because it's so versatile and makes working with objects so easily.

So, I ended with:

  • JS client listents for the user to click on a tab in the gradebook.
  • Hit the Flask endpoint to start the server call.
  • Return the requested objects.
  • Process the objects in the client and append to the page.

The code

Here's the commented code for a simple implementation using Flask and some Javascript.

A Quick Grading Case Study

I ran the following example with a small group of teachers evaluating standards-based grading in their PLC. The teachers are on board (and all are attaching standards to work for feedback and assessment) but they needed to see a tangible example of why standards-based grades can help all students.

I'm going to steal Dan Meyer's Three Act structure with no shame. Each screenshot uses the same four (fictional) students.

Act 1

https://blog.ohheybrian.com/wp-content/uploads/2019/09/2019-08-26_09-18-19.png

Questions for discussion:

  1. What can you tell (or infer) from this information?
  2. What cannot be inferred?
  3. Which student would you focus on for intervention? Why?
  4. What feedback would you give to each student?

Act 2

https://blog.ohheybrian.com/wp-content/uploads/2019/09/2019-08-26_09-19-15.png

Questions for discussion:

  1. What do you notice?
  2. Which student would you focus on for intervention? Why?
  3. What can you infer from this information?

Act 3

https://blog.ohheybrian.com/wp-content/uploads/2019/09/2019-08-26_11-30-52.png

Questions for discussion:

  1. What do you notice?
  2. What can you infer from this information?
  3. Where would you focus your interventions? Why?

We're using weighted categories for our students. Looking only at classwork (Act 1) doesn't show teachers gaps in the student learning. You can certainly target students for intervention, but it is based only on the the task completion, not necessarily the content.

In Act 2, we have a little more to go on because each assignment is aligned to a specific standard or skill. The big takeaway is that the student with the lowest assignment score (row 3) is actually learning all of the standards. The learning gaps are hidden for the "responsible" student who turns their work if we don't take standards into account.

Act 3 brings it home for teachers. Where do the 1's and 0's come from? It's from aggregated information over time. In this view, color coding (we have set up through Canvas) is a quick gauge of class comprehension on each standard. I can use this information to plan more effectively to help all students reach learning goals.

In the end, teachers wanted to know the student's calculated score. This table shows what would be on the student report card:

Student Classwork (20%) Standards (80%) Final score (%)
1 85.2 33.3 43.4
2 70.4 50 54.1
3 62.7 100 92.5
4 81.5 66.7 69.7

Standards-based grading can help root out lack of learning by moving the focus away from compliance. Assessing learning goals and making them the focus of feedback and reporting helps make that change a reality.

Setting Custom Search Engines

This post is specifically for browsers that use the Chromium engine (Chrome [duh], but also anything on this list) but most browsers have a similar feature, you'll just need to dig for it.

Setting a custom search keystroke is helpful because you can target your results without any hassle. For instance, when I type "dr" into my search bar before my terms, my browser only returns items in my Google Drive. It makes my seaching faster because I don't have to open a site first.

In Chrome, go to your Settings. (A quick way to get there is to type chrome:settings into the address bar.)

In Settings, find Manage Search Engines. This is where you can specify some custom places to look.

Manage Search Engines is found on the Chrome Settings page.

Each search engine has to be configured with two pieces: the search url and the search term. Depending on the site, this can be obvious or really unclear. The best method I've found is to go to the site, do a search, and then do some copy/pasting.

Let's say you want a quick search for YouTube. If you open YouTube, do a search for something. We'll use dogs because why not. When you seach, you get this URL:

https://www.youtube.com/results?search_query=dogs

We're going to replace dogs with a special placeholder: %s. This tells the browser to substitute that term with what you typed.

https://www.youtube.com/results?search_query=%s

Click on Manage Search Engines and then click on Add. In the pop up, type in the name of your search engine and then a custom key (or keys) to trigger that search. Then, paste in your search address (see above).

Create a custom search with a name, a key combination to trigger the search, and a URL to search from.

Save the search term with Add. If you open a new tab and type yt, you'll see a prompt to search YouTube by pressing Tab.

Helpful Search URLs

Here are some of my most used searches so you don't have to go and make your own:

Site Trigger Search URL
YouTube yt https://www.youtube.com/results?search_query=%s
Google Drive dr https://drive.google.com/drive/search?q=%s
Amazon ama https://www.amazon.com/s?k=%s
Unsplash un https://unsplash.com/search/%s
Flickr fl https://www.flickr.com/search/?text=%s

Creating a New User Onboard in WordPress

I'm finishing up a website for a private project which allows for people to register and create posts as members. The client wanted to have a specific onboarding flow which moved users from account creation to a terms page before finishing on a brief site overview.

I started by hooking into registration_redirect and pushing the user to a simple terms page.

add_filter( 'registration_redirect', 'new_user_redirect' );
function new_user_redirect() {
    return home_url('/complete-registration/');
}

This didn't work because the user was immediately redirected to the terms page instead of on their login. This meant my terms form (using Gravity Forms) couldn't collect the user_id field because it didn't actually exist yet.

To fix this, I hooked into the user_register action to capture the new user. There are several validation steps on the front and backend that I won't detail, but in the end, the user is written to the database and emailed a secure link to set their password. I created a new user meta key called is_first_login and set it to true. This was added when the user was written to the database.

update_user_meta( $user_id, 'is_first_login', true);

Now, I can check that key and send the user to the correct page when they log in.

add_action( 'login_redirect', 'redirect_user_on_login', 10, 3);
function redirect_user_on_login( $redirect, $request, $user ) {
    if(get_user_meta($user->ID, 'is_first_login', true)) {
        // Since it's the first login, set this to false
        update_user_meta($user->ID, 'is_first_login', false);

        // push to the terms page
        return home_url('/complete-registration');
    } else {
        // Check the user role and redirect appropraitely
        return (is_array($user->roles) && in_array('administrator', $user->roles)) ? admin_url() : site_url();
    }
}

If it is a new user, they are shown the terms page. Accepting the terms allows for accountability. Gravity Forms handles the next redirect when the user submits the terms form.

Canvas LMS is Missing a Big Opportunity with Outcomes

Every year, I'm building more support for standards based grading within our district. Though Canvas isn't really set up for SBG, there is a way to make it work, and it works well. In short, every assignment, whether it's a test/quiz, classwork, or even a conversation with students, can be linked to a standard which is aggregated and reported over time.




Current status of Instructure's outcomes roadmap.

With some of the recent Canvas updates, this system is eroding and Instructure is missing out on a huge opportunity to truly change the way we approach grading. This post is a breakdown of the changes and what could be done to fix them.

Reporting Outcomes

When Outcomes are attached to assignments, reporting is...okay. The Learning Mastery Gradebook view (which is toggled in the Course settings under Feature Options) is a helpful color-coded chart of any assessed outcome. Hovering the Outcome column title shows a context card with class averages and the outcome detail. But that's it.

If you switch to the Individual View in the gradebook, you can click a student name and then use the Learning Mastery tab to see all aligned Outcomes and their scores. This view gets closer to being helpful because it shows scores on assessments over time, which allows you to track progress (growth vs decline) in a chart.

To see reports organized by Outcome, you can go to Outcomes and then click on the title of the individual item. This shows the assignments it was assessed on and a list of students who were assessed. This list is not sortable and can be many, many pages long.

This is just to demonstrate that there is no consistency on where to find the information, which makes Outcomes less compelling to use.

Quizzes.Next and Outcomes

Canvas is building out a new Quiz engine called Quizzes.Next (though it will soon become 'Quizzes' and the old style will be 'Classic Quizzing'). While there are certainly some functional improvements, Outcomes are being left behind.

One benefit of Quizzes.Next is that Outcomes can be added to individual questions. This could be done in Classic Quizzing by using Question Banks, but the banks were never really exposed to teachers and alignment is a bit of a chore to find. I have more detail on our instructional tech blog if you want to see the process.

The Problems

There are three main issues:

  1. Quizzes.Next takes 24 hours to show course outcomes. I don't know why this limitation is in place, but I'm assuming it's because Quizzes.Next is built as a standalone LTI app and the data sync is metered. This hinders quick creation of reassessment.
  2. Question banks are now linked to the user, rather than the course. Individual teachers now maintain their banks rather than being attached to the course. In theory, teachers can share these banks like they would other items, but it adds a layer in the import process.
  3. This is the biggest: outcomes scores in Quizzes.Next do not report to the learning mastery gradebook. The mastery gradebook is the aggregate of all outcomes over time. Not reporting these assessments adds a step for teachers to have to process them in the course as a whole.

Removing the step of creating course-level question banks and moving it to the quiz creation step arguably makes outcome alignment more accessible. But, given that they do not report back erodes the value in aligning outcomes at all.

Solutions

While I'm not going to hold my breath, there are a few ways Canvas could make Outcomes more usable as they move forward with some of their development strategies.

  1. Make Outcome results easier to find. There is no reason they need to be buried. I submitted an idea with screenshots on how to display student results in the current structure...it's simply a link in the Outcome context card to take you to alignments. Canvas hasn't made any moves to make this easier.
  2. Keep Question Banks aligned to the course, not the user. I teach several courses and my banks are structured as such. When I'm building a quiz, I want my banks to stay in context. Searching through all of my question banks makes the process harder. Limiting banks to specific courses helps with organization and reporting.
  3. Push Quizzes.Next Outcome results to the Learning Mastery Gradebook. I'm still confused as to why this is a feature request. It is feature parity. If Canvas is going to remove functionality in a systems update, it makes the system less usable for instructors. The workaround, currently, would be to manually add a rubric to the quiz and then score it for each student based on the Quizzes.Next Outcome report. Workarounds are not solutions to problems.

If Canvas takes reporting seriously, they'll understand that it goes beyond assignment averages and test scores. Outcomes and their attachment to items in Canvas can really open doors to more accurate and equitable reporting. Unfortunately, most of the decisions coming from leadership are making this harder instead of easier.

Comments

No, Retakes Are Not a Bad Idea

There's an Edutopia article cycling around which argues against allowing retakes on assessments. The author builds a case focused on motivation, mental health, and teacher effectivness.

Instead of doing a large breakdown here, I annotated the original article with my pushback in context.

On a semi-related note, If you don't have the hypothes.is extension installed, you should grab it and leave replies on the original article. Comments are also welcome here.

On Being a Parent of Four

We had our fourth child, a boy, on June 28th. Since then, life has sped up in some ways and slowed down in others. I'm fortunate to have the entire summer off, the majority of which was with the baby. We've been able to focus on family time, the garden, and planning for our oldest to go to school in the fall. I've had some side projects here and there, but nothing compared to the magnitude of last summer's work.

I remember thinking ahead to what it would be like to have four kids at home. Some things I was right about, others...not so much.

We've come into a habit where one of us is with the baby and the other has the three older kids. It isn't 100% of the time for sure, but it's familiar. We take turns focusing on E and the day to day routine doesn't really change for the older girls.

It was hard to remember to call him "him." We had to erase "the girls" from our vocabulary when referring to all of our children collectively.

I tend to want to watch movies when I'm up late with E and read books whem I'm up early with him. Not sure why.

I head back to work in about two weeks and we're working on stepping up to full days away. The older girls have done a really amazing job playing together kindly, involving their youngest sister (still just 18 months old) in their adventures outside.

I think my biggest lesson has been to let go of some of the control I feel I need to have. If the kids are filling the pool with grass clippings and making pond scum, more power to them.

Brute Force Bike Lock

Two years ago, we took our kids to the north shore of Michigan for a camping trip. Part of that included a day on Mackinac Islad with the girls and the bikes. Living in the country, I'd never had need for a bike lock. But, heading to the island without one didn't seem like a good idea, so we got a word-combo lock for the bikes for a few bucks.

Two bikes next to one another A sign for M-185 on Mackinac Island

Fast foward two years. I got the bike back out because this summer, aside from having anoter baby, I don't have any major house renovations to do.

There was the lock. Not in the way of anything, but it was a little annoying to have hanging on the frame.

My memory isn't as good as it used to be.

We started by breaking out a pad of paper and looping over each dial, making a list of words changing one letter at a time. This took...a long time to do.

My wife is much smater than me and did a Google search for words used my Master Lock. Beacuse the Internet is a wonderful place (sometimes) she found a page which not only detailed Master Lock's four-dial lock but also provided a link to a Google Sheet of each of the words, in order.

An hour later with nothing more than slightly cramped fingers, I was in.

The opened bike lock in my living room. The bike and computer are in the background.

I wrote the combination down this time.

What is a Grade?

I had the pleasure of working with about 15 people yesterday on moving to standards-based grading next year. We started off with a long discussion about what grades are and what they mean. It's easy to get into what they should be, but I wanted to make sure we all had a solid understanding of what grades actually do in most of our classrooms.

I had a couple of guiding questions and one that generated the most interesting response was the following:

A student rarely comes to class and when they do, work isn't turned in. At the end of the semester, that student easily passes the final exam. Does that student pass your class?

Lots of eyebrows furrowed.

There was some uneasy looking around.

About half said yes, the other half said no.

Now, there are major assumptions here. Is the test valid and reliable (standards-aligned)? How did the teacher intervene? Did a student show growth before taking the test in some other way?

All issues aside, the root of the question forces us to consider whether a grade in our class represents learning or compliance.

Better than them doing well all year and then flunking the final exam?

—Brandon Dorman (@brandon_edu) June 13, 2019

I also wonder why we're more accepting of the inverse situation: a student who has not taken the class who passes the final is allowed to skip the course (or is given credit, etc).

If we're comfortable with allowing students to skip a class (be given credit) by testing out we should be just as comfortable allowig a student who "shows no effort" to be given credit for hitting the same benchmark. The difference is our perception of that student.

Challenging our biases is important, particularly long-held assumptions that dictate our perceptions about "good" vs "bad" students. Grades are the output of those biases in many cases.

What do you think?


The featured image is Br... flickr photo by Peter Schüler shared under a Creative Commons (BY-NC-SA) license

Change Course Role in Canvas via the API

Continuing my Canvas excursions...

We recently made a change where teachers could not manually add students to their courses. The change was made because manually enrolling a student breaks the grade passback to our SIS, which causes double the work to fix the problem in the first place.

But, this also affects manually-created courses that don't need the grade passback. One workaround is to add all students as a TA or teacher, but then you run into issues where students have access to grades.

The API doesn't allow you to directly change a user's enrollment status. You need to delete the user object from the course and then re-enroll to change the status. The change in the UI on the website does the same thing, but they make it look nice with a dropdown and it doesn't actually tell you that the user was deleted and re-added all in one step.

The nice thing about the API method is that you can set their enrollment state to active by default, which removes the course invitation notification and acceptance step for someone who is just having their status changed.

The example below is what I used to convert all students who were added as TAs back into Students. As always, I'm using the UCF Open Python library to handle the API calls.

from canvasapi import Canvas

canvas = Canvas('your_url', 'your_key')

course = canvas.get_course(course_id)

enrollments = course.get_enrollments(type='TaEnrollment')

for stu in enrollments:
    user_id = stu.user_id

    # the `deactivate` method takes a **kwarg to define what type of deactivation.
    # accepted strings: ['conclude', 'delete', 'deactivate', 'inactivate']
    stu.deactivate(task='delete')
    course.enroll_user(user_id, 'StudentEnrollment', enrollment_state='active')

This does wipe out the 'Last Activity' field in the People tab, so if that's important to you, make the change before the course is published. I made the change for a user, going from student to teacher and back with no loss of grade data, which was nice to see.

Creating a Curated RSS Feed with TinyTiny RSS

I've been using Tiny Tiny RSS (TTRSS) for several months now and I'm finally getting into some of the more advanced uses. It's more than just an RSS reader - it can be an RSS curator which makes it so much more powerful.

Here's what I mean.

TTRSS can collect and categorize feeds like any other reader out there. I have mine grouped by topics I'm interested in. Each installation also has something called a Generated Feed which allows me, the consumer, to republish my own curated feed with any article I want to share.

A unique URL is generated for my installation. Each article has a publish option that adds it to my public feed.

https://blog.ohheybrian.com/wp-content/uploads/2019/06/2019-06-07_07-58-29-1.jpg

Here's the generated feed, if you're curious. You can subscribe to this if you want to know what I think is worth reading. I can also hook this into IFTTT to auto-tweet new items, etc.

That's one layer. What if I want to share curated articles based on a topic? I can only have one published feed for my account.

That's where labels come in.

If I create a label - a custom tag, essentially - in TTRSS, it also has a feed I can publish out.

Woah.

Even more, there are filters in TTRSS, kind of like Gmail, which can automatically add a label to an incoming post. This is triple powerful because I don't have to manually mark articles I want to share out.

Here's a full example of how I'm taking advantage of this:

Next week, I'm kicking off a standards-based grading cohort with ~20 teachers from across my district. I want a way to easily curate and share articles with them. Instead of emailing everything, I'm going to use TTRSS filters and labels along with Diigo to collect SBG reading and share it all out in one, continuously updated place.

First, I set up the label in TTRSS. That created this RSS feed pushing back out.

Any post in my incoming feeds can be labelled with SBG which publishes it back out to the world.

Next, I set up a tag in Diigo for SBG-related stuff. This is anything I come across on the Internet that isn't from a blog feed. It can be YouTube videos, PDFs, newspaper clippings...whatever I want. Diigo gives a good RSS feed of tags and labels, so I ingest that with TTRSS and use a filter to automatically apply my SBG label, which then updates my outgoing feed.

TTRSS is becoming less of a pipeline in to me and more of a packaging complex which takes information in and allows me to publish it back out to serve a purpose.

RSS isn't dead.


Featured image: Pipes flickr photo by derekbruff shared under a Creative Commons (BY-NC) license

Lightning Fast Spotify Playlists

Update

After posting this and tagging WDBM on Twitter, they sent the following:

Hey Brian! We upload our playlists on Impact89fm’s Spotify every week! Thank you for the support, happy listening! ?

—Pity Party (@pityparty_wdbm) June 5, 2019

If you want to listen, just search next time ¯\_(ツ)_/¯


Python has been my programming language of choice lately. Today, I gave myself a little challenge to create a Spotify playlist from a tracklist posted to a website.

I'm a big fan of WDBM out of Michigan State University. They have a great college station that reminds of my the music scene back in Rochester, NY (what was awesome). Every week, they have a live show called Pity Party and it highlights alternative/emo/rock goodness. I try to catch the show if I can, but I often miss it because I'm not always near my computer to stream.

They post their playlist each week on their website. I fired up a Python project with BeautifulSoup and requests to get the web page data and a new (to me) library called Spotipy which gave me API access.

This happens in a couple steps. The first thing to do was scrape the web page, which is super easy with BeautifulSoup and Requests. The website uses the same format for their playlist each week:

<span class="storycontent">
  <p>Track 1 - Artist 1</p>
  <p>Track 2 - Artist 2</p>
</span>

BeautifulSoup lets me set up a quick loop to grab each of the <p> tags in a list that I can loop over.

The Spotify API allows you to search by artist and track name. If a single result is returned on the search, its ID is added to a list to post in bulk to the playlist. This is more efficient than looping each one individually.

Any track that isn't found for whatever reason is added to an errors list that is shown to the user when everythig is done. That way, they can go back and check them manually. It may be that the track doesn't exist or their was some weird punctuation or something.

Instead of taking 20 minutes to search and add each song manually, this runs in less than 10 seconds.

https://blog.ohheybrian.com/wp-content/uploads/2019/06/typing.gif

Rock on.

Here's the full script if you're interested in checking it out. The entire WDBM specialty show catalog uses the same format, so you can try it with other pages over ther.


Featured image is lightning flickr photo by Tom Gill. shared under a Creative Commons (BY-NC-ND) license.

Methods of Calculating Grades in SBG

I'm prepping a full-day workshop on standards based grading for about 20 teachers in a couple weeks. One major part of the day will be centered on converting a SBG report to a 100-point scale letter grade, mostly because we just have to.

Here are some of the methods I've come across, which have all (in one way or another) informed my own method, which is last in this post.

Equalized Weighting

I saw this calculation method first from Frank Noschese on his KISSBG blog post. He glances over it in the post body, but the comments below get into some of the details. Here's the formula:

50 + 50 * (earned/total)

At first, the additional 50 points added in look like a bonus, which feels weird. In reality, this wipes out the 0-50 F range. Now, each letter grade rougly corresponds to a 10-point spread:

  • F: 50 - 60
  • D: 61 - 70
  • C: 71 - 80
  • B: 81 - 90
  • A: 91 - 100

It's an equivicator, not a bonus.

Reflective Grading

Shifting away from assigning arbitrary points is a big piece of standards-based grading. Laura Gibbs, Kathryn Byars and Ken Bauer are the three names that jumped out in this region. Feedback is the main driver. Work is given feedback and only feedback. The focus between teacher and student is on demonstration, not on points or numbers.

For assessment, students reflect on and provide evidence of proficiency on each standard. Laura, Kathryn, and Ken all did this differently, but the main flavor is the same. Take a look at Kathryn's helpful Google slides, Laura's deep-dive book chapter and Ken's various blog posts. This is by far the most flexible, fuzzy, and subjective method of reporting.

Standard Purism

The most "pure" method of standards-based grading removes all items from the gradebook except for the standards. The methods of grading these varies. Some use a straight average of binary items (pass/fail). Others put each standards on some kind of rubric scale and give an average.

The main benefit of this structure is that practice work (homework, classwork, etc) is excluded. If a student forgets or decides not to do an assignment, their grade is not affected because it is practice.

On the other hand, this opens the door for assignments to be completely optional. This is a detriment, in my opinion, because students may not have the self-awareness or diligence to do independent work otherwise. Additionally, if a student skips a test or quiz because it doesn't go in the gradebook, it can set up an awkward situation where a student is racing to prove standards at the end of the year.

Some kind of blend

I ended up blending several of these ideas into a system I like. I used components of KISSBG (binary yes/no for standards) with a weighted course average to calculate the final grade.

Category Weight
Classwork 20%
Standards 80%

In my gradebook, any classwork/practice was lumped together into one category. Homework, tests, quizzes, etc, all contributed to 20% of the total course grade.

Standards were individual assignments worth one point. They were assessed over time on a four-point rubric:

Description Score
Exceeds Expectations 4
Meets Expectations 3
Approaches Expectations 2
Does Not Meet Expectations 1
No evidence 0

The cutoff for toggling a 1/1 in the gradebook was a 3. This meant they demonstrated proficiency in the concept in that situation. A 4 was given if the student could connect different related ideas...showing the relationships between standards.

Rubrics were used on every assignment and that aggregate score was used to determine the gradebook 1 or 0. Over time, patterns emerged and students were able to track their growth/decline in Canvas (more on that another time). I rarely graded Classwork assignments in depth...if it was turned in, I often gave full credit just for having it done. The rubric feedback was the important piece and I tried to put the focus on learning from those pieces.


Is ther a best method? I don't think so. It really depends on your group of students and situational context. In 2012, I used a more reflective approach. In 2016, I was using more the 80/20 split with some reflection thrown in. Both were equally valid and I felt good about the grades I ended up reporting.

What others would you suggest? Leave a comment below.


[caption width="500" align="aligncenter"]2018-11-08-Couthuin-et-environs-automne-45

Adding Assignment Statuses to the Canvas SpeedGrader

Here's another little script I hammered out for Canvas today.

With the new gradebook, you can set assignment statuses like "late" and "missing." This is helpful in the gradebook for on paper assignments (digital assignments are automatically flagged) but you can only change the status in the gradebook grid.

This is a hacked together script to add the same buttons to the SpeedGrader controls.

https://blog.ohheybrian.com/wp-content/uploads/2019/05/2019-05-29_14-45-39.gif

The easiest way to add this is by adding an extension called Tampermonky. This essentially allows you to run code on websites you don't have access to edit.

After installing the extension, click here to install the script.

Last step: click on the Tampermonkey Icon, choose Dashboard, and then click on SpeedGrader Status. In the editor, update line 14 with your Canvas URL.

Comments

Paul Bui

Hey, I just wanted to thank you for this blog post. I forked your code, fixed a bug, and added modification of the late days.

https://github.com/paulbui/canvas-tweaks/tree/master/speedgrader_status
Brian Bennett

Nice! I always worry a little about sharing these tweaks because of bugs, so I appreciate you sharing the updated code back.

Managing Standards Based Grades in Canvas LMS

I'm trying to make standards-based grading more approachable for my teachers. When I was teaching full time, I held to Frank Noschese's Keep It Simple philosopy. Single standards correlate to single assignments that are scored as pass/fail. Now, I averaged these out on a weighted scale to calculate a 0-100 grade, but that's for another post

Using Canvas, I was able to set up a functional reassessment strategy to aggregate demonstrations of proficiency.

The Learning Mastery Gradebook in Canvas does not translate anything into the traditional gradebook. This mean that every week or so, I would have to open the Mastery report alongside the traditional gradebook and update scores line by line. This was tedious and prone to error.

Using the Canvas API and a simple relational database, I put together a Python web app to do that work for me. The idea is that a single outcome in a Canvas course is linked with a single assignment to be scored as a 1 or 0 (pass/fail) when a mastery threshold is reached.

The app

Users are logged in via their existing Canvas account. There they are shown a list of active courses along with the number of students and how many Essential Standards are currently being assessed (ie, linked to an assignment).

The teacher dashboard

In the Course view, users select which grading category will be used for the standards. Outcomes are pulled in from the course and stored via their ID number. Assignments from the selected group are imported and added to the dropdown menu for each Outcome.

The alignment menu

Users align Outcomes to the Assignment they want to be updated in Canvas when the scores are reconciled. This pulls live from Canvas, so the Outcomes and Assignments must exist prior to importing.

As Assignments are aligned, they're added to the score report table.

Score reporting per assignment

Right now, it defaults to a 1 or 0 (pass/fail) if the Outcome score is greater than or equal to 3 (out of 4). All of the grade data is pulled at runtime - no student information is ever stored in the database. The Outcome/Assignment relationship that was created tells the app which assignment to update for which Outcome.

When scores are updated, the entire table is looped. If an Outcome has risen above a 3, the associated Assignment is toggled to a 1. The same is true for the inverse: if an Outcome falls below a 3, the Assignmet is toggled back to a 0.

I have mixed feelings about dropping a score, but the purpose of this little experiment is to make grade calculations and reconciliation between Outcomes and Assignments much more smooth for the teacher. It requires a user to run (no automatic updates) so grades can always be updated manually by the teacher in Canvas. Associations can also be removed at any time.

As always, the source for the project is on GitHub.

10 Years

Today is my 10th anniversary.

Mackinac Island, 2013 Easter, 2014 Wilderness State Park, 2017 Summer 2018

Making RSS Feeds for Instagram

tl;dr: I have a hacky proof-of-concept method for getting an Instagram account as an RSS feed. It uses Python and you can grab the source files here.


I'm not on Instagram because I got tired of only seeing three people's photos interwoven with ads. The problem is I still have friends who post frequently there and I feel like I should still be able to see those photos.

The wonderful thing about the Internet is that you can do things that weren't really meant to be done. Instagram (nor most other companies) provide RSS feeds anymore in order to force you into their platform. That's silly. I've been teaching myself Python and this seemed like a good way to flex some of my new powers.

Get that feed

Inspired by Andy Barefoot, who did some magic on his personal site with PHP, I decided to do the same using Python. What resulted was a command line program which can fetch any public Instagram account and create an XML document I could subscribe to.

I'm going to use Alane Levine as my guinea [STRIKEOUT:pig] dog for this post. To create the feed, run:

python subscribe.py cogdog

where the argument is the username of the account. I'm using a handly lbrary called PyRSS2Gen by Andrew Dalke to create the properly formatted feed. I ran the script and then threw it on my server and subscribed, just to see what would happen.

An RSS feed for an Instagram account. The feed exists in real life.

evil cackle

Update that feed

Instagram only shows 12 photos at a time. If I ran this script over and over, it would drop a photo from the feed each time it updated. That's no good.

I wrote up a second (notably more hacky) companion which takes almost the same form in order to update the feed rather than create one from scratch:

python update.py cogdog

This little guy looks for the existing XML doc and then fetches the user's Instagram page yet again. Instead of writing everything, it only writes things with timestamps newer than the most recent feed item. It's a little brute force, but hey, every tool can be a hammer if you swing hard enough.

Improvements

The subscribe script only loads those initial 12 photos. I may still go back and have it get the entire profile in the first go, but limiting it seems okay to me.

It's not general-purpose yet because you have to know how to install Python and several modules as well as have a web server to host the feeds on. I started finessing this into a small webapp which would do all the jobs, but my brain is stretched pretty far as it is.

If you want the source, it's in a GitHub gist and you can certainly tweak and improve. Let me know if you make changes or how I could do this better in the future.

Comments

Alan Levine

Holy cow, that's neat. I better post more photos for ya.