OLM On Rails

Groups

leave a comment »

The time has come to completely incorporate groups into checkmark. With various discussions with Karen and the crew here are the changes I am going to make to the schema and where I will be heading in the future. But first …

Where We Are

Currently, checkmark supports the simple case where an assignment has groups of a specified size. These groups can be made by the instructor manually or through a CSV upload (which is incompletely implemented – a little bit more on this later) and also by the students themselves. Basic group creation currently works with the current schema: (Which does not include the rubric or annotation stuff) which is explained by Geofrey here.

But, from a need for error checking groups on creation (mainly through the CSV upload) as well how classes like CSC301 run, I decided that we must incorporate a group name (auto-generated or provided) to distinguish between groups. From this we decided to do a small (and hopefully final) revamp on the schema to support the following 3 cases:

  1. Situations when students choose to work in groups but can choose to work alone on an assignment;
  2. Situations when groups do not persist across assignments;
  3. and situations when groups persist across assignments (But sometimes the members of a
    group might change)

To support these cases, there are a number of front end and back end changes that need to be made. The front end is fairly straight forward and so I am going to focus on the back end for this post.

Back end Changes

We decided to support these cases with very few changes to the schema. We are going to remove the assignments_groups table and maintain the groups-assignments associations solely in the memberships table with the addition of an assignment_id field.  So, the schema now looks like this (Sorry for the change of format from the above picture):

The above cases are now supported as follows:

  1. Students who choose to work alone will have one entry for the specific assignment in the memberships table, this is the same as the current situation with the addition of the assignment_id field.
  2. If groups do not persist across assignments, again, this is currently supported.
  3. If groups do persist, then each student will have a distinct entry in the memberships table for each assignment. This way, memberships can change across assignments by simply changing that students group_id for that assignment and all previous assignment memberships will be maintained. Every time an assignment with persisting groups is created, all the groups from the previous assignment will be copied over by duplicating the entries for the previous assignment and updating the assignment_id.
  4. But this situation is a bit more complicated. Most likely, instructors are going to set up all assignments at the beginning of the term and so the memberships entries for those assignments may already exist before the assignment is released. So, to ensure that each assignment has the most up-to-date group information, we will cascade any group modification actions that happen on any previous assignments to the future (and maybe currently open — by an extension) assignments. This is an expensive operation, but it is not likely to happen often.

It seems that few changes to the schema actually supported what we wanted, without sacrificing much performance (hopefully I am right in claiming this).

Where We Are Going

Now I am going to outline where I plan to go in the future.

Schema Changes

I am going to apply all the schema migrations within the next couple of days.

The relationships within the models do not actually change, since assignments_groups was simply a join table and does not hold any extra information on the relationship.

Changes to the Models and Controllers

  • I am sure there will be some refactoring that will need to be done, so I will revisit the old code and make sure everything that worked before still works.
  • I need to fully incorporate the group names, allowing the instructor to choose when the names will be auto-generated or provided by the user and when the group names should be viewed as well as who can view them.
  • Complete the CSV upload, with proper error checking and group creation.
  • Implement the functionality (front end and back end) described above for persisting groups.
  • … etc. I am sure there is more.

But before I make these changes, I am going to try something I have never tried before and which should be interesting…

Unit Testing, Unit Testing, Unit Testing and Test Driven Development

I am going to try my hand at test driven development and write all the unit tests required to (hopefully) adequately test all of the group functionality. Not only that, I am going to keep a journal of sorts and blog about it! So stay tuned for exciting unit testing goodness.

Written by Catherine

February 12, 2009 at 3:05 am

Posted in OLM on Rails

Prototype/Ajax.Request -> Rails SOLVED!

with one comment

Problem solved!  I’m super psyched – I can finally continue developing for our demo on Dec 3rd.  Whew!

The problem lay where I didn’t expect it – it was Firebug.  I was using an alpha version of Firebug, which hadn’t updated itself to the latest version.  It was capturing the AJAX POST request going out, but didn’t let the parameters carry through.  So, I updated to Firebug 1.2.1, and I’m set.  Everything’s jake.

Whew.  I was really starting to lose it, there.  Back to work…

-Mike

Written by mikec86

November 23, 2008 at 5:09 pm

Posted in Uncategorized

Prototype/Ajax.Request -> Rails – The Plot Thickens Part 2

with one comment

Still no solution in sight.  I’ve tried creating a brand new Rails project, and the behaviour persists there as well.  I’m convinced it’s a problem with Rails though, and not a problem in my machine.

Let’s go over the facts again:

  • I have Rails 2.1.0 installed on an Ubuntu 8.04 Hardy Heron Tablet PC
  • I’m using a Postgres database on the OLM project
  • Using Prototype JS library V 1.6.0.1
  • We’re using Mongrel, not WeBRICK
  • Using Ajax.Request with the ‘POST’ method does not pass any parameters to the params hash in the target controller
  • It works with GET though!
  • When using POST, I can see the parameters if I inspect the request object – they’re in @parameters, which I cannot seem to access
  • POSTing seems to work on other computers, using the same OLM code checked out from the repository
  • I’ve tried a complete reinstall of Rails, with the same result

Maybe my uninstall assumption is wrong:  what’s the best way to completely purge Rails from my machine?  I essentially followed these steps before:

http://www.youtube.com/watch?v=c0jR_hptpdA

Thoughts from anybody?

Thanks,

-Mike

Written by mikec86

November 21, 2008 at 3:04 am

Prototype/Ajax.Request -> Rails – The Plot Thickens

leave a comment »

So, no solution in sight yet – but I’ve localized the problem.

The parameters pass fine on Geofrey’s computer.  That’s good news.

The bad news, is that I did a full uninstall of Rails/Ruby on my machine, and then reloaded the whole thing from scratch…and the bug remains.

Super frustrating.

-Mike

Written by mikec86

November 19, 2008 at 5:29 pm

Posted in Uncategorized

Problems getting Prototype/Ajax.Request to talk to Rails

with 2 comments

I’m totally losing my mind over this problem, because it seems so simple, and yet I can’t seem to figure it out.

Here’s the situation:

I’ve begun linking up my Rubric Manager UI prototype to the Rails backend.  On this UI is a big “Save Changes” button, that implements all of the changes to the Rubric in one shot.  That button is linked to a Javascript function called save_changes.

Here is save_changes:

function save_changes() {
/* ....some code...*/
  new Ajax.Request('/checkmark/rubrics/modify',
  {
      method:'post',
      parameters: {authenticity_token: '<%=form_authenticity_token%>', key: 'value'},
      onSuccess: function(transport){
      console.log("Success");
  },
  onFailure: function(){ console.error('Something went wrong...') }
  });
/*...some more code...*/
}

So this seems alright to me.  I have to include the “authenticity_token” in my parameters because Rails seems to be using some type of internal request forgery protection.

When I click on “Save Changes”, however, Firebug tells me that the page I POSTED to threw an “InvalidAuthenticityToken” exception.  So that’s frustrating – I’m clearly passing the token in my parameters.  I created a tiny test form and POSTed to the same ‘modify’ method with the authenticity token as a hidden value, and it worked just fine.

So what gives?

Geofrey helped me get by the InvalidAuthenticityToken problem by inserting the authenticity_token variable into the URL string of the Ajax.Request target:

function save_changes() {
/* ....some code...*/
  new Ajax.Request('/checkmark/rubrics/modify?authenticity_token=43e5f5e3fbf3248b2a403f828010f8544ad36dbf',
  {
      method:'post',
      parameters: {key: 'value'},
      onSuccess: function(transport){
      console.log("Success");
  },
  onFailure: function(){ console.error('Something went wrong...') }
  });
/*...some more code...*/
}

But NOW, the parameter “key => value” isn’t being passed to the rails param map in the RubricsController.  I know this, because I inspect the param map in the Controller with the following code:

def modify
    render :text => params.inspect  
end

And here’s what is returned from the AJAX call:

{"authenticity_token"=>"43e5f5e3fbf3248b2a403f828010f8544ad36dbf", "action"=>"modify", "controller"=>"rubrics"}

So what’s going on?  Anyone.

-Mike

Written by mikec86

November 14, 2008 at 12:41 am

Posted in Uncategorized

Tagged with , , , , ,

Testing Rails with Authentication

leave a comment »

I’ve been putting off doing functional testing with rails for a while now because of the snag that we couldn’t figure out how to test with authentication.  Finally I’ve decided to solve this once and for all and get to the root of the problem. First approach we tried was to explicitly log in using a post request to the login page in the test setup. Of course, it didn’t work as each request I believe is independent of the subsequent requests, at least during functional testing.

The next approach we tried was to go down one level and expose the method that sets the current user for the session, and then call that as a controller function.  This made me a bit uneasy because we were making the current user setter public, which could probably mean that it would be exposed as an action that can be exploited.  Either way, this approach still didn’t work and was still giving us a 302 redirected status.

We decided to go one more level down and tried then to explicitly set the session hash, which is how we keep track of the current user.  One way that I came across was to set the session variable in a request object, that is

def setup
@controller = AssignmentsController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
@admin = users(:admin)
@request.session['uid'] = @admin.id # login before testing
end

or some reason, this didn’t work out either.  Finally, it turns out that session variables can be set on every (get/post/put/delete) request made as an optional parameter. Now, each of the request type methods are wrapped with an extra user parameter to make that request on behalf of that user. For example, we now have a get_as method defined as:

# Performs GET request as the supplied user for authentication
def get_as(user, action, params=nil, flash=nil)
session_vars = { 'uid' => user.id, 'timeout' => 3.days.from_now }
get(action, params, session_vars, flash)
end

Written by Geofrey

November 7, 2008 at 2:38 pm

Rubric Weight Management

leave a comment »

A few weeks back, I took my paper prototype for the Rubric Management control, and did some UI testing.  All went well, until last week, where I found a small flaw with my test and design.

The problem was that I tested the wrong user base – I tested my paper prototype on other computer science students, when I really should have been testing computer science profs.

It all comes down to weights.  As a student, I assumed that a professor takes a rubric, and distributes the weight among the criteria however they see fit:  Style is worth 5%, Creativity is worth 10%, etc.  My fellow CS students seemed to have the same impression.

Unfortunately, this is not the case.  CS profs, after creating the criteria for the rubric (where there are usually at least 10 criteria), add weight multipliers to the criteria that they deem more important.  So, for example, Style could get a weight multiplier of 2, and Creativity a weight multiplier of 1 (where 1 is the default weight).

So it’s a difference of orientation of thought:  I thought that 100% of weight was distributed amongst the criteria.  Instead, the criteria is created, and the weights are applied as professors see fit.  After the weight is applied, the 100% has been ‘created’, as opposed to the other way around.

So this means that the slider widget that I had originally planned for adjusting weights for the criteria no longer makes sense.  Instead, I plan to have a simple text input for inputting weight multipliers.  I’ll also put increment and decrement buttons on each side of the text input for quick bumps.

Just thought I’d share that.

-Mike

Written by mikec86

October 26, 2008 at 12:28 am

Posted in Uncategorized

Rebooting the Schema (part 2)

with 2 comments

Last time, I talked about the old model schema, and the problems it had that lead us to refactor the code. After refactoring, this is what the database looks like:

Association

The relationships are now more concrete with the addition of memberships and assignments_groups tables. The assignments_groups is a Rails convention of declaring a many-to-many relationship between two objects by use of the join table. Thus, an assignment can have many groups, and groups can also have many assignments if a group persists throughout the course. A caveat though is to make sure that the join table is in alphabetical order, meaning it must be assignments_groups and not groups_assignments. That’s just the “convention-over-configuration” mantra of Rails at work.

Once we have the database schema set, we can then just go in and declare those relationships in the ActiveRecord classes respectively:


class Group < ActiveRecord::Base
has_and belongs_to_many :assignments
end
class Assignment < ActiveRecord::Base
has_and belongs_to_many :groups
end

However it is a different case if the join table contains extra information, which is our case with the memberships table. Here, not only does it reference the user and the group together, but it also contains extra information such as status of the member. Thus, we need to have a Membership class representing a member, and use has-many-through relationship. which sort of explicitly states that the association between a User and a Group uses memberships as its link. Here we declare the relationship as follows:

class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :source => :user
end

Abstraction

We’ve also separated the old submissions table to a submissions and submission_files tables. The new submissions table doesn’t seem to have much information and seems to be a waste of space. However, having this table allows us to delegate submission functions to a Submissions class rather than mixing them directly with either the User or Group classes. All we have to do now is just ask a User or Group for its Submission instance, and handle all queries related to submitted files from it.

Since we also want to avoid checking to see if it is a User or a Group submission everytime, we’ve abstracted the Submissions class and added separate classes for each type, UserSubmission and GroupSubmission – classes that are linked to Users and Groups respectively. Since instead of declaring the relationship with Submissions, we have:

class User < ActiveRecord::Base
has_many :submissions, :classname => UserSubmission
end
class Group < ActiveRecord::Base
has_many :submissions, :classname => GroupSubmission
end

class UserSubmission < Submission
has_many :users
end
class Group
Submission < Submission
has_many :groups
end

This allows us to call either user.submissions or group.submissions and return with an instance of the appropriate Submission subclass type.

Final Results

The refactored models with the appropriate associations gave way to a much cleaner code in the end. With the schema set in place, I’ve revisited the old code and heeded the advice in the first post, stuffing all the business logic in the appropriate models and leaving workflow control to the controllers. The result turned several functions with 200+ lines into a single function with less than 50 lines. I was also able to create more thorough unit testing while code was being written. Here, we can see that we’ve improved our stats quite a bit:

$ for f in app/controllers app/models app/helpers; do echo $f
`find $f -name "*.rb" |xargs wc -l |tail -n1`; done
app/controllers 563
app/models 591
app/helpers 60

In retrospect, I think the refactoring decisions suits us very well with what we have in mind and gives us room for modifications at the same time…until we actually start porting OLM. Stay tuned.

Written by Geofrey

October 14, 2008 at 6:32 pm

Rebooting the Schema (part 1)

with one comment

Starting off this project, we’ve decided to start fresh with our model schema. When I started with Rails, and my first time developing on an MVC framework, I’ve always wondered how to decide what goes in the model and what goes in the controllers. After our summer iteration, a Rails reviewer came in to see the status of the code. With a little bit of console magic, he showed us this:

$ for f in app/controllers app/models app/helpers; do echo $f
`find $f -name "*.rb" |xargs wc -l |tail -n1`; done
app/controllers 720
app/models 390
app/helpers 32

He suggested that we should try to put most of the controller functions in the models as much as possible. That way, we can do a more thorough and fine-grained unit testing without mixing data functions to the workflow that controllers are suppose to do. To answer my question, he gave this advice:

One way to force oneself to use models more:
controllers should only have the methods pertaining to RESTful resources.

That way, the controller’s job would only be tying up together the business logic through the models and use the models to share code between controllers. Andrew, one of the students working on the project, also suggested to make use of the ActiveRecord relationships rather than having customized SQL queries in the code.

Before refactoring, the DB schema looked like this:

Old Schema

There were a lot of redundancies with this schema. For example, the group table not only contains group information but also maintains a record for each member of a group. Thus, a group with four members would have four records in the group table containing the same group_number and assignment_id. Also, if the same group persisted throughout the assignments in the course, then we would have to duplicate those four records for each assignment.

The simplicity of the tables also made it hard to actually manage it on the models layer. With no join tables to work on, I was using functions to wrap hard-coded ActiveRecord queries.

In the next post, I’ll talk about what the new schema looks like and what improvements we’ve made in the code.

Written by Geofrey

October 10, 2008 at 5:04 pm

Posted in OLM on Rails

Developing a Rubric for OLM on Rails

with 2 comments

Hey all – I’m Mike, and I’ve just started working on the OLM on Rails project.  My first task is to develop the Rubric models, controllers, and user experience, and I’m going to share some musings on it.

Ok, so first off, here’s the idea:  A professor creates an Assignment, and has to have a marking scheme to go along with it.  The Rubric that she develops will be used by the TA’s to mark the assignments.  Sounds pretty simple.

I’m still getting my thinking wrapped together here, so I’m just going to muse for a bit.  Here are some thoughts, rules, and caveats:

  • Creation of a Rubric is a separate event from creating an Assignment.  So if an Assignment exists, it does not imply that an associated Rubric exists yet
  • A Rubric consists of an arbitrary number of criteria to grade the student
  • Each criterion has a weight assigned to it (ie: 0.25, 0.5, etc)
  • Each criterion has a number of levels that can be achieved by a student.  By default, these levels are 0, 1, 2, 3, and 4 – with 0 being the worst performance, and 4 being the best.  We’re considering having the ability to add additional levels, to improve flexibility
  • An assignment cannot be marked until a Rubric has been created
  • A Rubric is not valid unless all of the weights of its criteria sum to 1.0 (100%)
  • The total number of marks achievable on an Assignment equals the sum of each criterion weight, multiplied by the maximum level of that criterion.  This does not include possible bonus marks, which are not handled by the Rubric
  • Assigning weights to criteria is going to be a little tricky, interface-wise.  While it’s tempting to just use a simple text-box, and trust the user to input the weights manually, it’d be nice for something a little more elegant and user friendly.
  • Some professors may want a simpler way of inputting this information quickly, for instance, by uploading a file describing what they want.  Andrew has suggested that users upload a YAML file… something worth thinking about.

That’s all I’ve really got rolling in my head about it right now.

So, what have I done?  I’ve created two new models:  RubricCriteria and RubricLevel.  A RubricCriteria is assigned to a single Assignment.  A RubricLevel is assigned to a single RubricCriteria.  Piece of cake.

I’ve also created some basic unit tests for these models.  I think I’m going to start thinking about user interfaces now, because I think it will give me some hints on what I’ll want the Controller to do.  I might upload some of my sketches later.

Cheers,

-Mike

Written by mikec86

October 8, 2008 at 12:43 pm

Posted in OLM on Rails

Tagged with , , , , , ,