module JamRuby class Teacher < ActiveRecord::Base include HtmlSanitize html_sanitize strict: [:biography, :website] attr_accessor :validate_introduction, :validate_basics, :validate_pricing attr_accessible :genres, :teacher_experiences, :experiences_teaching, :experiences_education, :experiences_award has_and_belongs_to_many :genres, :class_name => "JamRuby::Genre", :join_table => "teachers_genres", :order => "description" has_and_belongs_to_many :instruments, :class_name => "JamRuby::Instrument", :join_table => "teachers_instruments", :order => "description" has_and_belongs_to_many :subjects, :class_name => "JamRuby::Subject", :join_table => "teachers_subjects", :order => "description" has_and_belongs_to_many :languages, :class_name => "JamRuby::Language", :join_table => "teachers_languages", :order => "description" has_many :teacher_experiences, :class_name => "JamRuby::TeacherExperience" has_many :experiences_teaching, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'teaching'} has_many :experiences_education, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'education'} has_many :experiences_award, :class_name => "JamRuby::TeacherExperience", conditions: {experience_type: 'award'} has_many :reviews, :class_name => "JamRuby::Review", as: :target has_one :review_summary, :class_name => "JamRuby::ReviewSummary", as: :target has_one :user, :class_name => 'JamRuby::User' validates :user, :presence => true validates :biography, length: {minimum: 5, maximum: 4096}, :if => :validate_introduction validates :introductory_video, :format => {:with => /^(?:https?:\/\/)?(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=)?([\w-]{10,})/, message: "is not a valid youtube URL"}, :allow_blank => true, :if => :validate_introduction validates :years_teaching, :presence => true, :if => :validate_introduction validates :years_playing, :presence => true, :if => :validate_introduction validates :teaches_test_drive, inclusion: {in: [true, false]}, :if => :validate_pricing validates :top_rated, inclusion: {in: [true, false]} validates :test_drives_per_week, numericality: {only_integer: true, minimum: 2, maximum: 10}, :if => :validate_pricing validates :instruments, :length => {minimum: 1, message: "At least one instrument or subject is required"}, if: :validate_basics, unless: ->(teacher) { teacher.subjects.length>0 } validates :subjects, :length => {minimum: 1, message: "At least one instrument or subject is required"}, if: :validate_basics, unless: ->(teacher) { teacher.instruments.length>0 } validates :genres, :length => {minimum: 1, message: "At least one genre is required"}, if: :validate_basics validates :languages, :length => {minimum: 1, message: "At least one language is required"}, if: :validate_basics validate :offer_pricing, :if => :validate_pricing validate :offer_duration, :if => :validate_pricing validate :teaches_ages, :if => :validate_basics default_scope { includes(:genres).order('created_at desc') } def self.index(user, params = {}) limit = params[:per_page] limit ||= 20 limit = limit.to_i query = User.joins(:teacher) instruments = params[:instruments] if instruments && !instruments.blank? && instruments.length > 0 query = query.joins("inner JOIN teachers_instruments AS tinst ON tinst.teacher_id = teachers.id") .where("tinst.instrument_id IN (?)", instruments) end subjects = params[:subjects] if subjects && !subjects.blank? && subjects.length > 0 query = query.joins("inner JOIN teachers_subjects AS tsubjs ON tsubjs.teacher_id = teachers.id") .where('tsubjs.subject_id IN (?)', subjects) end genres = params[:genres] if genres && !genres.blank? && genres.length > 0 query = query.joins("inner JOIN teachers_genres AS tgenres ON tgenres.teacher_id = teachers.id") .where('tgenres.genre_id IN (?)', genres) end country = params[:country] if country && country.length > 0 query = query.where(country: country) end region = params[:region] if region && region.length > 0 query = query.where(state: region) end languages = params[:languages] if languages && !languages.blank? && languages.length > 0 query= query.joins("inner JOIN teachers_languages AS tlang ON tlang.teacher_id = teachers.id") .where('tlang.language_id IN (?)', languages) end years_teaching = params[:years_teaching].to_i if years_teaching && years_teaching > 0 query = query.where('years_teaching >= ?', years_teaching) end teaches_beginner = params[:teaches_beginner] teaches_intermediate = params[:teaches_intermediate] teaches_advanced = params[:teaches_advanced] if teaches_beginner || teaches_intermediate || teaches_advanced clause = '' if teaches_beginner clause << 'teaches_beginner = true' end if teaches_intermediate if clause.length > 0 clause << ' OR ' end clause << 'teaches_intermediate = true' end if teaches_advanced if clause.length > 0 clause << ' OR ' end clause << 'teaches_advanced = true' end query = query.where(clause) end student_age = params[:student_age].to_i if student_age && student_age > 0 query = query.where("teaches_age_lower <= ? AND (CASE WHEN teaches_age_upper = 0 THEN true ELSE teaches_age_upper >= ? END)", student_age, student_age) end current_page = params[:page].nil? ? 1 : params[:page].to_i next_page = current_page + 1 # will_paginate gem query = query.paginate(:page => current_page, :per_page => limit) if query.length == 0 # no more results {query: query, next_page: nil} elsif query.length < limit # no more results {query: query, next_page: nil} else {query: query, next_page: next_page} end end def self.save_teacher(user, params) teacher = build_teacher(user, params) teacher.save teacher end def self.build_teacher(user, params) # ensure person creating this Teacher is a Musician unless user && user.musician? raise JamPermissionError, "must be a musician" end teacher = user.teacher teacher ||= user.build_teacher() teacher.user = user teacher.website = params[:website] if params.key?(:website) teacher.biography = params[:biography] if params.key?(:biography) teacher.introductory_video = params[:introductory_video] if params.key?(:introductory_video) teacher.introductory_video = params[:introductory_video] if params.key?(:introductory_video) teacher.years_teaching = params[:years_teaching] if params.key?(:years_teaching) teacher.years_playing = params[:years_playing] if params.key?(:years_playing) teacher.teaches_age_lower = params[:teaches_age_lower] if params.key?(:teaches_age_lower) teacher.teaches_age_upper = params[:teaches_age_upper] if params.key?(:teaches_age_upper) teacher.website = params[:website] if params.key?(:website) teacher.biography = params[:biography] if params.key?(:biography) teacher.teaches_beginner = params[:teaches_beginner] if params.key?(:teaches_beginner) teacher.teaches_intermediate = params[:teaches_intermediate] if params.key?(:teaches_intermediate) teacher.teaches_advanced = params[:teaches_advanced] if params.key?(:teaches_advanced) teacher.prices_per_lesson = params[:prices_per_lesson] if params.key?(:prices_per_lesson) teacher.prices_per_month = params[:prices_per_month] if params.key?(:prices_per_month) teacher.lesson_duration_30 = params[:lesson_duration_30] if params.key?(:lesson_duration_30) teacher.lesson_duration_45 = params[:lesson_duration_45] if params.key?(:lesson_duration_45) teacher.lesson_duration_60 = params[:lesson_duration_60] if params.key?(:lesson_duration_60) teacher.lesson_duration_90 = params[:lesson_duration_90] if params.key?(:lesson_duration_90) teacher.lesson_duration_120 = params[:lesson_duration_120] if params.key?(:lesson_duration_120) teacher.price_per_lesson_30_cents = params[:price_per_lesson_30_cents] if params.key?(:price_per_lesson_30_cents) teacher.price_per_lesson_45_cents = params[:price_per_lesson_45_cents] if params.key?(:price_per_lesson_45_cents) teacher.price_per_lesson_60_cents = params[:price_per_lesson_60_cents] if params.key?(:price_per_lesson_60_cents) teacher.price_per_lesson_90_cents = params[:price_per_lesson_90_cents] if params.key?(:price_per_lesson_90_cents) teacher.price_per_lesson_120_cents = params[:price_per_lesson_120_cents] if params.key?(:price_per_lesson_120_cents) teacher.price_per_month_30_cents = params[:price_per_month_30_cents] if params.key?(:price_per_month_30_cents) teacher.price_per_month_45_cents = params[:price_per_month_45_cents] if params.key?(:price_per_month_45_cents) teacher.price_per_month_60_cents = params[:price_per_month_60_cents] if params.key?(:price_per_month_60_cents) teacher.price_per_month_90_cents = params[:price_per_month_90_cents] if params.key?(:price_per_month_90_cents) teacher.price_per_month_120_cents = params[:price_per_month_120_cents] if params.key?(:price_per_month_120_cents) teacher.teaches_test_drive = params[:teaches_test_drive] if params.key?(:teaches_test_drive) teacher.test_drives_per_week = params[:test_drives_per_week] if params.key?(:test_drives_per_week) # Many-to-many relations: if params.key?(:genres) genres = params[:genres] genres = [] if genres.nil? teacher.genres = genres.collect { |genre_id| Genre.find(genre_id) } end if params.key?(:instruments) instruments = params[:instruments] instruments = [] if instruments.nil? teacher.instruments = instruments.collect { |instrument_id| Instrument.find(instrument_id) } end if params.key?(:subjects) subjects = params[:subjects] subjects = [] if subjects.nil? teacher.subjects = subjects.collect { |subject_id| Subject.find(subject_id) } end if params.key?(:languages) languages = params[:languages] languages = [] if languages.nil? teacher.languages = languages.collect { |language_id| Language.find(language_id) } end # Experience: [:teaching, :education, :award].each do |experience_type| key = "experiences_#{experience_type}".to_sym if params.key?(key) list = params[key] list = [] if list.nil? experiences = list.collect do |exp| TeacherExperience.new( name: exp[:name], experience_type: experience_type, organization: exp[:organization], start_year: exp[:start_year], end_year: exp[:end_year] ) end # collect # we blindly destroy/recreate on every resubmit previous = teacher.send("#{key.to_s}") previous.destroy_all # Dynamically call the appropriate method (just setting the # value doesn't result in the behavior we need) teacher.send("#{key.to_s}=", experiences) end # if end # do # How to validate: teacher.validate_introduction = !!params[:validate_introduction] teacher.validate_basics = !!params[:validate_basics] teacher.validate_pricing = !!params[:validate_pricing] teacher end def offer_pricing unless prices_per_lesson.present? || prices_per_month.present? errors.add(:offer_pricing, "Must choose to price per lesson or per month") end end def offer_duration unless lesson_duration_30.present? || lesson_duration_45.present? || lesson_duration_60.present? || lesson_duration_90.present? || lesson_duration_120.present? errors.add(:offer_duration, "Must offer at least one duration") end end def teaches_ages if teaches_age_lower > 0 && teaches_age_upper > 0 && (teaches_age_upper < teaches_age_lower) errors.add(:ages_taught, "Age range is backwards") end end def recent_reviews reviews.order('created_at desc').limit(20) end def mark_background_checked self.background_check_at = Time.now self.save! end def mark_session_ready self.ready_for_session_at = Time.now self.save! end def mark_top_rated self.top_rated = true self.save! end def mark_not_top_rated self.top_rated = false self.save! end def has_experiences_teaching? experiences_teaching.count > 0 end def has_experiences_education? experiences_education.count > 0 end def has_experiences_award? experiences_award.count > 0 end def has_stripe_billing? false end def has_instruments_or_subject? instruments.count > 0 || subjects.count > 0 end def has_genres? genres.count > 0 end def has_languages? languages.count > 0 end def teaches_ages_specified? (!teaches_age_lower.nil? && teaches_age_lower > 0) || (!teaches_age_upper.nil? && teaches_age_upper > 0) end def teaching_level_specified? teaches_beginner || teaches_intermediate || teaches_advanced end def has_pricing_specified? specified = false durations_allowed = [] [30, 45, 60, 90, 120].each do |i| durations_allowed << i if self["lesson_duration_#{i}"] end durations_allowed.each do |i| if self["price_per_lesson_#{i}_cents"] || self["price_per_month_#{i}_cents"] specified = true break end end specified end def has_name_specified? !user.anonymous? end # how complete is their profile? def pct_complete @part_complete ||= { name_specified: has_name_specified?, experiences_teaching: has_experiences_teaching?, experiences_education: has_experiences_education?, experiences_award: has_experiences_award?, has_stripe_account: has_stripe_billing?, has_teacher_bio: !biography.nil?, intro_video: !introductory_video.nil?, years_teaching: years_teaching > 0, years_playing: years_playing > 0, instruments_or_subject: has_instruments_or_subject?, genres: genres.count > 0, languages: languages.count > 0, teaches_ages_specified: teaches_ages_specified?, teaching_level_specified: teaching_level_specified?, has_pricing_specified: has_pricing_specified? } done = 0 @part_complete.each do |k, v| if v done += 1 end end complete = 100.0 * done.to_f / @part_complete.length.to_f @part_complete[:pct] = complete.round @part_complete end end end