raen79/tutor_chatbot

View on GitHub
app/models/faq.rb

Summary

Maintainability
A
0 mins
Test Coverage
# @attr [integer] id
# @attr [string] question
# @attr [string] answer
# @attr [string] lecturer_id
# @attr [string] module_id
# @attr [string] coursework_id
# @attr [date-time] created_at
# @attr [date-time] updated_at

class Faq < ApplicationRecord
  before_create :associate_synonyms
  before_update :associate_synonyms
  before_validation :upcase_id

  has_and_belongs_to_many :synonyms
 
  validates :question,
            :uniqueness => { :case_insensitive => false, :scope => :coursework_id },
            :question => { :attr => :question },
            :presence => true
  validates :answer, :length => { :minimum => 3 }, :presence => true
  validates :lecturer_id, :presence => true
  validates :module_id, :presence => true
  validates :coursework_id, :presence => true

  def self.find_answer(question:, module_id:, coursework_id:, student_id:, lecturer_id:)
    return 'Your question must be greater than or equal to 3 characters.' if question.length < 3
    return 'Your question must be lower than or equal to 140 characters.' if question.length > 140
    return 'Your question must end in a question mark.' unless question.last == '?'
    
    @possible_answers = possible_answers(question, coursework_id).uniq

    case @possible_answers.size
      when 0
        StudentQuestion.create!(
          :text => question,
          :module_id => module_id,
          :coursework_id => coursework_id,
          :lecturer_id => lecturer_id,
          :student_id => student_id
        )

        'I have found no answer to your question, I\'ll ask my supervisor and reply to you by email.'
      when 1
        @possible_answers.first
      else
        'I have found multiple answers to your question:<br />' +
        @possible_answers.join('<br /><br /><b>or</b><br /><br />')
    end
  end

  private
    def question_must_end_with_question_mark
      unless question.last == '?'
        errors.add(:question, 'must end with question mark')
      end
    end

    def associate_synonyms
      self.synonyms = Synonym.in_sentence(question)
    end

    def upcase_id
      [:lecturer_id, :module_id, :coursework_id].each do |attribute|
        if !self[attribute].blank? && self[attribute].kind_of?(String)
          self[attribute].upcase!
        end
      end
    end

    def self.possible_answers(question, coursework_id)
      possible_answers = []

      tagger = EngTagger::Synonyms.new(question)
      tagger.get_relevant_words.combination(2).each do |first_word, second_word|
        first_word = first_word.singularize
        second_word = second_word.singularize

        ActiveRecord::Base.connection.execute("SELECT set_limit(0.4);")
        faqs = Faq.joins(:synonyms)
                  .where(:coursework_id => coursework_id)
                  .where("? % ANY (synonyms.words) OR synonyms.word % ?", first_word, first_word) &
               Faq.joins(:synonyms)
                  .where(:coursework_id => coursework_id)
                  .where("? % ANY (synonyms.words) OR synonyms.word % ?", second_word, second_word)
  
        possible_answers = possible_answers | faqs.pluck(:answer)
      end

      possible_answers
    end
end