gems-uff/sapos

View on GitHub
app/models/admissions/filled_form_field.rb

Summary

Maintainability
F
3 days
Test Coverage
F
16%
# Copyright (c) Universidade Federal Fluminense (UFF).
# This file is part of SAPOS. Please, consult the license terms in the LICENSE file.

# frozen_string_literal: true

class Admissions::FilledFormField < ActiveRecord::Base
  has_paper_trail

  serialize :list

  belongs_to :filled_form, optional: false,
    class_name: "Admissions::FilledForm"
  belongs_to :form_field, optional: false,
    class_name: "Admissions::FormField"

  has_many :scholarities, dependent: :delete_all,
    class_name: "Admissions::FilledFormFieldScholarity"

  validates :filled_form, presence: true
  validates :form_field, presence: true

  mount_uploader :file, FormFileUploader

  validate :that_either_value_or_file_is_filled
  validate :that_value_follows_configuration_rules
  validate :size_of_file

  accepts_nested_attributes_for :scholarities, reject_if: :all_blank,
    allow_destroy: true

  after_initialize :set_default_values

  def set_default_values
    return if self.form_field.nil?
    configuration = self.form_field.config_hash
    case self.form_field.field_type
    when Admissions::FormField::STRING
      self.value = configuration["default"] if self.value.nil?
    when Admissions::FormField::SELECT
      self.value = configuration["default"] if self.value.nil? &&
        self.form_field.get_values_map("values")[configuration["default"]].present?
    when Admissions::FormField::COLLECTION_CHECKBOX
      self.list = self.form_field.get_values_map("default_values").keys if
        self.list.nil?
    when Admissions::FormField::RADIO
      self.value = configuration["default"] if self.value.nil? &&
        self.form_field.get_values_map("values")[configuration["default"]].present?
    when Admissions::FormField::SINGLE_CHECKBOX
      self.value = configuration["default_check"] ? "1" : "0" if self.value.nil?
    when Admissions::FormField::TEXT, Admissions::FormField::NUMBER, Admissions::FormField::DATE
      self.value = configuration["default"] if self.value.nil?
    end
  end

  def that_either_value_or_file_is_filled
    presence = [self.file.present?, self.value.present?, self.list.present?]
    if presence.count { |x| x } > 1
      add_error(:multiple_filling)
    end
  end

  def that_value_follows_configuration_rules
    return if self.form_field.nil?
    configuration = self.form_field.config_hash
    case self.form_field.field_type
    when Admissions::FormField::STRING
      validate_value_required(configuration)
    when Admissions::FormField::SELECT
      validate_value_required(configuration)
    when Admissions::FormField::RADIO
      validate_value_required(configuration)
    when Admissions::FormField::COLLECTION_CHECKBOX
      validate_collection_checkbox(configuration)
    when Admissions::FormField::SINGLE_CHECKBOX
      nil
    when Admissions::FormField::FILE
      validate_file_field(configuration)
    when Admissions::FormField::TEXT
      validate_value_required(configuration)
    when Admissions::FormField::STUDENT_FIELD
      validate_student_field(configuration)
    when Admissions::FormField::CITY
      validate_city_field(configuration)
    when Admissions::FormField::RESIDENCY
      validate_residency_field(configuration)
    when Admissions::FormField::NUMBER
      validate_number_field(configuration)
    when Admissions::FormField::DATE
      validate_date_field(configuration)
    end
  end

  def size_of_file
    return unless self.file.present?
    size = self.file.file.size.to_f
    if size > 15.megabytes.to_f
      add_error(:filesize, count: 15)
      self.file = nil
    end
  end

  def validate_collection_checkbox(configuration)
    if configuration["required"] && self.list.blank?
      add_error(:blank)
    end
    if self.list.present?
      elements = self.list.filter { |x| x.present? }
      mincount = configuration["minselection"].to_i
      if mincount > 0 && elements.length < mincount
        add_error(:minselection, count: mincount)
      end
      maxcount = configuration["maxselection"].to_i
      if maxcount > 0 && elements.length > maxcount
        add_error(:maxselection, count: maxcount)
      end
    end
  end

  def validate_student_field(configuration)
    if configuration["field"] == "photo"
      validate_file_field(configuration, is_photo: true)
    elsif ["special_city", "special_birth_city"].include? configuration["field"]
      validate_city_field(configuration)
    elsif configuration["field"] == "special_address"
      validate_residency_field(configuration)
    else
      validate_value_required(configuration)
    end
    if ["birthdate", "identity_expedition_date"].include? configuration["field"]
      validate_date_field(configuration)
    end
  end

  def validate_city_field(configuration)
    validate_value_required(configuration)
    return if self.value.blank?
    values = self.value.split(" <$> ")
    if configuration["required"] && values[0].blank?
      add_error(:city_blank)
    end
    if configuration["state_required"] && values[1].blank?
      add_error(:state_blank)
    end
    if configuration["country_required"] && values[2].blank?
      add_error(:country_blank)
    end
  end

  def validate_residency_field(configuration)
    validate_value_required(configuration)
    return if self.value.blank?
    values = self.value.split(" <$> ")
    if configuration["required"] && values[0].blank?
      add_error(:street_blank)
    end
    if configuration["number_required"] && values[1].blank?
      add_error(:number_blank)
    end
  end

  def validate_value_required(configuration)
    if configuration["required"] && self.value.blank?
      add_error(:blank)
    end
  end

  def validate_number_field(configuration)
    return true if self.value.blank?
    !!Float(self.value)
  rescue
    add_error(:invalid_number)
  end

  def validate_date_field(configuration)
    return if self.value.blank?
    format_ok = self.value.match(/^\d{1,2}\/\d{1,2}\/\d{2,4}$/)
    parseable = Date.strptime(self.value, "%d/%m/%Y") rescue false
    if !format_ok || !parseable
      add_error(:invalid_date)
    end
  end

  def validate_file_field(configuration, is_photo: false)
    if configuration["required"] && (self.file.blank? || self.file.file.blank?)
      add_error(:blank)
    end
    if configuration["values"] && !(self.file.blank? || self.file.file.blank?)
      values = configuration["values"].dup
      values << ".jpg" if is_photo
      filename = self.file.filename.downcase
      if values.none? { |ext| filename.end_with?(ext.downcase) }
        add_error(:extension, valid: configuration["values"].join(', '))
      end
    end
  end

  def to_label
    return "-" if self.form_field.blank?
    name = self.form_field.name
    return "#{name}: #{self.file}" if self.file.present?
    return "#{name}: #{self.list}" if self.list.present?
    return "#{name}: #{self.value}" if self.value.present?
    "#{name}: -"
  end

  def to_text(blank: "-", field_type: nil, custom: {})
    return blank if self.form_field.blank?
    form_field = self.form_field
    field_type ||= form_field.field_type
    if custom[field_type].present?
      return custom[field_type].call(self, form_field)
    end
    case field_type
    when Admissions::FormField::COLLECTION_CHECKBOX
      default_values = form_field.get_values_map("default_values")
      original_value = form_field.get_values_map("values")
      values_map = original_value.merge(default_values)
      return blank if self.list.blank?
      values = self.list.filter_map { |x| values_map[x] if x.present? }
      values.join(", ")
    when Admissions::FormField::SELECT, Admissions::FormField::RADIO
      return blank if self.value.blank?
      values = form_field.get_values_map("values")
      values[self.value]
    when Admissions::FormField::FILE
      return blank if self.file.blank? || self.file.file.blank?
      Rails.application.routes.url_helpers.download_url(
        self.file.medium_hash,
        host: ENV["RAILS_RELATIVE_URL_ROOT"]
      )
    when Admissions::FormField::STUDENT_FIELD
      configuration = self.form_field.config_hash
      if ["special_city", "special_birth_city"].include? configuration["field"]
        self.to_text(
          blank: blank, field_type: Admissions::FormField::CITY, custom: custom)
      elsif configuration["field"] == "special_address"
        self.to_text(
          blank: blank, field_type: Admissions::FormField::RESIDENCY, custom: custom)
      elsif configuration["field"] == "special_majors"
        self.to_text(
          blank: blank, field_type: Admissions::FormField::SCHOLARITY, custom: custom)
      elsif configuration["field"] == "photo"
        self.to_text(
          blank: blank, field_type: Admissions::FormField::FILE, custom: custom)
      else
        self.to_text(
          blank: blank, field_type: Admissions::FormField::STRING, custom: custom)
      end
    when Admissions::FormField::CITY
      return blank if self.value.blank?
      values = (self.value || "").split(" <$> ")
      (3 - values.length).times { values << "" }
      values.join(", ")
    when Admissions::FormField::RESIDENCY
      return blank if self.value.blank?
      values = (self.value || "").split(" <$> ")
      values.join(", ")
    when Admissions::FormField::SCHOLARITY
      return blank if self.scholarities.blank?
      values = form_field.get_values_map("values")
      statuses = form_field.get_values_map("statuses")
      self.scholarities.map do |scholarity|
        scholarity.to_label(values: values, statuses: statuses)
      end.join("; ")
    else
      return blank if self.value.blank?
      self.value
    end
  end

  def simple_value
    if self.file.present? && self.file.file.present?
      self.file
    elsif !self.list.nil?
      self.list
    else
      self.value
    end
  end

  def add_error(error, **options)
    attribute = :value
    if self.form_field.field_type == Admissions::FormField::FILE
      attribute = :file
    elsif self.form_field.field_type == Admissions::FormField::COLLECTION_CHECKBOX
      attribute = :list
    end
    if self.class.respond_to?(:i18n_scope)
      i18n_scope = self.class.i18n_scope.to_s
      defaults = self.class.lookup_ancestors.flat_map do |klass|
        [ :"#{i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{error}" ]
      end
      defaults << :"#{i18n_scope}.errors.messages.#{error}"

      catch(:exception) do
        translation = I18n.translate(defaults.first, **options.merge(default: defaults.drop(1), throw: true))
        return self.errors.add(
          attribute, I18n.t(
            "activerecord.errors.full_messages.format",
            attribute: self.form_field.name,
            message: translation
          )
        ) unless translation.nil?
      end unless options[:message]
    else
      defaults = []
    end
    defaults << :"errors.messages.#{error}"
    key = defaults.shift
    defaults = options.delete(:message) if options[:message]
    options[:default] = defaults
    self.errors.add(attribute, I18n.t(
      "activerecord.errors.full_messages.format",
      attribute: self.form_field.name,
      message: I18n.translate(key, **options)
    ))
  end

  def set_model_field(
    update_log, model, model_attribute, simple: nil, to_string: :to_s)
    value = model.public_send(model_attribute)
    simple ||= self.simple_value
    if value.present? && (value_s = value.send(to_string)) != simple.send(to_string)
      message = "#{model.class.record_i18n_attr(model_attribute)} alterado."
      message += " Valor anterior: #{value_s}" if value_s.size < 5000
      update_log << message
    end
    model.assign_attributes(model_attribute => simple) if simple.present?
  end

  def set_model_place_field(
    place_cls, place, update_log, model, model_attribute, not_found, args
  )
    place ||= place_cls.search_name(**args).first
    if place.nil?
      update_log << not_found
    else
      self.set_model_field(
        update_log, model, model_attribute, simple: place,
        to_string: :full_name
      )
    end
  end

  def get_type
    case self.form_field.field_type
    when Admissions::FormField::NUMBER
      "number"
    when Admissions::FormField::DATE
      "date"
    when Admissions::FormField::STUDENT_FIELD
      configuration = self.form_field.config_hash
      case configuration["field"]
      when "birthdate", "identity_expedition_date"
        "date"
      else
        "string"
      end
    else
      "string"
    end
  end

  def self.convert_value(value, type)
    case type
    when "number"
      self.convert_number(value)
    when "string"
      self.convert_string(value)
    when "date"
      self.convert_date(value)
    else
      value
    end
  end

  def self.convert_string(value)
    value.to_s
  rescue
    nil
  end

  def self.convert_number(value)
    value.to_f
  rescue
    nil
  end

  def self.convert_date(value)
    Date.strptime(value, "%d/%m/%Y")
  rescue
    nil
  end
end