require 'find' ValSeparator='~' MenuFolder='menus/' TemplatesFolder = 'sentencetemplates/' PicListFolder='piclists/' # Filters added to this controller apply to all controllers in the application. # Likewise, all the methods added will be available for all controllers. class ApplicationController < ActionController::Base helper :all # include all helpers, all the time # See ActionController::RequestForgeryProtection for details # Uncomment the :secret if you're not using the cookie session store protect_from_forgery # :secret => '2d78e3a9f11c3fe227052e6c5e36141d' # See ActionController::Base for details # Uncomment this to filter the contents of submitted # sensitive data parameters # from your application log (in this case, all fields with # names like "password"). # filter_parameter_logging :password MaxRows=6 WordsDir='public/words/' MaxWords=30 SentencesDir='public/words/sentences/' Mchoices='public/multiplechoices' LoginFolder='users/' LineLen=150 require 'rubygems' require 'hpricot' #require 'festivaltts4r' # Pick a unique cookie name to distinguish our session # data from others' session :session_key => '_table_session_id' session :session_expires => 3.years.from_now def modulecombos {'fun'=>'nim+picbrowser+recognize+langselect+presentation', 'write text'=> 'abcd+words+nextword+modsentence+easypic'+ '+backspace+scroll+scrolldown', 'learn'=>'addcredit+oddmanout+howmany+aoran+quiz+choices+whatbefore', # 'use text'=>'emailaddr+wikipedia+bulletin', 'picture editing'=>'stepsize+picedit+reflect+picbrowser'} end # path_from_login returns nil unless in LoginFolder # file "#{user}.txt" the string pstarter is found. # if pend is nil, all text in that line is returned, else # all text upto pend is returned, split at comma def path_from_login(user,pstarter,pend=nil) begin if user && str=IO.read("#{LoginFolder}#{user}.txt") if pend r= str=~ /#{pstarter}:(.*)#{pend}/ ? $1.split(/\s*,\s*/) : nil else r= str=~ /#{pstarter}:\s*(\S+)/ ? [$1] : nil end return r end nil rescue nil end end def listed_controllers(user) a=[] begin if user && str=IO.read("#{LoginFolder}#{user}.txt") # logger.info "userfile #{str}" a=str.scan(/\w+:/).collect{|x| x.gsub(/:/,'')} end rescue end a end def get_quiz_dir if p=path_from_login(session[:login],'multiplechoices') p.first else Mchoices end end def get_sentences_dir if p=path_from_login(session[:login],'sentences') p.first else SentencesDir end end def get_words_dir if p=path_from_login(session[:login],'words') p.first else WordsDir end end def get_question_no session[:question_no] ||= 0 end def get_correct_answer session[:correct_answer] || 'this is a mistake' end def get_correct session[:correct] ||=0 end def get_wrong session[:wrong] ||= 0 end #separator used to fit more than one choice per line #if the separator is nowhere among the choices, #reformat_choices tries to rearrange the choices #using the separator evenly among the rows def get_separator ',' end #returns a string of keywords the user has picked #used by wikinext to select wikipedia pages for #next word prediction def get_keywords session[:keywords] || '' end # def get_criteria # session[:criteria] || [] # end def get_credit session[:credit] ||= 5 end # #passes_criteria returns true if all strings in # #criteria array found in path # def passes_criteria(path,criteria) # criteria.each do |c| # if !path.match(c) # return nil # end # end # p "#{path} passes #{criteria}" # return true # end #gffp looks under public_path for files that #pass criteria and match frx, returns $1's in array def get_filtered_filename_part(searchpath,frx) return_files=[] Find.find(searchpath) do |path| if FileTest.directory?(path) if (File.basename(path)[0] == ?.) Find.prune # Don't look any further into end #this directory. elsif path.match(frx) found=$1 if found return_files << found end end end # p "found #{return_files}" return_files.sort end #find words in filename that match rx def matching_words_from_file(filename, rx) str ="" str=IO.read(filename) str.strip.scan(/[\w']+/).select{|w| w.match(rx)} end #get_matching_words returns an array #of words in txt files #under root_dir that #match rx,alphabetically sorted def get_matching_words (root_dir, rx) rxfile=Regexp.new( '([\w,/, ]*\.txt)$',Regexp::IGNORECASE) # full path of txt files @files=get_filtered_filename_part(root_dir,rxfile) # logger.info "#{root_dir}~~#{@files.inspect}" @words=[] @files.each do |f| @words += matching_words_from_file(f,rx) end @words.collect{|w| w.strip}.uniq.sort end def random_word random_pics(get_folder,1).first # first_chars=('a'..'z').to_a.shuffle # logger.info "randomized alphabet #{first_chars}" # begin # fc=first_chars.pop # rx=/^#{fc}/ # logger.info "random first char #{fc}" # unless (w=get_matching_words(get_words_dir,rx)).empty? # logger.info "words of random letter #{w}" # return w[rand w.size] # end # end until first_chars.empty? # '' end #get_matching_sentences returns sentences #in root_dir folder matching rx def get_matching_sentences (root_dir,rx) files=get_filtered_filename_part(root_dir, '(.+txt)$') sentences=[] logger.info "sentence files #{files}" files.each do |fs| # p("searching file #{fs}") f=open(fs) f.each do |s| # p("searching sentence #{s}") if s.match rx sentences << s.strip end end end sentences end #returns words that start sentences or appear #after a comma or semicolon in root_dir def get_starting_words(root_dir) rx=/\w/ ac=get_matching_sentences(root_dir,rx) nw=ac.collect do |se| if se =~ /^\s*([A-Z,a-z]\w*)/ $1 end end nw.uniq.sort{|a,b| a.length <=> b.length} end #get_textfile_list returns a filtered list of #.txt files in the subfolders of containg_folder def get_textfile_list (containg_folder) rx=Regexp.new(containg_folder+ '/(.*)\.txt$', Regexp::IGNORECASE) g=get_filtered_filename_part(containg_folder,rx) g end def full_file_name (folder,partname) rx=Regexp.new('(.*'+folder+ '/'+partname+'\..*)', Regexp::IGNORECASE) get_filtered_filename_part(folder,rx)[0] end #get_page returns nth page as array of lines #blank line is page separator def get_page(folder,file,n) if f=full_file_name(folder,file) str=IO.read(f) # logger.info "entire file #{f}: #{str}" pages=str.split(/\s*\n\s*\n/) if n < pages.size pages[n].split(/\s*\n/) else [] end else [] end end def get_presentation session[:presentation] || '' end #stepsize is used in picture editing def get_stepsize session[:stepsize] ||= 10 end def get_user session[:login] ||= 'default' end #get_folder_list returns array of non-empty image folders def get_folder_list u= get_user if p=path_from_login(u,selfname,'.') return p.reject {|f| get_pic(f,0).empty?} end g=Giver.new folders=g.get_subdirs("images") # logger.info "image folders #{folders}" folders.reject {|f| get_pic(f,0).empty?} end #current folder for picbrowser, recognize,.. def get_folder_index session[:folder_index] ||= 0 end #get_folder returns i'th folder, but if no i provided, a random one def get_folder(i=nil) i=rand get_folder_list.size unless i fl=get_folder_list fl.empty? ? '': fl[i% fl.size] end #sibling_pics returns names of all files in same folder as pic def sibling_pics pic g = Giver.new rx=Regexp.new('images/(.*)/'+"#{pic}"+ '\.(JPEG|JPG|GIF|PNG)$', Regexp::IGNORECASE) folder=g.get_filename_part('images/',rx).first logger.info "sibling folder #{folder}" if folder rx=Regexp.new('images/'+folder+ '/(.*)\.(JPEG|JPG|GIF|PNG)$', Regexp::IGNORECASE) words=g.get_filename_part('images/'+ folder ,rx) else [] end end #random_pics returns n picture names at random #from the desired folder def random_pics(folder, n) # logger.info "random picture folder #{folder}" g = Giver.new rx=Regexp.new('images/'+folder+ '/(.*)\.(JPEG|JPG|GIF|PNG)$', Regexp::IGNORECASE) words=g.get_filename_part('images/'+ folder ,rx) selwords=[] while (selwords.size < n) && !words.empty? selwords.push(words. delete_at(rand(words.size))) end selwords end #get_pic returns normalized i'th picture #from desired folder def get_pic(folder, i) g ||= Giver.new rx=Regexp.new( 'images/(.*(JPEG|JPG|GIF|PNG))$', Regexp::IGNORECASE) pictures=g.get_filename_part('images/'+ folder ,rx) if pictures.empty? "" else pic = pictures[i% pictures.size] || '' # logger.info("giving picture #{i} from "+folder+': #' +pic #) pic end end def pic_folder "images" end # get_pic_file returns a filename suitable for #passing to image_tag if what is in name # is the basename of an image file in the #subfolders of public/images def get_pic_file(name) unless name && !name.empty? return '' end g=Giver.new pf=g.get_filename_part(pic_folder, Regexp.new('images/(.*/'+name+'\.(JPEG|JPG|GIF|PNG))$', Regexp::IGNORECASE)) # logger.info "getting pic #{pf.inspect} for #{name}" if pf.empty? '' else pf.first end end # get_pic_from_folder returns filename suitable for #passing to image_tag if what is in name # is the basename of an image file in the #subfolders of public/images/folder def get_pic_from_folder(name,folder) g=Giver.new pf=g.get_filename_part("images/#{folder}", Regexp.new('images/(.*/'+name+'\.(JPEG|JPG|GIF|PNG))$', Regexp::IGNORECASE)) if pf.empty? '' else pf.first end end #~ def format_pic (pic) ' ' end #get_pic_html returns the html for picname if #such a pic exists, else empty string def get_pic_html(picname) # p=get_pic_file picname if p.empty? p else format_pic p end end #get_rxpics returns an array of pictures #whose names match rx def get_rxpics(folder, rx) g ||= Giver.new pictures=g.get_filename_part('images/'+ folder ,rx) end #one choice is shown in selected_font #get_highlighted_choice returns it def get_highlighted_choice # logger.info "highlighted choice #{@selected_item} of #{@choices.inspect}" if @choices.empty? '' else @choices[@selected_item% get_choices_size] end end #selfname returns name of current_controller def selfname self.class.name. gsub(/Controller/,'').downcase end #make_clickable returns unchanged anything #containing an anchor. otherwise encloses tch #in an anchor link. Displayed is num if it exists #else tch with space replaced by - unless nowords set def make_clickable(tch, num=nil) if tch.match(/' + tch +'' # logger.info "show #{show}" end end #max_choices_in_line is used in an attempt to evenly #divide the choices among several lines def max_choices_in_line Math.sqrt(@choices.size).floor+1 end #line_full provides the #criterion used to determine that no more choices #should be added to this line def line_full ln Hpricot(ln).inner_text.length > LineLen end #reformat_choices reformats @choices and #makes each into a link def reformat_choices if !@choices || @choices.empty? return end # logger.info "reformat choices #{ # @choices.each {|c| c.inspect}}" if (@choices.size<=MaxRows) or (@choices.join.match(get_separator)) @choices.collect! do |cl| ca=cl ? cl.split(get_separator) :[] ca.collect!{|c| make_clickable c} s=ca.join(get_separator) # logger.info "reformatted choices #{ca}" s end return end @lines=[] lcount=0 # session[:selected_item]=@selected_item=0 #max_choices_in_line=Math.sqrt(@choices.size).floor+1 @choices=@choices.reverse (0..MaxRows).each do |row| @lines[row]='' (0..max_choices_in_line).each do |item| tch=@choices.pop @lines[row] << make_clickable(tch) << get_separator # if (@lines[row].length > LineLen) or if line_full(@lines[row]) or @choices.empty? break end end if @choices.empty? break end end @choices= @lines.collect do |l| l.strip.chop # removes separator at the end end end #gets the last word from str def last_word(str) if str && str.strip.match('(\w+)$') $1 else '' end end #updownevent is overridden by modules that need some #processing each time up or down is pressed def updownevent end #default action when up is selected def up session[:selected_item]=get_selected_item-1 updownevent what_next end #default action when down is selected def down session[:selected_item]=get_selected_item+1 updownevent what_next end #get_and_reset_typed helps modules contribute #text via session[:typed] which this function resets # def get_and_reset_typed # t = session[:typed] # session[:typed]=nil # t # end #get_modules returns corr session ensuring an #item in it is 'modules' def get_modules if sm=modulecombos[session[:modules]] ma=(sm.split('+') << "modules").uniq else ma=['modules'] end h={} ma.each{|s| h[s] = translate(s)} h end def get_selected_item session[:selected_item] ||=0 end # def selector # '|' # end #start_choices are defined differently by each #module def start_choices %w{should be overridden} end #filechoices? is true for static data, the kind #that can be stored in files and locally modified def filechoices? true end #get_choices is a getter for session[:choices] #if that is nil it consults filechoices to either #pick up choices from the appropriate file or #invokes start_choices def get_choices session[:choices] ||= if filechoices? if @thiscontroller fd=MenuFolder.chop Dir.mkdir(fd) unless File.directory?(fd) fch=MenuFolder+@thiscontroller+'choices.txt' if File.exist?(fch) #logger.info "reading #{fch}" chs= IO.readlines(fch) chs.collect!{|s|s.strip} else chs=start_choices File.open(fch,"w") do |f| f.puts chs end end chs else start_choices end else start_choices end end def get_overflow session[:overflow] ||='' end #get_match returns the matching portion between #string s and regular expr rx if any def get_match (s, rx) begin s.match(rx)[0] rescue '' end end #get_text is a getter that also processes any #contributions from other modules via session[:typed] def get_text session[:text] ||= "welcome to skid" # g=get_and_reset_typed # if g # session[:text]+=" #{g} " # end # session[:text] end #what_next enables you, #after selection, to go someplace else # by setting session[:controller] def what_next(next_action='index') if session[:controller] s=session[:controller] session[:controller]=nil redirect_to( :action => "first_time", :controller => s) and return else redirect_to :action => next_action end end # selected decides what action should be taken #when str is selected -- #will always be overridden def selected(str) end # get_choices_size is used to #normalize selected_item, # so 0 is not ok def get_choices_size @choices = get_choices @choices.size ==0? 1: @choices.size end #strip_href returns the displayed portion #of a link def strip_href(str) str.match(%r{>(.+)}) ? $1.strip : str end # process_choice decides what to do when a choice #is made: splits it if it contains a separator #else calls selected def process_choice selected_choice # logger.info "selected_choice#{selected_choice}" + # "get_separator #{get_separator}" if (selected_choice.include?(get_separator) and (selected_choice.length>1)) session[:choices]=selected_choice.split(get_separator) # logger.info "separated #{session[:choices].inspect}" else str=strip_href(selected_choice) #logger.info("selected: #{str}") session[:choices]=selected(str) # logger.info "selected returned #{sel.inspect}" session[:selected_item]=0 end end #select is activated when the smiley is clicked #if the id is a _number, that choice is taken #if the id is a string, it is selected def select if params[:id] if params[:id]=~/_(\d+)/ i=$1.to_i session[:selected_item]=i # process_choice(get_choices[i]) # logger.info "sid: #{params[:id]}" else session[:choices]=selected(params[:id].gsub(/-/,' ')) # logger.info "selected choices #{session[:choices].inspect}" what_next # logger.info "show #{session[:displayed]}" return end end @choices=get_choices reformat_choices @selected_item= get_selected_item # logger.info "#{@selected_item} from choices #{ # @choices.inspect}" if !@choices.empty? selected_choice=get_highlighted_choice process_choice selected_choice else session[:choices]=selected nil end what_next end def get_displayed session[:displayed] || "" end def up_icon %s{ up } end def down_icon # "v" %s{down} end def select_icon # ">" %s{ select } end def backspace_icon # "<" %s{ something
else } end def get_title "Welcome to skid" end # extras is for module-specific additions to index def extras end # index is only defined at the application level # session garbage collection def clear_unused_in_session session[:choices]=nil session[:user_choice]=nil session[:computer_choice]=nil session[:sticks]=nil # session[:displayed]=nil session[:picked]=nil session[:toggle]=nil session[:display]=nil session[:pictures]=nil session[:wrong]=nil session[:freeze]=nil session[:answer]=nil session[:question]=nil session[:correct_answer]=nil session[:state]=nil session[:correct]=nil session[:picfolder]=nil session[:oddfolder]=nil session[:separator]=nil session[:problem]=nil session[:n1]=session[:n2]=nil # session[:nitems]=nil end def get_picfolder session[:picfolder] ||= get_folder(get_folder_index) end def get_nitems session[:nitems] ||= 4 end def get_buttons {"up" => up_icon, "down" => down_icon, "backspace" => backspace_icon, "select" => select_icon} end def get_delay session[:delay] ||= 5 end #index is where everything must end #for it is the only view def get_autorefresh session[:auto_refresh] ||= false end def increment_selected_item session[:selected_item]=get_selected_item+1 end def index # logger.info "myid #{params[:id]}" if params[:id]=~ /showsettings/ session[:hidesettings]=nil end if params[:id]=~ /hidesettings/ session[:hidesettings]=true end @showsettings=!session[:hidesettings] if params[:id]=~ /resetsession/ reset_session end if get_autorefresh increment_selected_item end @settings={} if params[:id]=~ /_refresh/ session[:selected_item] ||= 0 session[:selected_item]+=1 session[:auto_refresh]=true updownevent end if params[:id]=~ /stopscroll/ session[:auto_refresh]=false end if params[:id]=~ /nobuttons/ session[:nobuttons]=true @settings['with buttons']='buttons' end if params[:id]=~ /withbuttons/ session[:nobuttons]=nil end if params[:id]=~ /nomodules/ session[:nomodules]=true end if params[:id]=~ /withmodules/ session[:nomodules]=nil end if params[:id]=~ /delay/ delay=params[:id].sub('delay','').to_i if delay > 0 session[:delay]=delay end end if params[:id]=~ /nopics/ session[:nopics]=true end if params[:id]=~ /withpics/ session[:nopics]=nil end if params[:id]=~ /nowords/ session[:nowords]=true end if params[:id]=~ /withwords/ session[:nowords]=nil end myname=self.class.name if !session[:current_controller] || (myname!=session[:current_controller]) session[:current_controller]=myname clear_unused_in_session what_next("first_time") end @refresh= get_autorefresh @delay=get_delay @with_words = !session[:nowords] if @refresh @settings['manual scrolling']='stopscroll' else @settings['automatic scrolling']='auto_refresh' end if session[:nobuttons] @settings['with buttons']='withbuttons' else @settings['without buttons']='nobuttons' end if session[:nomodules] @settings['with modules']='withmodules' else @settings['without modules']='nomodules' end if session[:nopics] @settings['with pictures in choices']='withpics' else @settings['without pictures in choices']='nopics' end if session[:nowords] @settings['with words in choices']='withwords' else @settings['without words in choices']='nowords' end @thiscontroller=myname. gsub(/Controller/,'').downcase @modules={} if !session[:nomodules] @modules=get_modules # logger.info "modules with display names #{@modules}" #used for the module table on top end @title=get_title #the title top right if session[:nobuttons] @buttons={} else @buttons=get_buttons end @choices=get_choices || [] @selected_item=get_selected_item if !@choices.empty? reformat_choices @selected_item%=@choices.size end extras @spoken= session[:spoken] session[:spoken]=nil @choices.each_index do |i| @choices[i]=make_clickable(@choices[i],i) end @pictures=@choices.collect do|cho| get_pic_file(strip_href(cho)) end unless session[:nopics] @extracols=@modules.size if session[:nopics] then @extracols-=1 end if @extracols< 1 then @extracols=1 end @designers=designers @highlight_selected = highlight_selected_item @choicepicsize= if p=path_from_login(session[:login],'choicepicsize') p.first else "80x80" end logger.info "session at index_end: #{session.inspect}" end # if highlight_selected_item is true, a choice is displayed inverted, # which can be selected by clicking the smiley def highlight_selected_item get_autorefresh end #if the module does not define an action for #backspace, it restarts the module def backspace first_time end #first_time will often be overridden def first_time what_next end ################################################################### #translator function for english sentences to french or german # def translate(engSentence) # @lang=session[:language] ||= "English" # @search=engSentence # allsentences=Sentencecollection.find(:all) # @totranslate=allsentences.select do |x| # x[:sentence]==(@search) # end # return engSentence if @totranslate.empty? # logger.info "matchingsentence #{@totranslate.first[:sentence]}" # @recNo=@totranslate.first[:id] # logger.info "testing #{@recNo}" # @translated=allsentences.select do |y| # y[:transOfWhat]==(@recNo)&&y[:language].match(@lang) # end # logger.info "matching #{@recNo}: #{@translated}" # @check = @translated.first[:sentence] # logger.info "#{@check}" # @translated.empty? ? engSentence : @translated.first[:sentence] # end LanguagesFolder='languages/' SourceFile='source' def translate(engSentence) lang=session[:language] ||= "English" source_list=IO.readlines("#{LanguagesFolder}#{SourceFile}") # logger.info source_list.to_s if source_list && i=source_list.find_index{|x| x.strip == engSentence} then # logger.info "found.match #{i}" translated_string=IO.readlines("#{LanguagesFolder}#{lang}.txt")[i] if translated_string && !translated_string.strip.empty? return translated_string end end engSentence #rescue # engSentence end ###################################################################### def designers nil end # def get_para(lw, item_no) displayed='' if !lw.empty? begin page=Wikipedia.new.page(lw) logger.info("wikipage for #{lw}:") logger.info(page) doc =Hpricot(page) bc=doc.search('#bodyContent') ps = bc/:p rescue Exception logger.info "pageerror: #{$!}" logger.info "page #{page}" ps=[] @title='no wikipedia access' end if ps.empty? displayed='nothing found' elsif ps.size==1 paras=[ps.html] lis=bc/:li lis.each { |i| paras << i.html} item_no%=paras.size displayed = paras[item_no] else ps.reject! {|pa| pa.inner_text !~ /\w/} # @paras.delete_at(0) # logger.info('wikipedia paras'+@paras.inspect) item_no%=ps.size displayed = ps[item_no].inner_html end displayed.gsub!('/wiki/','/wikipedia/clicked/') end displayed end def teacherlog s n= Time.now logger.info "For teacher: #{s} via: #{selfname} user: #{ session[:login] || 'default'} at #{n}/#{n.to_i}" end def lognewproblem(s, answer) teacherlog "new problem #{s}, answer #{answer}" end def logcorrect(correct_answer,problem) teacherlog "correct: #{correct_answer} to: #{problem}" end def logwrong(wrong_answer,problem) teacherlog "wrong: #{wrong_answer} to: #{problem}" end def client_ip s=request.remote_ip s end def sendmail (recipient,subject= 'mail from skid',message=session[:text]) message=session[:text] unless message Sender.deliver_contact(recipient, subject, message) return if request.xhr? logger.info "Message #{message} sent successfully to #{recipient}" end end