MarkUs Blog

MarkUs Developers Blog About Their Project

Archive for the ‘OLM on Rails’ Category

How to Install Seed_fu

without comments

Today, we got a pretty awesome cursory code review from Mike Gunderloy.

One thing he suggested, was that we switch from running a sloppy Ruby script to seed our database with initial data, to something like seed_fu.

So, how do we install Seed_fu?  It took me a while to figure it out, actually, since I’d never installed a Rails plugin from GitHub before.

Anyhow, here’s how you do it:

From your application root:


script/plugin install git://github.com/mbleigh/seed-fu.git

Easy.

Written by m_conley

June 11th, 2009 at 5:07 pm

Posted in OLM on Rails

Tagged with , ,

Single Table Inheritance, and Testing Fixtures

without comments

I’m still trying to get our unit tests up and running, and I ran into a snag a few hours ago.

I tried to run a single unit test, on the Admin model (which is a subclass of type User). I kept getting this error message:


ActiveRecord::StatementInvalid: PGError: ERROR: relation "students" does not exist
: DELETE FROM "students"

Hrmph. We don’t have a “students” table, so of course that won’t work. We have Students (which, like Admins, subclass from User), but we certainly don’t have a “students” table.

So how come it’s trying to access that table?

It turns out that Rails test fixtures don’t deal with Single Table Inheritance. Instead, a Rails fixture should be a YAML file that populates a particular table with its contents.

And it turns out I had a fixture called “students.yml” in my test/fixtures folder. So, Rails tried to connect to the “students” table, insert some records, and clear them out afterwards.

The solution was to remove the students.yml, tas.yml, and admins.yml files, and simply have a users.yml file. The same goes for student_memberships.yml and ta_memberships.yml. Replace those with memberships.yml. Boom. Tests run.

Now it’s just a matter of getting some good content in the fixtures, and getting some solid tests written…

Written by m_conley

May 26th, 2009 at 1:54 pm

A Sneaky Change in Rails 2.3.2 Temporarily Broke Our Tests

without comments

Today, I’ve decided to revamp the test suites for OLM. I won’t lie – we’ve been a little lazy in terms of testing. We have lots of meetings where we say “our testing is going to be solid”, but then nothing really happens.

I’m sure we’re not alone in this regard.

Anyhow, I decided to dust off our tests and give them a run. Here’s what I got:


./test/unit/../test_helper.rb:22: undefined method `use_transactional_fixtures=' for Test::Unit::TestCase:Class (NoMethodError)

Hrmph. What’s going on? After a little Googling, and it turns out that in Rails 2.3.2, “Test::Unit::TestCase changed to ActiveSupport::TestCase”.

So, I had to go into ./test/test_helper.rb, and change the class to ActiveSupport::TestCase. No big deal really, but it was confusing at first.

It’s a real blow to your self-esteem when your testing framework doesn’t even run.

Anyhow, it’s fixed. Now to write some good tests…

FOLLOW UP: You also need to change Test::Unit::TestCase to ActiveSupport::TestCase in your tests.

Written by m_conley

May 26th, 2009 at 11:02 am

Using Subversion for File Storage

without comments

Over the past few days, the team has been talking about how we eventually want to use Subversion as the file storage backend for the work that students submit through OLM.

Here’s what we ended up deciding:  we’re going to build a generic Repository class, and have three different implementations:  File System, Memory, and Subversion.

Each Repository will return Revision objects, and each revision will have File’s and (eventually) Directories.

So, how would this thing work?  Say I want to get all files from the root directory of a particular repository for the latest revision.  Here’s how we’d more or less want to pull it off with our classes in Ruby:

repo = Repository::RepositoryFactory("svn").open('/some/subversion/repository')
revision = repo.get_latest_revision
# I want all files for the root directory /.
files = revision.all_files('/')

This would provide a collection of File objects representing the files in the latest revision of the repository. If we wanted to download/display that file, we could use the following:

repo = Repository::RepositoryFactory("svn").open('/some/subversion/repository')
revision = repo.get_latest_revision
# Say I wanted the file Test.java from the root directory...
wanted_file = revision.all_files('/')["Test.java"]
file_output = repo.download(wanted_file)
#...insert code here to send file_output to browser

As it stands, that’s more or less how we’re designing the Repository classes. Big thanks to the Basie team for their suggestions on the design!

Anyhow, I’ll try to keep you all posted on our progress for implementing the Repositories.

Written by m_conley

May 25th, 2009 at 12:47 am

Groups

without comments

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 12th, 2009 at 3:05 am

Posted in OLM on Rails

Testing Rails with Authentication

without comments

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 7th, 2008 at 2:38 pm

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 14th, 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 10th, 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 m_conley

October 8th, 2008 at 12:43 pm

Posted in OLM on Rails

Tagged with , , , , , ,

Switching Rails to PostgreSQL

with one comment

Since most of the web applications we develop here uses PostgreSQL, we’ve decided to port our environment to use postgres instead. Here I outline how I managed to get rails and PostgreSQL 8.3 up and running on Ubuntu 8.04.

1. Install PostgreSQL

If you haven’t already, try typing:

sudo apt-get install postgresql postgresql-client postgresql-contrib libpq-dev

Make sure that the installation completed successfully by typing psql --version. Current version is 8.3.3. libpq-dev is important since the postgres gem needs it, so make sure that we have that installed even if you have postgresql installed already.

2. Install PostgreSQL adapter for ruby

Update your rubygem first by typing sudo gem update --include-dependencies. You then need to install ruby-pg, which superseded the now-obsolete ruby-postgres gem (which can’t even compile postgres 8.3 due to a compilation bug). To install postgres gem, type:

sudo gem install postgres

3. Create a PostgreSQL user

By default, the user in the rails DB config file is the project name. Thus we can create this user by going into the postgres console

sudo su postgres -c psql

Once in the psql console, type (including the quotes)

create user "<user_name>" with [superuser] password '<password>';

Include the superuser option if needed, usually easier for development when working on your own computer. Verify that the CREATE ROLE message was displayed then exit the console.

4. Change PostgreSQL Authentication

When executing some of the database commands, you might encounter a FATAL: ident authentication failed error. To avoid this, you need to edit your pg_hba.conf (found in /etc/postgresql/8.3/main in Ubuntu 8.04) and change the authentication scheme. Open the file with root privilege and change

# "local" is for Unix domain socket connections only
local all all ident sameuser

to

# "local" is for Unix domain socket connections only
local all all md5

Then restart your postgres server by typing sudo /etc/init.d/postgresql-8.3 restart

5. Create the databases

to create the database we can use the createdb shell command, by typing on prompt:

createdb <project_name>_development -U <user_name> -W

or, if user does not have superuser account, from the psql console (by sudo’ing as postgres from before) typing:

create database <project_name>_development owner <user_name>;

and give privilege to the user by typing:

grant all privileges on database <database_name> to <user_name>;

Do the same thing for creating <project_name>_test for the test database.

5.5 Install phppgadmin (optional)

To make your life easier, there’s a php console for developing with postgres called phppgadmin, derived from its MySQL counterpart, phpmyadmin. phpmyadmin is still a lot better interface, but phppgadmin is better than nothing at all. Assuming you have PHP and apache2 installed, install phppgadmin by typing:

sudo apt-get install phppgadmin

and create a symlink for apache:

sudo ln -s /etc/phppgadmin/apache.conf /etc/apache2/conf.d/phppgadmin.conf

Reload apache by typing sudo /etc/init.d/apache2 reload and go to http://localhost/phppgadmin/ to play around with your new tool.

6. Generate a database config file for PostgreSQL

If you’re starting from scratch, the easiest way is to create your application using

rails <project_name> -d postgresql

However, if we’re migrating to postgres from a current rails application, then the easiest way is to just create another rails application of the same name but on a different directory. Go to a directory where your current rails application is not located and create a rails application of the same name using above command. Once that’s created, just copy the database config file to your old rails application by copying:

mv <original_project_path>/config/database.yml <original_project_path>/config/database.yml.old

cp <project_name>/config/database.yml <original_project_path>/config

Once copied, open database.yml on a text editor and fill in the database details we just created.

Epilogue

One of the problems I encountered was figuring out which postgresql adapter to use. ruby-postgres wasn’t working with postgres 8.3 so I regressed back to 8.2, only to find out postgres adapter exists. Not much documentation exists since it’s fairly new and a lot of people I guess is still in psql-8.2. The other one is the dreaded FATAL: ident authentication failed error you get when starting to use psql on Ubuntu. Again, thanks to Jeff and the Dr. Project documentation, we finally got it working.

And finally, psql-8.3 seems to break the implicit cast being done from the previous 8.2 before.  So now, when I try to find matches where numbers are stored as string, I get the following error:

ERROR:  operator does not exist: character varying = integer

The column being declared as string does not match the passed integer argument, which should be converted to String in the first place.  Thus I had to explicitly cast it to a String before using.

For more information regarding PostgreSQL and Ruby on Rails, see the official wiki.

Written by Geofrey

August 12th, 2008 at 10:22 am