class Submission < ActiveRecord::Base belongs_to :language belongs_to :problem belongs_to :user before_validation :assign_problem before_validation :assign_language validates_presence_of :source validates_length_of :source, :maximum => 100_000, :allow_blank => true, :message => 'code too long, the limit is 100,000 bytes' validates_length_of :source, :minimum => 1, :allow_blank => true, :message => 'too short' validate :must_have_valid_problem validate :must_specify_language has_one :task before_save :assign_latest_number_if_new_recond def self.find_last_by_user_and_problem(user_id, problem_id) where("user_id = ? AND problem_id = ?",user_id,problem_id).last end def self.find_all_last_by_problem(problem_id) # need to put in SQL command, maybe there's a better way Submission.includes(:user).find_by_sql("SELECT * FROM submissions " + "WHERE id = " + "(SELECT MAX(id) FROM submissions AS subs " + "WHERE subs.user_id = submissions.user_id AND " + "problem_id = " + problem_id.to_s + " " + "GROUP BY user_id) " + "ORDER BY user_id") end def self.find_in_range_by_user_and_problem(user_id, problem_id,since_id,until_id) records = Submission.where(problem_id: problem_id,user_id: user_id) records = records.where('id >= ?',since_id) if since_id and since_id > 0 records = records.where('id <= ?',until_id) if until_id and until_id > 0 records.all end def self.find_last_for_all_available_problems(user_id) submissions = Array.new problems = Problem.available_problems problems.each do |problem| sub = Submission.find_last_by_user_and_problem(user_id, problem.id) submissions << sub if sub!=nil end submissions end def self.find_by_user_problem_number(user_id, problem_id, number) where("user_id = ? AND problem_id = ? AND number = ?",user_id,problem_id,number).first end def self.find_all_by_user_problem(user_id, problem_id) where("user_id = ? AND problem_id = ?",user_id,problem_id) end def download_filename if self.problem.output_only return self.source_filename else timestamp = self.submitted_at.localtime.strftime("%H%M%S") return "#{self.problem.name}-#{timestamp}.#{self.language.ext}" end end protected def self.find_option_in_source(option, source) if source==nil return nil end i = 0 source.each_line do |s| if s =~ option words = s.split return words[1] end i = i + 1 if i==10 return nil end end return nil end def self.find_language_in_source(source, source_filename="") langopt = find_option_in_source(/^LANG:/,source) if langopt return (Language.find_by_name(langopt) || Language.find_by_pretty_name(langopt)) else if source_filename return Language.find_by_extension(source_filename.split('.').last) else return nil end end end def self.find_problem_in_source(source, source_filename="") prob_opt = find_option_in_source(/^TASK:/,source) if problem = Problem.find_by_name(prob_opt) return problem else if source_filename return Problem.find_by_name(source_filename.split('.').first) else return nil end end end def assign_problem if self.problem_id!=-1 begin self.problem = Problem.find(self.problem_id) rescue ActiveRecord::RecordNotFound self.problem = nil end else self.problem = Submission.find_problem_in_source(self.source, self.source_filename) end end def assign_language if self.language == nil self.language = Submission.find_language_in_source(self.source, self.source_filename) end end # validation codes def must_specify_language return if self.source==nil # for output_only tasks return if self.problem!=nil and self.problem.output_only if self.language == nil errors.add('source',"Cannot detect language. Did you submit a correct source file?") end end def must_have_valid_problem return if self.source==nil if self.problem==nil errors.add('problem',"must be specified.") else #admin always have right return if self.user.admin? #check if user has the right to submit the problem errors[:base] << "Authorization error: you have no right to submit to this problem" if (!self.user.available_problems.include?(self.problem)) and (self.new_record?) end end # callbacks def assign_latest_number_if_new_recond return if !self.new_record? latest = Submission.find_last_by_user_and_problem(self.user_id, self.problem_id) self.number = (latest==nil) ? 1 : latest.number + 1; end end