Create a spreadsheet-like form with Rails - mysql

I've been learning Rails for about two months now. I'm creating an application for teachers to track the progress of their students. I've got the "Assignments" model working for teachers to add new assignments to a classroom, and I've got the "Users" model working so that teachers and students are both Users who can log in to the app. There's also a "Classroom" model, and each classroom has_many students and has_many assignments.
One of the main views needs to feature a spreadsheet form like traditional teacher gradebook programs. The spreadsheet will use students as the rows and assignments as the columns. Each cell in the spreadsheet will represent the student's score on that assignment.
From what I've learned so far, I think that my next step should be to create a join table that links students and assignments, with a third column for "score".
The part where I'm stumped is in creating the form so that the input cells are tied to the "score" column in the join table, so that entering a new number will change the student's score for that assignment.
I'm sure that articles or tutorials must exist somewhere for this concept, but I haven't been able to find any yet. At least, none that I recognize as a solution to this goal.
Thank you in advance for any guidance.
UPDATED TO INCLUDE CODE FOR MODELS
User Model:
class User < ApplicationRecord
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
has_many :seminars, dependent: :destroy
# Neccessary for finding all classes that a student is enrolled in
has_many :aulas, dependent: :destroy,
foreign_key: :student_id
validates :first_name, length: {maximum: 25},
presence: true
validates :last_name, length: {maximum: 25},
presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: {minimum: 6}, allow_nil: true
### Several methods that I omitted to keep the question shorter
end
Seminar Model:
(A "Seminar" is a class period, but I wanted to avoid the word, "Class" because I thought that would cause errors.)
class Seminar < ApplicationRecord
belongs_to :teacher, class_name: "User",
foreign_key: "user_id"
has_many :aulas, dependent: :destroy
has_many :students, through: :aulas, source: :student
has_many :assignments
validates :user_id, presence: true
validates :name, presence: true, length: { maximum: 40 }
end
Aula Model:
(Aula is Spanish for class. Again, I wanted to avoid the word, "Class". This model creates a relationship between a student user and a seminar (class period)."
class Aula < ApplicationRecord
# Aula is the relationship between a student and a classperiod.
belongs_to :student, class_name: "User"
belongs_to :seminar
validates :student_id, presence: true
validates :seminar_id, presence: true
end
Assignment model:
class Assignment < ApplicationRecord
belongs_to :seminar
validates :name, presence: true, length: { maximum: 40 }
validates :seminar_id, presence: true
validates :possible, presence: true
end

I would suggest you to show the Users x Assignments in a table and use in place edit, so the user can click in the cell and edit it value right there. For rails, you have a gem called "best in place" (https://github.com/bernat/best_in_place) that does the trick (there's also a rails cast that shows hot to use it: http://railscasts.com/episodes/302-in-place-editing?view=asciicast). Hope it helps, thanks
EDIT:
Answering your question, I used best_in_place for a project manager and it performance it's really nice. Looks like you're editing on microsoft excel, or something else.
About the backend: Well, you have a n x n relationship between student and assignments. You'll need a assignments_student model, for example, that belongs both to your user and assignment model and also has the score (take a look on nxn relationships if you're in doubt). So each row on your assignments_student junction table (that has two foreign keys to user and assignment table plus the score attribute) will be a cell on your table, in a way that you are editing the value of score attribute for that respective user/assignment.
Hope I made it clear. Good luck!

You might be interested in cocoon, it is a gem that allows you to do:
Dynamic nested forms using jQuery made easy
That allows you to add the "rows" to your spreadsheet-like form dynamically, based on the number of students.
Also read about Rails's accepts_nested_attributes_for, this is the foundation for allowing to do nested forms.

Related

How to make associations on this

Hello, i'm new to rails. I have been fed up with active record associations. I did study associations from the rails guides. Yet i cant find myself a clear way to add associations to the models suggested in the diagram.
I have one doubt whether a single foreign key(SECOND MODEL) can reference two primary keys (SECOND MODEL LEVEL 2 FIRST & SECOND MODEL LEVEL 2 SECOND). This has been done because the user has to choose whether to add from the SECOND MODEL LEVEL 2 FIRST TABLE or the SECOND MODEL LEVEL 2 SECOND TABLE while inserting values into the SECOND MODEL.
If u find this hard to understand please leave a comment, ill make appropriate changes. And i would appreciate on how to query from the FINAL LEVEL FIRST with BASE-MODEL thorough a join condition.
You can use polymorphic association to refer to one table OR another, check the model below as per the posted image:
class BaseModel
has_many :first_models
has_many :second_models
end
class FirstModel
belgons_to :base_model
has_one :level_two_first_model
end
class LevelTwoFirstModel
belgons_to :first_model
end
class SecondModel
belgons_to :base_model
has_many :final_first_levels, as: :referenceable, :dependent => :destroy
has_many :final_second_levels, as: :referenceable, :dependent => :destroy
end
class LevelOneSecondModel
belongs_to :referenceable, polymorphic: true
has_many :final_first_levels
end
class LevelTwoSecondModel
belongs_to :referenceable, polymorphic: true
has_many :final_first_levels
end
class FinalFirstLevel
belongs_to :LevelOneSecondModel
end
class FinalSecondLevel
belongs_to :LevelTwoSecondModel
end
referenceable is used as the glue between the parent table and other polymorphic associations (LevelOneSecondModel OR LevelTwoSecondModel)
NB:
Don't forget to add the below line in the migration files of the 2 children tables used in polymorphic association.
t.references :referenceable, polymorphic: true, index: true
Reference:
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

search association having class_name

i have a Book model looking like this:
class Book < ActiveRecord::Base
belongs_to :author, class_name: 'User', touch: true
belongs_to :translator, class_name: 'User'
end
How can i search for books with specific author name? i join the tables first:
Book.joins(:author).joins(:translator)
But i cant chain
.where('authors.name = "Tolkien"')
because there is no "authors" table in the database. is
.where('users.name = "Tolkien"')
a safe approach here? is there no risk concerning both translators and authors being users in fact?
(select() method is not an option, i need activerecord::relation here)
You need to pass table name in query
Book.joins(:author).where(users: {name: 'Tolkien'})
is .where('users.name = "Tolkien"') a safe approach here?
If you are concerned about two associations using same table you might need to add a field to distinguish them
For ex. a boolean field author and use condition with association which will solve the problem
class Book < ActiveRecord::Base
belongs_to :author, -> { where(author: true) }, class_name: 'User', touch: true
belongs_to :translator, -> { where(author: false) }, class_name: 'User'
end

Validate uniqueness of both ids in a join table

I have a list model which has_and_belongs_to_many postings and vice versa.
I have a join table:
create_table :postings_lists do |t|
t.integer :posting_id
t.integer :list_id
end
Users have many lists.
I already validate the list uniqueness for the user with:
validates :name, presence: true, uniqueness: {scope: :user_id}
In the join table, how can I validate the uniqueness of both the :posting_id and the :list_id so that a posting can't belong to a list more than once?
I have tried adding, uniq: true to both has_and_belongs_to_manys in the models but it messes things up and I have tried added custom validations in the list model but it wasn't working.
I think the simplest thing would be just to validate both ids in the join table but I don't know if I can do that without creating a model?
I would use a has_many :through instead of HABTM.
class List < ActiveRecord::Base
has_many :postings
has_many :posts, through: :postings
end
class Posting < ActiveRecord::Base
belongs_to :list
belongs_to :post
validate :post_id, uniqueness: {scope: :list_id}
end
class Post < ActiveRecord::Base
has_many :postings
has_many :lists, through: postings
end
I ended up adding uniq: true
Lists
has_and_belongs_to_many :postings, uniq: true
Postings
has_and_belongs_to_many :lists, uniq: true
This causes some issues if you use things like pluck on postings because things have to be "distinct" before doing other things like "order_by" to them.
But you can work around this issue by using different queries like 'map' instead.

Updating a Rails Association table

I'm working on a quiz app in Rails that keeps track of a number of Facts
app/models/fact.rb
class Fact < ActiveRecord::Base
validates(:question, presence: true, uniqueness: { case_sensitive: false })
validates(:answer, presence: true)
end
Every time a user takes a new quiz, they generate an Exam
app/models/exam.rb
class Exam < ActiveRecord::Base
after_create :assign_facts
belongs_to :user
default_scope -> { order('created_at DESC') }
validates :user_id, presence: true
has_many :problems
has_many :facts, through: :problems
def assigned?(fact)
problems.find_by(fact_id: fact.id)
end
def assign!(fact)
problems.create!(fact_id: fact.id)
end
private
def assign_facts
facts = Fact.all.sample(10)
facts.each do |fact|
self.assign!(fact)
end
end
end
Since there are many Exams all using the same Facts, each Exam has_many Facts though an association table of Problems
app/models/problem.rb:
class Problem < ActiveRecord::Base
belongs_to :exam
belongs_to :fact
validates :exam_id, presence: true
validates :fact_id, presence: true
end
excerpt from db/scheme.rb:
create_table "problems", force: true do |t|
t.integer "exam_id"
t.integer "fact_id"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "correct", default: false
end
My problem is that I'm trying to figure out how to store the results of each user's exam (whether they answer a specific question either correctly, or incorrectly). I was planning on updating the association table each time the user answers a question and storing the result in the t.boolean "correct" collumn. This would be a fairly simple matter in PHP/MySQL (UPDATE problems SET correct = 1 WHERE exam = 'id' AND fact = 'id'), but I'm having difficulty figuring out how to do it the Rails way.
Is there some way I can simply, and easily update my associations table (problems) with Rails? OR Should I create a fourth table (maybe 'results' or something) to keep track of the user's correct/incorrect answers? --I know I don't have any controller code here, I'm just thinking out the broad strokes, and I want to keep things simple. Any help would be greatly appreciated.
You are almost there... you have already added a Boolean column called correct in the schema for the problem model, so now you just need to access that as an attribute when updating an Exam. Som somewhere in your controller code, you would say:
ps=#exam_taken.problems
ps.each do |p|
if answered_correctly(p)
p.correct=true
p.save
end
end
# This assumes you have a method that checks correctness inside the binding where the
# above code is written
#exam_taken.save
If you are using Rails3, you would have to also declare the correct attribute as attr_accessible in your model.
And here's a free pro-tip: Default scope is evil :)

Multiple Foreign Keys with Ruby on Rails

I have the following setup:
One matchdays table with a column called home_team_id and one called visitor_team_id
and a team table.
My Match model looks like this:
class Match < ActiveRecord::Base
belongs_to :home_team, class_name: "Team", foreign_key: :home_team_id
belongs_to :visitor_team, class_name: "Team", foreign_key: :visitor_team_id
belongs_to :matchday
validates :home_team, presence: true
validates :visitor_team, presence: true
end
And the Team model like that:
class Team < ActiveRecord::Base
has_many :matches
has_many :player
end
Now it's getting tricky (at least for me). I'd like to be able to call team.matches and get all of the matches for the team. Since every team has home games and also games on the road.
Currently I'm getting a ActiveRecord::StatementInvalid Error because it's looking for the team_id column in the matches table.
So if I understand correctly, what you need is just a method that returns all games where the current team is playing. This should do the trick:
class Team < ActiveRecord::Base
has_many :player
def matches
Team.where(home_team_id => self.id, foreign_key => self.id)
# This line will also work if you want to try it out.
# Team.where("home_team_id = ?", self.id).where("foreign_key = ?", self.id)
end
end
Happy coding!