opengovernment/askthem

View on GitHub
app/models/question.rb

Summary

Maintainability
A
0 mins
Test Coverage
class Question
  include Mongoid::Document
  include Mongoid::Timestamps
  include AutoHtmlFor

  # The author of the question.
  belongs_to :user
  accepts_nested_attributes_for :user

  # The signatures in support of the question.
  has_many :signatures, dependent: :destroy

  # The official answer to the question
  has_many :answers, dependent: :destroy

  # for uploaded photo or video
  mount_uploader :media, MediaUploader

  # The jurisdiction in which the question is asked.
  field :state, type: String
  # The person to whom the question is addressed.
  field :person_id, type: String
  # The bill the question is about.
  field :bill_id, type: String
  # The question's summary.
  field :title, type: String
  # The question's content.
  field :body, type: String
  # The question's issue area.
  field :subject, type: String
  # The questions's time of publication.
  field :issued_at, type: Time

  # based on asking user at time of question creation
  # since we aren't geocoding directly, don't need geocoder stuff
  field :coordinates, type: Array

  # alternative to uploaded photo or video
  # remotely hosted media (think youtube, vimeo)
  # note that "_file_" is added to avoid name conflict
  # with carrierwave generated methods for media uploader
  field :media_file_url, type: String

  # local cache of the number of signatures
  field :signature_count, type: Integer, default: 0
  field :threshold_met, type: Boolean, default: false

  field :answered, type: Boolean, default: false

  field :delivered, type: Boolean, default: false

  field :originating_url, type: String

  field :needs_confirmation, type: Boolean, default: false
  field :confirm_code, type: String

  index(state: 1)
  index(person_id: 1, answered: 1)
  index(bill_id: 1, answered: 1)
  index({ coordinates: "2d" }, { background: true })
  index(originating_url: 1)

  validates_presence_of :state, :person_id, :user_id, :title
  validates_length_of :title, within: 3..100, allow_blank: true
  validate :state_must_be_included_in_the_list_of_states
  validate :subject_must_be_included_in_the_list_of_subjects
  validate :question_and_person_must_belong_to_the_same_jurisdiction
  validate :person_and_bill_must_belong_to_the_same_jurisdiction
  validates :media_file_url, :uri => { accessible: true, allow_blank: true }

  attr_accessor :person_state, :bill_state

  after_create :add_signature_of_creating_user
  after_create :copy_coordinates_from_user

  AutoHtml.add_filter(:image).with({ alt: "", width: "", class: "" }) do |text, options|
    r = Redcarpet::Markdown.new(NoParagraphRenderer)
    alt, width, html_class = options[:alt], options[:width], options[:class]
    options[:proxy] ||= ""
    text.gsub(/(?<!src=")https?:\/\/.+?\.(jpg|jpeg|bmp|gif|png)(\?\S+)?/i) do |match|
        r.render("<img src=\"#{options[:proxy]}#{match}\" alt=\"#{alt}\" width=\"#{width}\" class=\"#{html_class}\">")
    end
  end

  auto_html_for [:body, :media_file_url] do
    html_escape
    hashtag
    image(width: 425, class: "question-image")
    dailymotion(width: 400, height: 250, autoplay: false)
    vimeo(width: 400, height: 250, autoplay: false)
    youtube(width: 400, height: 250, autoplay: false)
    link
    simple_format
  end

  # @return [Metadatum] the jurisdiction in which the question is asked
  def metadatum
    Metadatum.find_by_abbreviation(state)
  end

  # @todo delete if unnecessary with only 1 db
  # i.e. does belongs_to now work
  # @return [Person] the person to whom the question is addressed
  def person
    Person.find(person_id)
  end

  # @todo delete if unnecessary with only 1 db
  # i.e. does belongs_to now work
  # and delegation of state and person_state to Person
  # @param [Person] person a person
  def person=(person)
    if person
      self.person_id = person.id
      self.person_state = person['state']
      self.state ||= person['state']
    else
      self.person_id = nil
    end
  end

  def answered?
    answers.any?
  end

  # @todo delete if unnecessary with only 1 db
  # i.e. does belongs_to now work
  # @return [Bill] the bill the question is about
  def bill
    Bill.where(id: bill_id).first
  end

  # @todo delete if unnecessary with only 1 db
  # i.e. does belongs_to now work
  # and delegation of state and person_state to Person
  # @param [Bill] bill a bill
  def bill=(bill)
    if bill
      self.bill_id = bill.id
      self.bill_state = bill['state']
      self.state ||= bill['state']
    else
      self.bill_id = nil
    end
  end

  private
  def add_signature_of_creating_user
    signatures.create!(user: user)
  end

  def state_must_be_included_in_the_list_of_states
    unless state.blank? || Metadatum.find_by_abbreviation(state)
      errors.add(:state, 'is not included in the list of states')
    end
  end

  def subject_must_be_included_in_the_list_of_subjects
    unless subject.blank? || Subject.all.include?(subject)
      errors.add(:subject, 'is not included in the list of subjects')
    end
  end

  def question_and_person_must_belong_to_the_same_jurisdiction
    unless person_state.blank? || person_state == state
      errors.add(:base, 'The question and the person must belong to the same state')
    end
  end

  def person_and_bill_must_belong_to_the_same_jurisdiction
    unless bill_state.blank? || bill_state == person_state
      errors.add(:base, 'The person and the bill must belong to the same state')
    end
  end

  def copy_coordinates_from_user
    QuestionCoordinatesWorker.perform_async(id.to_s)
  end
end