Building a Rails API to Track Student Progress
Tags: rails, activerecord, ruby, sinatra
Published: 2022-04-08 01:03:01

What is the most difficult thing about being a teacher? Actually, that's a trick question. Everything is difficult, hence, my switch to becoming a software developer. But one of those difficult teacher tasks is gathering data, as a means to guide your instruction, along with a way to show parents that you know their children well. As a teacher, I was always hoping to find the balance of a fun, engaging app that actually taught my students, while giving me some information about them after they finished.

As a 3rd grade teacher, I tried to make sure every one of my students was able to walk away with the ability to recite their multiplication facts in no time. But it was difficult to keep track of what each of them knew. So, after a switch to programming, I began my quest to create an application that could track students' knowledge of the times tables.

<h1>The Database</h1>

I'll be covering the set up of the database using Ruby on Rails and ActiveRecord. Setting up the frontend is a whole other story, but what we're really after here, and what a teaching application should create, is data.

The structure of a classroom lends itself easily to being created using Active Record associations. Here is how we can set up our models:

**Teacher** model - has_many :classrooms, has_many: students, through: :classrooms

**Classroom** model - has_many :students, belongs_to: teacher

**Student** model - belongs_to: classroom

As a teacher, sometimes you need data on individual students, other times you'll need data on your whole classroom. Think about how you'll want to access the data throughout whatever app you have planned.

If you want the teacher to have simple access to all of their students, you can include that association.

```
class TeacherSerializer < ActiveModel::Serializer
  attributes :id, :username, :is_teacher

  has_many :classrooms
  has_many :students, through: :classrooms

end
```
With this set up through your ActiveRecord associations, an array of students will now be available at the first level of your data that you fetch, along with all of the data they hold. Though you can serialize their data and return their classroom_id, they will not be nested in the second level within a classroom. You'll need to handle that logic on the front end.

Another option is to take away the *has_many :students, through: :classrooms* association, and instead serialize your classroom data to include the students.

```
class TeacherSerializer < ActiveModel::Serializer
  attributes :id, :username, :is_teacher
  has_many :classrooms
end


class ClassroomSerializer < ActiveModel::Serializer
  
  belongs_to :teacher
  has_many :students

  attributes :id, :classroom_name, :students
```

<h1>Create a Problem model</h1>

In my case, building an app to track student knowledge of the times tables, I created a **Problem** model, with 100 entries, each one containing a problem (string: "6 x 7"), and an answer (integer: 42). 

In order to create student data to track, I needed to create a joins table through my ActiveRecord associations to keep tabs on a student's progress. 

<h1>Create a Mastery model</h1>
The **Mastery** model is a table created to house a student's data on each of the entries in the **Problem** model. (You could alternately think of this as a **Quiz** model, rather than one single problem.)

Now to set up the joins the table:

```
class Problem < ApplicationRecord
  has_many :masteries
  has_many :students, through: :masteries
end
```
```
class Student < ApplicationRecord
  has_many :masteries
  has_many :problems, through: :masteries
end
```

```
class Mastery < ApplicationRecord
  belongs_to :student
  belongs_to :problem
end
```
Each time a student takes a quiz, you can create a new instance of a Mastery (or Quiz), giving it attributes such as :times_answered, :times_correct, :mastery_level, or custom methods, such as :mastery_percentage. And thanks to our associations, each instance of a **Mastery** will also have a :student_id and a :problem_id.

In the case of the multiplication tables, students will review facts multiple times, so their scores can be updated regularly to reflect how many times each fact was answered and how many times it was answered correctly.

<h1>Customizing our Data</h1>

Now comes the fun part. What sort of data do we want our teachers to have access to? We can build custom methods to return whatever we think will be valuable information about the students. 

```
class Student < Application Record
 
  def mastery_percentage 
    self.masteries.average(:total_score).to_f
  end

  def difficult_facts
    self.masteries.where("total_score" < ?", 20) 
  end

end
```
In the first method, we make a call to the database, which will return to us a method for each student, searching through each of the student's **Masteries**, and returning the average of all of them. In the second custom method, we're returning a custom list that searches through each student's masteries and finds all cases where their :total_score is less than our chosen number.

Just make sure you add your custom defined method into your **Student** serializer.

```
class StudentSerializer < ActiveModel::Serializer
  attributes :id, :username, :mastery_percentage,  
  :difficult_facts, :classroom_id, :masteries,
  
  belongs_to :classroom
  has_many :masteries

end
```

Teachers deserve all the help they can get. Educational apps with well thought out databases are just what those teachers are looking for. 


Photo by <a href="https://unsplash.com/@johnschno?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">John Schnobrich</a> on <a href="https://unsplash.com/s/photos/students?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>