app/models/questionnaire.rb
class Questionnaire < ApplicationRecord
audited
include ActiveModel::Dirty
include DeletableAttachment
before_validation :consolidate_school_names
before_validation :clean_for_non_rsvp
before_validation :clean_negative_special_needs
before_validation :clean_negative_dietary_restrictions
after_create :queue_triggered_email_create
after_update :queue_triggered_email_update
after_update :queue_triggered_email_rsvp_reminder
after_update :queue_triggered_email_checked_in
after_save :update_school_questionnaire_count
after_destroy :update_school_questionnaire_count
belongs_to :user
belongs_to :school
belongs_to :bus_list, optional: true
has_and_belongs_to_many :agreements
validates_uniqueness_of :user_id
validates_presence_of :phone, :date_of_birth, :school_id, :experience, :shirt_size, :interest
validates_presence_of :gender, :major, :level_of_study, :graduation_year, :race_ethnicity, :country
DIETARY_SPECIAL_NEEDS_MAX_LENGTH = 500
validates_length_of :dietary_restrictions, maximum: DIETARY_SPECIAL_NEEDS_MAX_LENGTH
validates_length_of :special_needs, maximum: DIETARY_SPECIAL_NEEDS_MAX_LENGTH
validate :agreements_present
# if HackathonManager.field_enabled?(:why_attend)
# validates_presence_of :why_attend
# end
has_one_attached :resume
deletable_attachment :resume
validates :resume, file_size: { less_than_or_equal_to: 2.megabytes },
file_content_type: { allow: ['application/pdf'] },
if: -> { resume.attached? }
validates :portfolio_url, url: { allow_blank: true }
validates :vcs_url, url: { allow_blank: true }
validates_format_of :vcs_url,
with: %r{\A((https?:\/\/)?(www\.)?((github\.com)|(gitlab\.com)|(bitbucket\.org))\/(.*){0,62})\z}i,
allow_blank: true,
message: "Must be a GitHub, GitLab or Bitbucket url"
strip_attributes
POSSIBLE_EXPERIENCES = {
"first" => "This is my 1st hackathon!",
"experienced" => "My feet are wet. (1-5 hackathons)",
"expert" => "I'm a veteran hacker. (6+ hackathons)"
}.freeze
POSSIBLE_INTERESTS = {
"design" => "Design",
"software" => "Software",
"hardware" => "Hardware",
"combination" => "Combination of everything!"
}.freeze
POSSIBLE_SHIRT_SIZES = [
"Women's - XS",
"Women's - S",
"Women's - M",
"Women's - L",
"Women's - XL",
"Unisex - XS",
"Unisex - S",
"Unisex - M",
"Unisex - L",
"Unisex - XL"
].freeze
POSSIBLE_ACC_STATUS = {
"pending" => "Pending Review",
"accepted" => "Accepted",
"waitlist" => "Waitlisted",
"denied" => "Denied",
"late_waitlist" => "Waitlisted, Late",
"rsvp_confirmed" => "RSVP Confirmed",
"rsvp_denied" => "RSVP Denied"
}.freeze
POSSIBLE_GRAD_YEARS = (Date.today.year - 3...Date.today.year + 7).to_a.freeze
POSSIBLE_RACE_ETHNICITIES = [
"American Indian or Alaskan Native",
"Asian / Pacific Islander",
"Black or African American",
"Hispanic",
"White / Caucasian",
"Multiple ethnicities / Other",
"Prefer not to answer"
].freeze
# From My MLH's dropdown list.
# Should *not* validate against this list in case My MLH changes their options,
# as this would cause errors until a manual gem update
POSSIBLE_GENDERS = [
"Female",
"Male",
"Non-Binary",
"I prefer not to say",
"Other"
].freeze
# From My MLH's dropdown list.
# Should *not* validate against this list in case My MLH changes their options,
# as this would cause errors until a manual gem update
POSSIBLE_LEVELS_OF_STUDY = [
"Elementary / Middle School / Primary School",
"High School / Secondary School",
"University (Undergraduate)",
"University (Master's / Doctoral)",
"Vocational / Code School",
"Not Currently a Student",
"Other"
].freeze
POSSIBLE_COUNTRIES = [
"United States",
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola",
"Antigua and Barbuda",
"Argentina",
"Armenia",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Brazil",
"Brunei",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Cape Verde",
"Central African Republic",
"Chad",
"Chile",
"China",
"Colombia",
"Comoros",
"Congo, Democratic Republic of the Congo",
"Congo, Republic of the",
"Costa Rica",
"Cote d'Ivoire (Ivory Coast)",
"Croatia",
"Cuba",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"Timor-Leste (East Timor)",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Ethiopia",
"Fiji",
"Finland",
"France",
"Gabon",
"Gambia, The",
"Georgia",
"Germany",
"Ghana",
"Greece",
"Grenada",
"Guatemala",
"Guinea",
"Guinea-Bissau",
"Guyana",
"Haiti",
"Honduras",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran",
"Iraq",
"Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Korea, North",
"Korea, South",
"Kuwait",
"Kyrgyzstan",
"Laos",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Macedonia, North",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Mauritania",
"Mauritius",
"Mexico",
"Micronesia",
"Moldova",
"Monaco",
"Mongolia",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nauru",
"Nepal",
"Netherlands",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Poland",
"Portugal",
"Qatar",
"Romania",
"Russia",
"Rwanda",
"Saint Kitts and Nevis",
"Saint Lucia",
"Saint Vincent",
"Samoa",
"San Marino",
"Sao Tome and Principe",
"Saudi Arabia",
"Senegal",
"Serbia and Montenegro",
"Seychelles",
"Sierra Leone",
"Singapore",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Sudan",
"Spain",
"Sri Lanka",
"Sudan",
"Suriname",
"Swaziland",
"Sweden",
"Switzerland",
"Syria",
"Taiwan",
"Tajikistan",
"Tanzania",
"Thailand",
"Togo",
"Tonga",
"Trinidad and Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Vatican City",
"Venezuela",
"Vietnam",
"Yemen",
"Zambia",
"Zimbabwe"
].freeze
validates_inclusion_of :experience, in: POSSIBLE_EXPERIENCES
validates_inclusion_of :interest, in: POSSIBLE_INTERESTS
# validates_inclusion_of :school_id, :in => School.select(:id)
validates_inclusion_of :shirt_size, in: POSSIBLE_SHIRT_SIZES
validates_inclusion_of :acc_status, in: POSSIBLE_ACC_STATUS
def email
user&.email
end
def portfolio_url=(value)
value = value.downcase unless value.blank?
value = "http://" + value if !value.blank? && !value.include?("http://") && !value.include?("https://")
super value
end
def vcs_url=(value)
value = value.downcase unless value.blank?
value = "https://" + value if !value.blank? && !value.include?("http://") && !value.include?("https://")
super value
end
def phone=(value)
# strips the string to just numbers for standardization
value = value.try(:tr, '^0-9', '')
super value
end
def school
School.find(school_id) if school_id
end
def school_name
school.name if school_id
end
def full_location
"#{school.city}, #{school.state}"
end
def date_of_birth_formatted
date_of_birth.strftime("%B %-d, %Y")
end
def acc_status_author
return unless acc_status_author_id.present?
User.find_by_id(acc_status_author_id)
end
def checked_in?
checked_in_at.present?
end
def boarded_bus?
boarded_bus_at.present?
end
def checked_in_by
return unless checked_in_by_id.present?
User.find_by_id(checked_in_by_id)
end
def fips_code
Fips.where(city: school.city, state: school.state).first
end
def age_at_time_of_event
(Date.parse(HackathonConfig['event_start_date']) - date_of_birth).to_i * 1.day
end
def minor?
age_at_time_of_event < 18.years
end
def can_rsvp?
["accepted", "rsvp_confirmed", "rsvp_denied"].include? acc_status
end
def did_rsvp?
['rsvp_confirmed', 'rsvp_denied'].include? acc_status
end
def verbal_status
if acc_status == "rsvp_denied"
"Not Attending"
elsif acc_status == "rsvp_confirmed"
"Accepted & Attending"
elsif acc_status == "accepted"
"Accepted, Awaiting RSVP"
elsif acc_status == "pending"
"Pending Review"
elsif ["waitlist", "late_waitlist"].include? acc_status
"Waitlisted"
elsif acc_status == "denied"
"Denied"
end
end
def agreements_present
if (Agreement.all - agreements).any?
errors.add(:agreements, "must be accepted.")
end
end
def all_agreements_accepted?
(Agreement.all - agreements).empty?
end
def unaccepted_agreements
Agreement.all - agreements
end
def as_json(options = {})
result = super
result['all_agreements_accepted'] = all_agreements_accepted?
result
end
private
def clean_for_non_rsvp
if acc_status != "rsvp_confirmed"
self.bus_list_id = nil
self.is_bus_captain = false
self.bus_captain_interest = false
end
end
def clean_negative_special_needs
self.special_needs = nil if special_needs.present? && %w[none n/a non-applicable na nothing nil null no].include?(special_needs.strip.downcase)
end
def clean_negative_dietary_restrictions
self.dietary_restrictions = nil if dietary_restrictions.present? && %w[none n/a non-applicable na nothing nil null no].include?(dietary_restrictions.strip.downcase)
end
def consolidate_school_names
return if school.blank?
duplicate = SchoolNameDuplicate.find_by(name: school.name)
return if duplicate.blank?
self.school_id = duplicate.school_id
end
def update_school_questionnaire_count
if destroyed?
School.decrement_counter(:questionnaire_count, school_id)
elsif saved_change_to_school_id?
old_school_id = saved_changes['school_id'].first
School.decrement_counter(:questionnaire_count, old_school_id) if old_school_id.present?
School.increment_counter(:questionnaire_count, school_id)
end
end
def queue_triggered_email_update
Message.queue_for_trigger("questionnaire.#{acc_status}", user_id) if saved_change_to_acc_status?
end
def queue_triggered_email_create
Message.queue_for_trigger("questionnaire.#{acc_status}", user_id)
end
def queue_triggered_email_checked_in
return unless saved_change_to_checked_in_at && checked_in?
Message.queue_for_trigger("questionnaire.checked-in", user_id)
end
def queue_triggered_email_rsvp_reminder
return unless saved_change_to_acc_status? && acc_status == "accepted"
event_start = Date.parse(HackathonConfig["event_start_date"]).in_time_zone
days_remaining = event_start.to_date - Time.now.in_time_zone.to_date
if days_remaining > 14
deliver_date = 7.days.from_now
elsif days_remaining > 10
deliver_date = 5.days.from_now
elsif days_remaining > 3
deliver_date = 2.days.from_now
end
UserMailer.rsvp_reminder_email(user_id).deliver_later(wait_until: deliver_date) if deliver_date.present?
end
end