├── log └── .keep ├── app ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── concerns │ │ └── .keep │ └── course.rb ├── assets │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── sessions │ │ │ └── new.js │ │ └── courses │ │ │ └── front.js │ └── stylesheets │ │ └── application.scss ├── controllers │ ├── concerns │ │ └── .keep │ ├── pages_controller.rb │ ├── sessions_controller.rb │ ├── application_controller.rb │ ├── courses_controller.rb │ └── update_courses_controller.rb ├── views │ ├── courses │ │ ├── remove_course.js.erb │ │ ├── search.js.erb │ │ ├── _course_table_body.html.haml │ │ ├── front.html.haml │ │ ├── _course_table.html.haml │ │ ├── _list_of_courses.html.haml │ │ ├── add_course.js.erb │ │ ├── _search_result.html.haml │ │ └── _form.html.haml │ ├── shared │ │ ├── _footer.html.haml │ │ ├── _messages.html.haml │ │ └── _header.html.haml │ ├── pages │ │ ├── landing_page.html.haml │ │ ├── advice.html.haml │ │ ├── useful_links.html.haml │ │ └── about.html.haml │ ├── update_courses │ │ └── new.html.haml │ ├── sessions │ │ └── new.html.haml │ └── layouts │ │ └── application.html.erb ├── services │ ├── course_search_service.rb │ └── course_updates.rb └── helpers │ └── application_helper.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── public ├── favicon.ico ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── test ├── helpers │ └── .keep ├── mailers │ └── .keep ├── models │ └── .keep ├── controllers │ └── .keep ├── fixtures │ └── .keep ├── integration │ └── .keep └── test_helper.rb ├── vendor └── assets │ ├── javascripts │ ├── .keep │ └── social.js │ └── stylesheets │ └── .keep ├── bin ├── bundle ├── rake ├── rails ├── spring └── setup ├── config ├── boot.rb ├── initializers │ ├── cookies_serializer.rb │ ├── session_store.rb │ ├── mime_types.rb │ ├── filter_parameter_logging.rb │ ├── backtrace_silencers.rb │ ├── assets.rb │ ├── wrap_parameters.rb │ └── inflections.rb ├── environment.rb ├── database.yml ├── puma.rb ├── locales │ └── en.yml ├── secrets.yml ├── application.rb ├── environments │ ├── development.rb │ ├── test.rb │ └── production.rb └── routes.rb ├── config.ru ├── db ├── migrate │ ├── 20150526001645_add_index_to_institute_code_of_courses.rb │ ├── 20150823032048_remove_several_tables.rb │ ├── 20150507060955_create_basic_chineses.rb │ ├── 20150507061712_create_general_educations.rb │ ├── 20150507061640_create_citizenship_histories.rb │ ├── 20150526000217_create_courses.rb │ ├── 20150507061550_create_international_languages.rb │ └── 20150621020208_add_columns_to_courses_tables.rb ├── seeds.rb └── schema.rb ├── Rakefile ├── README.md ├── .gitignore ├── Gemfile └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/courses/remove_course.js.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /app/views/shared/_footer.html.haml: -------------------------------------------------------------------------------- 1 | %footer.footer 2 | .container 3 | -#%p.text-muted Place sticky footer content here. 4 | -#= link_to "關於作者", "#" 5 | -#= link_to "關於本站", "#" 6 | 7 | 8 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_general_courses_helper_session' 4 | -------------------------------------------------------------------------------- /app/views/courses/search.js.erb: -------------------------------------------------------------------------------- 1 | <% if @search_result %> 2 | $('#search-result').html("<%= escape_javascript (render partial: 'search_result', locals: {search_result: @search_result}) %>") 3 | <% end %> 4 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | require_relative '../config/boot' 7 | require 'rake' 8 | Rake.application.run 9 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /db/migrate/20150526001645_add_index_to_institute_code_of_courses.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToInstituteCodeOfCourses < ActiveRecord::Migration 2 | def change 3 | add_index :courses, :institute_code 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/shared/_messages.html.haml: -------------------------------------------------------------------------------- 1 | - flash.each do |name, msg| 2 | %div(class="alert alert-#{name}") 3 | %a.close(data-dismiss='alert') × 4 | = content_tag :div, msg, :id => "flash_#{name}" if msg.is_a?(String) 5 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | APP_PATH = File.expand_path('../../config/application', __FILE__) 7 | require_relative '../config/boot' 8 | require 'rails/commands' 9 | -------------------------------------------------------------------------------- /app/views/pages/landing_page.html.haml: -------------------------------------------------------------------------------- 1 | .intro-header 2 | .container 3 | .row 4 | .jumbotron 5 | %h1 成大選課小幫手 6 | %p 覺得學校課程系統找課不夠方便嗎?試試看選課小幫手吧! 7 | %p 讓你輕鬆找到空堂有什麼課 8 | = link_to "開始使用", setting_path, class: "btn btn-success btn-lg" 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/views/update_courses/new.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .col-md-3 3 | = form_tag update_courses_path do 4 | .form-group 5 | = password_field_tag :password, nil, placeholder: 'password', class: 'form-control' 6 | = submit_tag '確認', class: 'btn btn-primary' 7 | -------------------------------------------------------------------------------- /db/migrate/20150823032048_remove_several_tables.rb: -------------------------------------------------------------------------------- 1 | class RemoveSeveralTables < ActiveRecord::Migration 2 | def change 3 | drop_table :general_educations 4 | drop_table :basic_chineses 5 | drop_table :international_languages 6 | drop_table :citizenship_histories 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | class PagesController < ApplicationController 2 | 3 | def landing_page 4 | redirect_to front_path if user_has_finished_necessary_settings? 5 | end 6 | 7 | def about 8 | end 9 | 10 | def useful_links 11 | end 12 | 13 | def advice 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/courses/_course_table_body.html.haml: -------------------------------------------------------------------------------- 1 | - course_table_data(courses).each do |schedule_time, row| 2 | %tr 3 | %td 4 | - row.each do |day, data| 5 | - if data != {} 6 | %td.col-md-2.info{:class => "course-table-#{data[:id]}"}= data[:course_name].html_safe 7 | - else 8 | %td.col-md-2 9 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: postgresql 3 | encoding: unicode 4 | database: general_courses_helper_dev 5 | pool: 5 6 | username: Yulin 7 | password: 8 | 9 | test: 10 | adapter: postgresql 11 | encoding: unicode 12 | database: general_courses_helper_test 13 | pool: 5 14 | username: Yulin 15 | password: 16 | 17 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | end 11 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/social.js: -------------------------------------------------------------------------------- 1 | (function(d, s, id) { 2 | var js, fjs = d.getElementsByTagName(s)[0]; 3 | if (d.getElementById(id)) return; 4 | js = d.createElement(s); js.id = id; 5 | js.src = "//connect.facebook.net/zh_TW/sdk.js#xfbml=1&version=v2.3&appId=1666360613599510"; 6 | fjs.parentNode.insertBefore(js, fjs); 7 | }(document, 'script', 'facebook-jssdk')); 8 | -------------------------------------------------------------------------------- /app/views/pages/advice.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .well 3 | %h2 建議或回報問題 4 | %p.lead 5 | 對「成大選課小幫手」有任何建議,或是發現 bug ,可以 6 | %a{:href => "https://www.facebook.com/yulin.sky.5"}直接聯繫我 7 | ,現在在當兵,我回來看到了會儘快回覆 8 | %p.lead 9 | 或者是您有能力解決 bug 或新增功能,也絕對歡迎發 pull request 給我! 10 | %h2 本站原始碼 11 | %div 12 | %p.lead 13 | %a{:href => "https://github.com/festime/NCKU-select-course-helper"} NCKU-select-course-helper 14 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require "rubygems" 8 | require "bundler" 9 | 10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m) 11 | Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq } 12 | gem "spring", match[1] 13 | require "spring/binstub" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | workers Integer(ENV['WEB_CONCURRENCY'] || 2) 2 | threads_count = Integer(ENV['MAX_THREADS'] || 5) 3 | threads threads_count, threads_count 4 | 5 | preload_app! 6 | 7 | rackup DefaultRackup 8 | port ENV['PORT'] || 3000 9 | environment ENV['RACK_ENV'] || 'development' 10 | 11 | on_worker_boot do 12 | # Worker specific setup for Rails 4.1+ 13 | # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot 14 | ActiveRecord::Base.establish_connection 15 | end 16 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /app/views/courses/front.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .row 3 | .col-md-6 4 | = render partial: "course_table", locals: {courses: @courses} 5 | .col-md-6 6 | = render partial: "list_of_courses", locals: {courses: @courses} 7 | .row 8 | = render partial: "form", locals: {institute_code: @institute_code} 9 | .row 10 | #search-result 11 | %ul.nav.pull-left.scroll-top 12 | %li 13 | %a{:href => "#"} 14 | %i.glyphicon.glyphicon-chevron-up 15 | = javascript_tag do 16 | window.obligatory_courses = #{@courses.to_json.html_safe} 17 | -------------------------------------------------------------------------------- /app/views/courses/_course_table.html.haml: -------------------------------------------------------------------------------- 1 | .table-responsive 2 | %table#course-table.table.table-bordered 3 | %thead 4 | %tr 5 | %th 6 | %th.monday.select-this-day-free-time 一 7 | %th.tuesday.select-this-day-free-time 二 8 | %th.wednesday.select-this-day-free-time 三 9 | %th.thursday.select-this-day-free-time 四 10 | %th.friday.select-this-day-free-time 五 11 | %tbody 12 | = render partial: "course_table_body", locals: {courses: courses} 13 | = link_to "恢復成預設課表", default_table_path, class: "btn btn-danger btn-lg" 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NCKU select course helper 2 | 3 | An open source website which grab courses data from NCKU official website. The site is created to help students in NCKU find courses easily. 4 | 5 | Visit [NCKU select course helper](https://ncku-select-course-helper.herokuapp.com) 6 | 7 | ## Main Features 8 | * Intuitive user interface 9 | * Better filter function to find courses 10 | 11 | ## Issues 12 | * The app can't automatically update space-available information of courses regularly. 13 | * The courses data need update manually. 14 | 15 | ## Version 16 | * Rails 4.2.0 17 | * Ruby 2.1.5 18 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .col-md-3 3 | = form_tag setting_path do 4 | .form-group 5 | = label_tag :institute_code, "系" 6 | = select_tag(:institute_code, grouped_options_for_select(options_of_departments), class: "form-control") 7 | .form-group 8 | = label_tag :grade, "年級" 9 | = select_tag(:grade, options_for_select((1..4).map {|number| [number.to_s, number]}), class: "form-control") 10 | .form-group.two-classes-options 11 | .form-group.three-classes-options 12 | = submit_tag "確認", class: "btn btn-primary btn-lg" 13 | -------------------------------------------------------------------------------- /db/migrate/20150507060955_create_basic_chineses.rb: -------------------------------------------------------------------------------- 1 | class CreateBasicChineses < ActiveRecord::Migration 2 | def change 3 | create_table :basic_chineses do |t| 4 | t.string :institute_code 5 | t.string :serial_number 6 | t.string :year 7 | t.string :category 8 | t.boolean :taught_in_english 9 | t.string :course_name 10 | t.integer :credits 11 | t.string :instructor 12 | t.string :selected 13 | t.string :space_available 14 | t.string :schedule 15 | t.string :classroom 16 | t.string :remark 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /db/migrate/20150507061712_create_general_educations.rb: -------------------------------------------------------------------------------- 1 | class CreateGeneralEducations < ActiveRecord::Migration 2 | def change 3 | create_table :general_educations do |t| 4 | t.string :institute_code 5 | t.string :serial_number 6 | t.string :year 7 | t.string :category 8 | t.boolean :taught_in_english 9 | t.string :course_name 10 | t.integer :credits 11 | t.string :instructor 12 | t.string :selected 13 | t.string :space_available 14 | t.string :schedule 15 | t.string :classroom 16 | t.string :remark 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/views/pages/useful_links.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .well 3 | %div 4 | %p.lead 5 | 這裡列出一些對選課有用的連結,方便大家使用 6 | %ul 7 | %li 8 | %a{:href => 'http://course.ncku.edu.tw/course/'} 成大選課系統 9 | %li 10 | %a{:href => 'http://class-qry.acad.ncku.edu.tw/qry/'} 成大課程查詢系統 11 | %li 12 | %a{:href => 'http://cge.ncku.edu.tw/files/11-1024-13439.php'} 成大各系通識規定 13 | %li 14 | %a{:href => 'http://140.116.165.64/grachk/index.php'} 成大畢業預審系統 15 | %li 16 | %a{:href => 'https://www.facebook.com/groups/637099219647956/'} 成大選課懂ㄘ 懂ㄘ 社團 17 | -------------------------------------------------------------------------------- /db/migrate/20150507061640_create_citizenship_histories.rb: -------------------------------------------------------------------------------- 1 | class CreateCitizenshipHistories < ActiveRecord::Migration 2 | def change 3 | create_table :citizenship_histories do |t| 4 | t.string :institute_code 5 | t.string :serial_number 6 | t.string :year 7 | t.string :category 8 | t.boolean :taught_in_english 9 | t.string :course_name 10 | t.integer :credits 11 | t.string :instructor 12 | t.string :selected 13 | t.string :space_available 14 | t.string :schedule 15 | t.string :classroom 16 | t.string :remark 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | !/log/.keep 17 | /tmp 18 | 19 | # Ignore application configuration 20 | /config/application.yml 21 | -------------------------------------------------------------------------------- /db/migrate/20150526000217_create_courses.rb: -------------------------------------------------------------------------------- 1 | class CreateCourses < ActiveRecord::Migration 2 | def change 3 | create_table :courses do |t| 4 | t.string :institute_code 5 | t.string :serial_number 6 | t.string :class_name 7 | t.string :year 8 | t.string :category 9 | t.boolean :taught_in_english 10 | t.string :course_name 11 | t.string :elective_or_required 12 | t.integer :credits 13 | t.string :instructor 14 | t.string :space_available 15 | t.string :schedule 16 | t.string :classroom 17 | t.string :remark 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20150507061550_create_international_languages.rb: -------------------------------------------------------------------------------- 1 | class CreateInternationalLanguages < ActiveRecord::Migration 2 | def change 3 | create_table :international_languages do |t| 4 | t.string :institute_code 5 | t.string :serial_number 6 | t.string :year 7 | t.string :category 8 | t.boolean :taught_in_english 9 | t.string :course_name 10 | t.integer :credits 11 | t.string :instructor 12 | t.string :selected 13 | t.string :space_available 14 | t.string :schedule 15 | t.string :classroom 16 | t.string :remark 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /db/migrate/20150621020208_add_columns_to_courses_tables.rb: -------------------------------------------------------------------------------- 1 | class AddColumnsToCoursesTables < ActiveRecord::Migration 2 | def change 3 | add_column :basic_chineses, :class_name, :string 4 | add_column :basic_chineses, :elective_or_required, :string 5 | add_column :international_languages, :class_name, :string 6 | add_column :international_languages, :elective_or_required, :string 7 | add_column :general_educations, :class_name, :string 8 | add_column :general_educations, :elective_or_required, :string 9 | add_column :citizenship_histories, :class_name, :string 10 | add_column :citizenship_histories, :elective_or_required, :string 11 | 12 | add_column :courses, :selected, :string 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require bootstrap-sprockets 15 | //= require jquery_ujs 16 | //= require turbolinks 17 | //= require_tree . 18 | //= require social.js 19 | -------------------------------------------------------------------------------- /app/views/courses/_list_of_courses.html.haml: -------------------------------------------------------------------------------- 1 | .form-group 2 | %h3= "目前修課" 3 | %table#course-list.table.table-striped 4 | %thead 5 | %tr 6 | %th 7 | %th 課名 8 | %th 學分 9 | %th 教師姓名 10 | %th 上課時間 11 | %th 上課地點 12 | %th 備註 13 | %tbody 14 | - courses.each do |course| 15 | %tr{:id => "list-#{course.id}"} 16 | %td 17 | = link_to '', remove_course_path(course_id: course.id), class: 'glyphicon glyphicon-remove', remote: true, method: :delete 18 | %td.col-md-2= course.course_name.html_safe 19 | %td.col-md-1= course.credits 20 | %td.col-md-2= course.instructor 21 | %td.col-md-2= better_schedule_text(course.schedule) 22 | %td.col-md-2= course.classroom 23 | %td.col-md-3= course.remark 24 | 25 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /app/views/courses/add_course.js.erb: -------------------------------------------------------------------------------- 1 | <% if @new_course %> 2 | var new_course = '' + 3 | ' <%= link_to '', remove_course_path(course_id: @new_course.id), class: 'glyphicon glyphicon-remove', remote: true, method: :delete %> ' + 4 | ' <%= @new_course.course_name.html_safe %> ' + 5 | ' <%= @new_course.credits %> ' + 6 | ' <%= @new_course.instructor %> ' + 7 | ' <%= @new_course.schedule %> ' + 8 | ' <%= @new_course.classroom %> ' + 9 | ' <%= @new_course.remark %> ' + 10 | '' 11 | 12 | $('#course-table tbody').html("<%= escape_javascript (render partial: 'course_table_body', locals: {courses: @courses}) %>") 13 | $('#course-list tbody').append(new_course) 14 | <% else %> 15 | alert('這個時段已經有課囉'); 16 | <% end %> 17 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 0473c500a9b97156a5213b9b865a79d4380214fc179823869ac459a63ea96f16d6b17edb39cd7268ff71f7de2079a6eb4bc309d201437610fca90010f646998e 15 | 16 | test: 17 | secret_key_base: 9bb92b88c3207a6fecd7319096c837f44cee890acadad20121ff80d8f40418dfcde73ec2647be591718476bd0a7c1bd0f61b29a816fd40750843b7654207c0ff 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /app/views/shared/_header.html.haml: -------------------------------------------------------------------------------- 1 | .navbar.navbar-default{:role => "navigation"} 2 | .container 3 | .navbar-header 4 | %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", :type => "button"} 5 | %span.sr-only Toggle navigation 6 | %span.icon-bar 7 | %span.icon-bar 8 | %span.icon-bar 9 | = link_to "成大選課小幫手", root_path, class: "navbar-brand" 10 | .navbar-collapse.collapse 11 | %ul.nav.navbar-nav.navbar-left 12 | %li 13 | = link_to "建議", advice_path 14 | %li 15 | = link_to "關於", about_path 16 | %li 17 | = link_to "有用連結", useful_links_path 18 | - if user_has_finished_necessary_settings? 19 | %li 20 | = link_to "清除系級設定", clear_setting_path, method: :delete 21 | %ul.nav.navbar-nav.navbar-right 22 | .fb-like{"data-action" => "like", "data-href" => "https://ncku-select-course-helper.herokuapp.com/", "data-layout" => "button_count", "data-share" => "true", "data-show-faces" => "true"} 23 | / /.nav-collapse 24 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 成大選課小幫手 5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 7 | <%= csrf_meta_tags %> 8 | 9 | 10 |
11 | 12 | 13 | 15 | 20 | 21 | 22 | <%= render 'shared/header' %> 23 | <%= render 'shared/messages' %> 24 | <%= yield %> 25 | <%= render 'shared/footer' %> 26 | 27 | 28 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module GeneralCoursesHelper 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | # config.time_zone = 'Central Time (US & Canada)' 18 | 19 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 20 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 21 | # config.i18n.default_locale = :de 22 | 23 | # Do not swallow errors in after_commit/after_rollback callbacks. 24 | config.active_record.raise_in_transactional_callbacks = true 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | // "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables" 2 | @import "bootstrap-sprockets"; 3 | @import "bootstrap"; 4 | 5 | .navbar { 6 | margin-bottom: 15px; 7 | } 8 | 9 | .footer { 10 | background-color: #f5f5f5; 11 | height: 45px; 12 | position: absolute; 13 | width: 100%; 14 | } 15 | 16 | .row { 17 | padding-top: 10px; 18 | padding-bottom: 10px; 19 | } 20 | 21 | .scroll-top { 22 | position: fixed; 23 | bottom: 50%; 24 | left: 0%; 25 | z-index: 100; 26 | font-size: 16px; 27 | border-top-left-radius: 3px; 28 | border-top-right-radius: 3px; 29 | /*background: #ffcc33;*/ 30 | } 31 | 32 | .glyphicon-remove { 33 | color: red; 34 | } 35 | 36 | .glyphicon-plus { 37 | color: #337ab7; 38 | } 39 | 40 | .glyphicon-collapse-up { 41 | font-size: 20px; 42 | height: 25px; 43 | color: #337ab7; 44 | } 45 | 46 | .glyphicon-collapse-down { 47 | font-size: 20px; 48 | height: 25px; 49 | color: #337ab7; 50 | } 51 | 52 | .course-type { 53 | display: flex; 54 | 55 | h3 { 56 | margin-top: 0px; 57 | margin-bottom: 10px; 58 | } 59 | } 60 | 61 | .footer { 62 | margin-top: 20px; 63 | background-color: white; 64 | } 65 | 66 | .fb-like { 67 | margin-top: 14px; 68 | } 69 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | 9 | departments = [ 10 | '學士學程', 11 | '中文系', 12 | '外文系', 13 | '歷史系', 14 | '台文系', 15 | '數學系', 16 | '物理系', 17 | '化學系', 18 | '地科系', 19 | '光電系', 20 | '機械系', 21 | '化工系', 22 | '資源系', 23 | '材料系', 24 | '土木系', 25 | '水利系', 26 | '工科系', 27 | '能源學程', 28 | '系統系', 29 | '航太系', 30 | '環工系', 31 | '測量系', 32 | '醫工系', 33 | '會計系', 34 | '統計系', 35 | '工資系', 36 | '企管系', 37 | '交管系', 38 | '護理系', 39 | '醫技系', 40 | '醫學系', 41 | '物治系', 42 | '職治系', 43 | '藥學系', 44 | '法律系', 45 | '政治系', 46 | '經濟系', 47 | '心理系', 48 | '電機系', 49 | 'CSIE', 50 | '建築系', 51 | '都計系', 52 | '工設系', 53 | '生科系' 54 | ] 55 | 56 | general_education = [ 57 | '外國語言', 58 | '基礎國文', 59 | '通識中心', 60 | '公民歷史' 61 | ] 62 | 63 | CourseUpdates.new.execute(departments[0..13]) 64 | CourseUpdates.new.execute(departments[14..27]) 65 | CourseUpdates.new.execute(departments[28..-1]) 66 | CourseUpdates.new.execute(general_education) 67 | -------------------------------------------------------------------------------- /app/views/pages/about.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .well 3 | %h2 關於本站 4 | %div 5 | %p.lead 6 | 「成大選課小幫手」嘗試解決的是,每次新學期要選課時, 7 | 用學校的課程查詢系統找課、排課,對某些人(或者至少對我) 8 | 來說是個有點麻煩的過程,只不過想找自己想修的課而已, 9 | 卻得在好幾個分頁之間跳來跳去、用肉眼過濾資訊, 10 | 實在是有點費事... 11 | %p.lead 12 | 我大一大二的時候只有想法,沒有技術,只能紙上談兵, 13 | 最近一年多到資訊系修課、自學 Rails ,終於可以實際動手解決這個我耿耿於懷已久的問題 14 | %p.lead 15 | 但是,懂技術之後發現,因為我並不是學校負責選課和課程網站的人員, 16 | 我沒有取得同學修課歷史資料的權限,所以無法達成我心目中最理想的解決方案 17 | %p.lead 18 | 理想上,如果我知道一個同學過去修了哪些課、還有他的系的畢業規定, 19 | 那我就能在網站上直接列出他還要修哪些通識課、多少選修課,以達成畢業要求 20 | %p.lead 21 | 理想方案雖然無法實現,但找課的過程依然可以改善,學校課程查詢系統的篩選功能, 22 | 有改進的空間,所以這個網站就誕生了 23 | %h2 本站原始碼 24 | %div 25 | %p.lead 26 | %a{:href => "https://github.com/festime/NCKU-select-course-helper"} NCKU-select-course-helper 27 | %h2 作者 28 | %p.lead 29 | 我是 Yulin Chen ,統計 104 級,因為對本科系沒興趣,後來就開始到資訊系修課、學程式設計, 30 | 起初什麼都不懂,過了一陣子認知到程式和電腦科學非常廣,思考自己想走什麼路, 31 | 想到之前選課時對學校的課程查詢網站一直覺得 OOXX ,心想何不嘗試改善它呢? 32 | 這問題沒想像中容易,至少不是一個外行人摸個幾天就能做出來的, 33 | 所以最近半年多我主要聚焦在學習 web programming 和 Rails , 34 | 成大選課小幫手算是到目前為止的一個學習成果吧 35 | %h3 我的 github 和部落格 36 | %p.lead 37 | %a{:href => 'https://github.com/festime'} 我的 github 38 | , 39 | %a{:href => 'http://yulin-learn-web-dev.logdown.com'} 我的技術部落格 40 | ,歡迎互相交流 41 | -------------------------------------------------------------------------------- /app/assets/javascripts/sessions/new.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('select#institute_code').on("change", function() { 3 | var department = $(this).find(":selected").text(); 4 | 5 | if (department === "機械系" || department === "化工系" || 6 | department === "電機系") { 7 | $('.two-classes-options').children('label').remove(); 8 | $('.three-classes-options').children('label').remove(); 9 | $('#class_name').remove(); 10 | $('.three-classes-options').append(''); 18 | 19 | } else { 20 | $('.two-classes-options').children('label').remove(); 21 | $('.three-classes-options').children('label').remove(); 22 | $('#class_name').remove(); 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /app/models/course.rb: -------------------------------------------------------------------------------- 1 | class Course < ActiveRecord::Base 2 | validates_presence_of :course_name, :institute_code, 3 | :elective_or_required, :credits, 4 | :selected, :space_available, 5 | :schedule 6 | 7 | def well_formatted_schedule 8 | result = {"1" => [], "2" => [], "3" => [], "4" => [], "5" => []} 9 | schedule_clone = schedule.clone 10 | 11 | begin 12 | loop do 13 | continuous_courses = schedule_clone[/\[\d\]\d~\d/] 14 | 15 | if continuous_courses 16 | # "[1]2~3" 17 | result[continuous_courses[1]] += (continuous_courses[3]..continuous_courses[5]).to_a 18 | schedule_clone.sub!(/\[\d\]\d~\d/, "") 19 | else 20 | break 21 | end 22 | end 23 | 24 | loop do 25 | single_course = schedule_clone[/\[\d\](\d|N)/] 26 | 27 | if single_course 28 | # "[2]5" 29 | result[single_course[1]] += [single_course[3]] 30 | schedule_clone.sub!(/\[\d\](\d|N)/, "") 31 | else 32 | break 33 | end 34 | end 35 | 36 | result.each do |day, schedule| 37 | schedule.delete_if do |time| 38 | !(['1', '2', '3', '4', 'N', '5', '6', '7', '8', '9'].include? time) 39 | end 40 | end 41 | rescue => e 42 | puts e.message 43 | end 44 | 45 | result.select {|day, free_time| free_time != []} 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | before_action :reject_user_has_set_personal_information, only: [:new, :create] 3 | 4 | def new 5 | end 6 | 7 | def create 8 | courses_id = Course.where( 9 | "institute_code LIKE ? AND 10 | year LIKE ? AND 11 | elective_or_required LIKE ? AND 12 | class_name LIKE ?", 13 | params[:institute_code], 14 | params[:grade], 15 | "%必%", 16 | "%#{params[:class_name]}%" 17 | ).collect do |course| 18 | course.id 19 | end 20 | 21 | courses_id.delete_if do |course_id| 22 | course_name = Nokogiri::HTML(Course.find(course_id).course_name).text 23 | 24 | course_name =~ /通識課程|英文(含口語訓練)|哲學與藝術/ || 25 | course_name =~ /基礎國文(一)|基礎國文(二)/ || 26 | course_name == "歷史" || 27 | course_name == "公民" 28 | end 29 | 30 | session[:institute_code] = params[:institute_code] 31 | session[:courses_id] = courses_id 32 | session[:obligatory_courses_id] = session[:courses_id].clone 33 | 34 | flash[:warning] = "課程人數餘額的資料可能不是最即時的" 35 | 36 | redirect_to front_path 37 | end 38 | 39 | def destroy 40 | session[:institute_code] = session[:courses_id] = session[:obligatory_courses_id] = nil 41 | redirect_to setting_path 42 | end 43 | 44 | private 45 | 46 | def reject_user_has_set_personal_information 47 | redirect_to front_path if user_has_finished_necessary_settings? 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20150823032048) do 15 | 16 | # These are extensions that must be enabled in order to support this database 17 | enable_extension "plpgsql" 18 | 19 | create_table "courses", force: :cascade do |t| 20 | t.string "institute_code" 21 | t.string "serial_number" 22 | t.string "class_name" 23 | t.string "year" 24 | t.string "category" 25 | t.boolean "taught_in_english" 26 | t.string "course_name" 27 | t.string "elective_or_required" 28 | t.integer "credits" 29 | t.string "instructor" 30 | t.string "space_available" 31 | t.string "schedule" 32 | t.string "classroom" 33 | t.string "remark" 34 | t.string "selected" 35 | end 36 | 37 | add_index "courses", ["institute_code"], name: "index_courses_on_institute_code", using: :btree 38 | 39 | end 40 | -------------------------------------------------------------------------------- /app/views/courses/_search_result.html.haml: -------------------------------------------------------------------------------- 1 | - if search_result 2 | - search_result.each do |type, array_of_courses| 3 | - if array_of_courses.present? 4 | .course-type 5 | %h3= type 6 | %i.glyphicon.glyphicon-collapse-up 7 | .table-responsive 8 | %table.table.table-bordered.table-striped 9 | %thead 10 | %tr 11 | %th 12 | %th 系號 13 | %th 序號 14 | %th 班別 15 | %th 年級 16 | %th 類別 17 | %th 英語授課 18 | %th 課程名稱 19 | %th 必選修 20 | %th 學分 21 | %th 教師姓名 22 | %th 已選課人數 23 | %th 餘額 24 | %th 時間 25 | %th 教室 26 | %th 備註 27 | %tbody 28 | - array_of_courses.each do |course| 29 | %tr 30 | - course_column_names.each do |column_name| 31 | - if column_name != "id" 32 | - if column_name == "icon" 33 | %td 34 | = link_to '', add_course_path(course_id: course.id), class: 'glyphicon glyphicon-plus', remote: true, method: :post 35 | - elsif column_name == "course_name" 36 | %td= course[column_name].html_safe 37 | - elsif column_name == "taught_in_english" 38 | %td= (course[column_name] ? "是" : "否") 39 | - elsif column_name == "schedule" 40 | %td= better_schedule_text(course[column_name]) 41 | - else 42 | %td= course[column_name] 43 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations. 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 31 | # yet still be able to expire them through the digest params. 32 | config.assets.digest = true 33 | 34 | # Adds additional error checking when serving assets at runtime. 35 | # Checks for improperly declared sprockets dependencies. 36 | # Raises helpful error messages. 37 | config.assets.raise_runtime_errors = true 38 | 39 | # Raises error for missing translations 40 | # config.action_view.raise_on_missing_translations = true 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | 6 | helper_method :user_has_finished_necessary_settings? 7 | 8 | def user_has_finished_necessary_settings? 9 | session[:institute_code] && 10 | session[:courses_id] && 11 | session[:obligatory_courses_id] 12 | end 13 | 14 | def department_name_of(institute_code) 15 | department_name_to_institute_code.invert[institute_code] 16 | end 17 | 18 | def department_name_to_institute_code 19 | { 20 | '中文系' => 'B1', 21 | '外文系' => 'B2', 22 | '歷史系' => 'B3', 23 | '台文系' => 'B5', 24 | '數學系' => 'C1', 25 | '物理系' => 'C2', 26 | '化學系' => 'C3', 27 | '地科系' => 'C4', 28 | '光電系' => 'F8', 29 | '機械系' => 'E1', 30 | '化工系' => 'E3', 31 | '資源系' => 'E4', 32 | '材料系' => 'E5', 33 | '土木系' => 'E6', 34 | '水利系' => 'E8', 35 | '工科系' => 'E9', 36 | '能源學程' => 'F0', 37 | '系統系' => 'F1', 38 | '航太系' => 'F4', 39 | '環工系' => 'F5', 40 | '測量系' => 'F6', 41 | '醫工系' => 'F9', 42 | '會計系' => 'H1', 43 | '統計系' => 'H2', 44 | '工資系' => 'H3', 45 | '企管系' => 'H4', 46 | '交管系' => 'H5', 47 | '護理系' => 'I2', 48 | '醫技系' => 'I3', 49 | '醫學系' => 'I5', 50 | '物治系' => 'I6', 51 | '職治系' => 'I7', 52 | '藥學系' => 'I8', 53 | '法律系' => 'D2', 54 | '政治系' => 'D4', 55 | '經濟系' => 'D5', 56 | '心理系' => 'D8', 57 | '電機系' => 'E2', 58 | '資訊系' => 'F7', 59 | '建築系' => 'E7', 60 | '都計系' => 'F2', 61 | '工設系' => 'F3', 62 | '生科系' => 'C5' 63 | } 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | 4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 5 | gem 'rails', '4.2.0' 6 | # Use SCSS for stylesheets 7 | gem 'sass-rails', '~> 5.0' 8 | # Use Uglifier as compressor for JavaScript assets 9 | gem 'uglifier', '>= 1.3.0' 10 | # Use CoffeeScript for .coffee assets and views 11 | gem 'coffee-rails', '~> 4.1.0' 12 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 13 | # gem 'therubyracer', platforms: :ruby 14 | 15 | # Use jquery as the JavaScript library 16 | gem 'jquery-rails' 17 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks 18 | gem 'turbolinks' 19 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 20 | gem 'jbuilder', '~> 2.0' 21 | # bundle exec rake doc:rails generates the API under doc/api. 22 | gem 'sdoc', '~> 0.4.0', group: :doc 23 | 24 | # Use ActiveModel has_secure_password 25 | # gem 'bcrypt', '~> 3.1.7' 26 | 27 | # Use Unicorn as the app server 28 | # gem 'unicorn' 29 | 30 | # Use Capistrano for deployment 31 | # gem 'capistrano-rails', group: :development 32 | 33 | gem 'pg' 34 | gem 'bcrypt' 35 | gem 'puma' 36 | gem 'haml-rails' 37 | gem 'bootstrap-sass', '~> 3.3.4' 38 | gem 'mechanize' 39 | gem 'figaro' 40 | 41 | group :development, :test do 42 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 43 | gem 'byebug' 44 | 45 | # Access an IRB console on exception pages or by using <%= console %> in views 46 | gem 'web-console', '~> 2.0' 47 | 48 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 49 | gem 'spring' 50 | gem 'pry' 51 | end 52 | 53 | group :development do 54 | gem 'better_errors' 55 | end 56 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/courses_controller.rb: -------------------------------------------------------------------------------- 1 | class CoursesController < ApplicationController 2 | before_action :require_setting 3 | before_action :initialize_courses, only: [:front, :search] 4 | before_action :set_institute_code, only: [:front, :search] 5 | 6 | 7 | 8 | def front 9 | end 10 | 11 | def search 12 | respond_to do |format| 13 | format.js do 14 | if params[:checkboxes] 15 | search_keywords = params[:checkboxes] 16 | elsif params[:other_department] 17 | search_keywords = [ 18 | "#{department_name_of(params[:other_department][:institute_code])} " + 19 | "#{params[:other_department][:institute_code]} " + 20 | "year=#{params[:other_department][:year]}" 21 | ] 22 | end 23 | 24 | @search_result = CourseSearchService.new( 25 | search_keywords: search_keywords, 26 | courses_id: session[:courses_id] 27 | ).search 28 | end 29 | end 30 | end 31 | 32 | def add_course 33 | respond_to do |format| 34 | format.js do 35 | @new_course = Course.find(params[:course_id]) 36 | 37 | if !(session[:courses_id].include? params[:course_id].to_i) && 38 | CourseSearchService.new(courses_id: session[:courses_id]).available_courses([@new_course]) == [@new_course] 39 | session[:courses_id] << params[:course_id].to_i 40 | @courses = Course.find(session[:courses_id]) 41 | else 42 | @new_course = nil 43 | end 44 | end 45 | end 46 | end 47 | 48 | def remove_course 49 | respond_to do |format| 50 | format.js do 51 | if session[:courses_id].include? params[:course_id].to_i 52 | session[:courses_id].delete(params[:course_id].to_i) 53 | end 54 | end 55 | end 56 | end 57 | 58 | def default_table 59 | session[:courses_id] = session[:obligatory_courses_id].clone 60 | redirect_to front_path 61 | end 62 | 63 | 64 | 65 | private 66 | 67 | def require_setting 68 | unless user_has_finished_necessary_settings? 69 | redirect_to setting_path 70 | end 71 | end 72 | 73 | def initialize_courses 74 | @courses = Course.find(session[:courses_id]) 75 | end 76 | 77 | def set_institute_code 78 | @institute_code = session[:institute_code] 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'pages#landing_page' 3 | 4 | get 'about', to: 'pages#about' 5 | get 'advice', to: 'pages#advice' 6 | get 'useful_links', to: 'pages#useful_links' 7 | 8 | get 'front', to: 'courses#front' 9 | post 'search', to: 'courses#search' 10 | post 'add_course', to: 'courses#add_course' 11 | delete 'remove_course', to: 'courses#remove_course' 12 | get 'default_table', to: 'courses#default_table' 13 | 14 | get 'setting', to: 'sessions#new' 15 | post 'setting', to: 'sessions#create' 16 | delete 'clear_setting', to: 'sessions#destroy' 17 | 18 | get 'update_courses', to: 'update_courses#new' 19 | post 'update_courses', to: 'update_courses#create' 20 | 21 | # The priority is based upon order of creation: first created -> highest priority. 22 | # See how all your routes lay out with "rake routes". 23 | 24 | # You can have the root of your site routed with "root" 25 | # root 'welcome#index' 26 | 27 | # Example of regular route: 28 | # get 'products/:id' => 'catalog#view' 29 | 30 | # Example of named route that can be invoked with purchase_url(id: product.id) 31 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase 32 | 33 | # Example resource route (maps HTTP verbs to controller actions automatically): 34 | # resources :products 35 | 36 | # Example resource route with options: 37 | # resources :products do 38 | # member do 39 | # get 'short' 40 | # post 'toggle' 41 | # end 42 | # 43 | # collection do 44 | # get 'sold' 45 | # end 46 | # end 47 | 48 | # Example resource route with sub-resources: 49 | # resources :products do 50 | # resources :comments, :sales 51 | # resource :seller 52 | # end 53 | 54 | # Example resource route with more complex sub-resources: 55 | # resources :products do 56 | # resources :comments 57 | # resources :sales do 58 | # get 'recent', on: :collection 59 | # end 60 | # end 61 | 62 | # Example resource route with concerns: 63 | # concern :toggleable do 64 | # post 'toggle' 65 | # end 66 | # resources :posts, concerns: :toggleable 67 | # resources :photos, concerns: :toggleable 68 | 69 | # Example resource route within a namespace: 70 | # namespace :admin do 71 | # # Directs /admin/products/* to Admin::ProductsController 72 | # # (app/controllers/admin/products_controller.rb) 73 | # resources :products 74 | # end 75 | end 76 | -------------------------------------------------------------------------------- /app/services/course_search_service.rb: -------------------------------------------------------------------------------- 1 | class CourseSearchService 2 | 3 | def initialize(search_keywords: [], courses_id:) 4 | @search_keywords = search_keywords 5 | @courses_id = courses_id 6 | end 7 | 8 | def search 9 | search_result = {} 10 | 11 | @search_keywords.each do |keyword| 12 | category_name, institute_code, condition_string = keyword.split(' ') 13 | 14 | first_argument = "institute_code LIKE ?" 15 | remaining_arguments = [institute_code] 16 | 17 | if condition_string 18 | conditions = condition_string.split('&&') 19 | 20 | while conditions.count > 0 21 | first_argument += " AND #{conditions[0].split('=').first} LIKE ?" 22 | remaining_arguments << conditions[0].split('=').last 23 | conditions.shift 24 | end 25 | end 26 | 27 | desired_courses = Course.where(first_argument, *remaining_arguments) 28 | search_result[category_name] = available_courses(desired_courses) 29 | end 30 | 31 | search_result 32 | end 33 | 34 | def available_courses(desired_courses) 35 | user_freetime = current_user_freetime 36 | 37 | desired_courses.select do |course| 38 | compatible = true 39 | 40 | #begin 41 | course.well_formatted_schedule.each do |day, day_schedule| 42 | if day_schedule - user_freetime[day] != [] 43 | compatible = false 44 | break 45 | end 46 | end 47 | #rescue => e 48 | #p e.message 49 | #p e.backtrace 50 | #end 51 | 52 | compatible 53 | end 54 | end 55 | 56 | private 57 | 58 | def current_user_freetime 59 | user_freetime = { 60 | '1' => ['1', '2', '3', '4', 'N', '5', '6', '7', '8', '9'], 61 | '2' => ['1', '2', '3', '4', 'N', '5', '6', '7', '8', '9'], 62 | '3' => ['1', '2', '3', '4', 'N', '5', '6', '7', '8', '9'], 63 | '4' => ['1', '2', '3', '4', 'N', '5', '6', '7', '8', '9'], 64 | '5' => ['1', '2', '3', '4', 'N', '5', '6', '7', '8', '9'] 65 | } 66 | 67 | current_courses_schedule.each do |day, course_schedule| 68 | user_freetime[day] -= course_schedule 69 | end 70 | 71 | user_freetime 72 | end 73 | 74 | def current_courses_schedule 75 | courses_schedule = Course.find(@courses_id).collect do |course| 76 | course.well_formatted_schedule 77 | end 78 | 79 | courses_schedule.inject do |result, schedule| 80 | result.merge(schedule) {|day, result_schedule, new_schedule| result_schedule | new_schedule} 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /app/views/courses/_form.html.haml: -------------------------------------------------------------------------------- 1 | .col-md-6 2 | = form_tag search_path, remote: true do 3 | .form-group 4 | %h3 系上專業選修課 5 | .checkbox 6 | %label 7 | = check_box_tag('checkboxes[]', "大一選修 #{institute_code} year=1&&elective_or_required=選修") 8 | 大一 9 | %label 10 | = check_box_tag('checkboxes[]', "大二選修 #{institute_code} year=2&&elective_or_required=選修") 11 | 大二 12 | %label 13 | = check_box_tag('checkboxes[]', "大三選修 #{institute_code} year=3&&elective_or_required=選修") 14 | 大三 15 | %label 16 | = check_box_tag('checkboxes[]', "大四選修 #{institute_code} year=4&&elective_or_required=選修") 17 | 大四 18 | .form-group 19 | %h3 核心通識 20 | .checkbox 21 | %label 22 | = check_box_tag('checkboxes[]', '基礎國文 A7') 23 | 基礎國文 24 | %label 25 | = check_box_tag('checkboxes[]', '大一英文 A1 year=1') 26 | 大一英文 27 | %label 28 | = check_box_tag('checkboxes[]', '公民與歷史 AG') 29 | 公民與歷史 30 | %label 31 | = check_box_tag('checkboxes[]', '哲學與藝術 A9 category=哲學與藝術') 32 | 哲學與藝術 33 | .form-group 34 | %h3 跨領域通識 35 | .checkbox 36 | %label 37 | = check_box_tag('checkboxes[]', '科際整合 A9 category=科際整合') 38 | 科際整合 39 | %label 40 | = check_box_tag('checkboxes[]', '自然與工程科學 A9 category=自然與工程科學') 41 | 自然與工程科學 42 | %label 43 | = check_box_tag('checkboxes[]', '生命科學與健康 A9 category=生命科學與健康') 44 | 生命科學與健康 45 | %label 46 | = check_box_tag('checkboxes[]', '人文學 A9 category=人文學') 47 | 人文學 48 | %label 49 | = check_box_tag('checkboxes[]', '社會科學 A9 category=社會科學') 50 | 社會科學 51 | .form-group 52 | %input{:type => "hidden", :name => "freetime[1]", :id => "monday"} 53 | %input{:type => "hidden", :name => "freetime[2]", :id => "tuesday"} 54 | %input{:type => "hidden", :name => "freetime[3]", :id => "wednesday"} 55 | %input{:type => "hidden", :name => "freetime[4]", :id => "thursday"} 56 | %input{:type => "hidden", :name => "freetime[5]", :id => "friday"} 57 | = submit_tag "找課", class: "btn btn-success btn-lg" 58 | = link_to "清空搜尋結果", front_path, class: "btn btn-warning btn-lg" 59 | .col-md-6 60 | = form_tag search_path, remote: true do 61 | .form-group 62 | %h3 外系課程 63 | = label_tag "other_department[institute_code]", "系" 64 | = select_tag("other_department[institute_code]", grouped_options_for_select(options_of_departments), class: "form-control") 65 | = label_tag "other_department[year]", "年級" 66 | = select_tag("other_department[year]", options_for_select((1..4).map {|number| [number.to_s, number]}), class: "form-control") 67 | = submit_tag "找課", class: "btn btn-success btn-lg" 68 | = link_to "清空搜尋結果", front_path, class: "btn btn-warning btn-lg" 69 | 70 | -------------------------------------------------------------------------------- /app/controllers/update_courses_controller.rb: -------------------------------------------------------------------------------- 1 | class UpdateCoursesController < ApplicationController 2 | def new 3 | end 4 | 5 | def create 6 | if params[:password] == ENV["password"] 7 | CourseUpdate.new.update_general_education_courses 8 | flash[:success] = "通識類課程更新成功!" 9 | redirect_to update_courses_path 10 | 11 | elsif params[:password] == ENV["front_departments"] 12 | departments = [ 13 | '中文系', 14 | '外文系', 15 | '歷史系', 16 | '台文系', 17 | '數學系', 18 | '物理系', 19 | '化學系', 20 | '地科系', 21 | '光電系', 22 | '機械系', 23 | '化工系', 24 | '資源系', 25 | '材料系', 26 | '土木系' 27 | ] 28 | CourseUpdate.new.update_courses_of_departments(departments) 29 | flash[:success] = "前半系課程更新成功!" 30 | redirect_to update_courses_path 31 | 32 | elsif params[:password] == ENV["middle_departments"] 33 | departments = [ 34 | '水利系', 35 | '工科系', 36 | '系統系', 37 | '航太系', 38 | '環工系', 39 | '測量系', 40 | '醫工系', 41 | '會計系', 42 | '統計系', 43 | '工資系', 44 | '企管系', 45 | '交管系', 46 | '護理系', 47 | '醫技系' 48 | ] 49 | CourseUpdate.new.update_courses_of_departments(departments) 50 | flash[:success] = "中間系課程更新成功!" 51 | redirect_to update_courses_path 52 | 53 | elsif params[:password] == ENV["back_departments"] 54 | departments = [ 55 | '醫學系', 56 | '物治系', 57 | '職治系', 58 | '藥學系', 59 | '法律系', 60 | '政治系', 61 | '經濟系', 62 | '心理系', 63 | '電機系', 64 | 'CSIE', 65 | '建築系', 66 | '都計系', 67 | '工設系', 68 | '生科系' 69 | ] 70 | CourseUpdate.new.update_courses_of_departments(departments) 71 | flash[:success] = "後半系課程更新成功!" 72 | redirect_to update_courses_path 73 | 74 | else 75 | flash[:danger] = "失敗!" 76 | redirect_to update_courses_path 77 | end 78 | end 79 | end 80 | 81 | #departments = [ 82 | #'中文系', 83 | #'外文系', 84 | #'歷史系', 85 | #'台文系', 86 | #'數學系', 87 | #'物理系', 88 | #'化學系', 89 | #'地科系', 90 | #'光電系', 91 | #'機械系', 92 | #'化工系', 93 | #'資源系', 94 | #'材料系', 95 | #'土木系', 96 | #'水利系', 97 | #'工科系', 98 | #'能源學程', 99 | #'系統系', 100 | #'航太系', 101 | #'環工系', 102 | #'測量系', 103 | #'醫工系', 104 | #'會計系', 105 | #'統計系', 106 | #'工資系', 107 | #'企管系', 108 | #'交管系', 109 | #'護理系', 110 | #'醫技系', 111 | #'醫學系', 112 | #'物治系', 113 | #'職治系', 114 | #'藥學系', 115 | #'法律系', 116 | #'政治系', 117 | #'經濟系', 118 | #'心理系', 119 | #'電機系', 120 | #'CSIE', 121 | #'建築系', 122 | #'都計系', 123 | #'工設系', 124 | #'生科系' 125 | #] 126 | #departments = ["能源學程"] 127 | #CourseUpdate.new.update_courses_of_departments(departments) 128 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | # config.action_controller.asset_host = 'http://assets.example.com' 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 68 | # the I18n.default_locale when a translation cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Use default logging formatter so that PID and timestamp are not suppressed. 75 | config.log_formatter = ::Logger::Formatter.new 76 | 77 | # Do not dump schema after migrations. 78 | config.active_record.dump_schema_after_migration = false 79 | end 80 | -------------------------------------------------------------------------------- /app/assets/javascripts/courses/front.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | var numbers = [ '1', '2', '3', '4', 'N', '5', '6', '7', '8', '9' ]; 3 | $.each(numbers, function(index, value) { 4 | $('#course-table tbody').children('tr').eq(index).children('td').eq(0).text(value); 5 | }); 6 | }); 7 | 8 | $(document).on('click', ".glyphicon-plus", function() { 9 | var course_name = $(this).closest('tr').children('td').eq(7).children().text(); 10 | var course_schedule = $(this).closest('tr').children('td').eq(13).text(); 11 | var parsed_schedule = {1: [], 2: [], 3: [], 4: [], 5: []}; 12 | var zero_to_nine = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 13 | 14 | // ex: [1]2~3, [2]2~4 15 | while (true) { 16 | var continuous_courses = course_schedule.match(/\[\d\]\d~\d/); 17 | 18 | if (continuous_courses) { 19 | var day = continuous_courses[0].match(/\[\d\]/)[0].match(/\d/)[0]; 20 | var schedule_of_this_day = continuous_courses[0].match(/\d~\d/)[0]; 21 | var schedule_start = parseInt(schedule_of_this_day.split('~')[0]); 22 | var schedule_end = parseInt(schedule_of_this_day.split('~')[1]) + 1; 23 | parsed_schedule[day] = parsed_schedule[day].concat(zero_to_nine.slice(schedule_start, schedule_end)); 24 | course_schedule = course_schedule.replace(/\[\d\]\d~\d/, ''); 25 | } 26 | else { 27 | break; 28 | } 29 | } 30 | 31 | // ex: [2]N 32 | while (true) { 33 | var single_course = course_schedule.match(/\[\d\]\d|\[\d\]N/); 34 | 35 | if (single_course) { 36 | var day = single_course[0].match(/\[\d\]/)[0].match(/\d/)[0]; 37 | var schedule_of_this_day = single_course[0].match(/\]\d/)[0]; 38 | parsed_schedule[day] = parsed_schedule[day].concat(schedule_of_this_day); 39 | course_schedule = course_schedule.replace(/\[\d\]\d|\[\d\]N/, ''); 40 | } 41 | else { 42 | break; 43 | } 44 | } 45 | 46 | var user_can_select_this_course = true; 47 | Object.keys(parsed_schedule).forEach(function(day) { 48 | for (var j = 0 ; j < parsed_schedule[day].length ; ++j) { 49 | var course_time_index = parsed_schedule[day][j]; 50 | var target_cell = $('tr[value='+course_time_index+'] td:nth-child('+day+')').next(); 51 | 52 | if (target_cell.text() != "") { 53 | user_can_select_this_course = false; 54 | } 55 | } 56 | }); 57 | 58 | if (user_can_select_this_course) { 59 | Object.keys(parsed_schedule).forEach(function(day) { 60 | for (var j = 0 ; j < parsed_schedule[day].length ; ++j) { 61 | var course_time_index = parsed_schedule[day][j]; 62 | var target_cell = $('tr[value='+course_time_index+'] td:nth-child('+day+')').next(); 63 | target_cell.addClass('warning'); 64 | target_cell.text(course_name); 65 | } 66 | }); 67 | } 68 | else { 69 | alert("這個時段已經有課囉"); 70 | } 71 | }); 72 | 73 | $(document).on("click", ".glyphicon-remove", function() { 74 | var courseId = $(this).closest('tr').attr('id').split('-')[1]; 75 | var courseInTable = $('.course-table-'+courseId+''); 76 | 77 | courseInTable.text(''); 78 | courseInTable.removeClass('info'); 79 | $(this).closest('tr').remove(); 80 | }); 81 | 82 | $(document).on('mouseenter', ".glyphicon-collapse-down", function() { 83 | $(this).css('cursor', 'pointer'); 84 | }); 85 | 86 | $(document).on('mouseenter', ".glyphicon-collapse-up", function() { 87 | $(this).css('cursor', 'pointer'); 88 | }); 89 | 90 | $(document).on('click', ".glyphicon-collapse-up", function() { 91 | $(this).parent().next().hide(); 92 | $(this).parent().append(''); 93 | $(this).remove(); 94 | }); 95 | 96 | $(document).on('click', ".glyphicon-collapse-down", function() { 97 | $(this).parent().next().show(); 98 | $(this).parent().append(''); 99 | $(this).remove(); 100 | }); 101 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | # sessions/new.html.haml 3 | def options_of_departments 4 | result = [] 5 | departments = [ 6 | ['中文系', 'B1'], 7 | ['外文系', 'B2'], 8 | ['歷史系', 'B3'], 9 | ['台文系', 'B5'], 10 | ['數學系', 'C1'], 11 | ['物理系', 'C2'], 12 | ['化學系', 'C3'], 13 | ['地科系', 'C4'], 14 | ['光電系', 'F8'], 15 | ['機械系', 'E1'], 16 | ['化工系', 'E3'], 17 | ['資源系', 'E4'], 18 | ['材料系', 'E5'], 19 | ['土木系', 'E6'], 20 | ['水利系', 'E8'], 21 | ['工科系', 'E9'], 22 | ['能源學程', 'F0'], 23 | ['系統系', 'F1'], 24 | ['航太系', 'F4'], 25 | ['環工系', 'F5'], 26 | ['測量系', 'F6'], 27 | ['醫工系', 'F9'], 28 | ['會計系', 'H1'], 29 | ['統計系', 'H2'], 30 | ['工資系', 'H3'], 31 | ['企管系', 'H4'], 32 | ['交管系', 'H5'], 33 | ['護理系', 'I2'], 34 | ['醫技系', 'I3'], 35 | ['醫學系', 'I5'], 36 | ['物治系', 'I6'], 37 | ['職治系', 'I7'], 38 | ['藥學系', 'I8'], 39 | ['法律系', 'D2'], 40 | ['政治系', 'D4'], 41 | ['經濟系', 'D5'], 42 | ['心理系', 'D8'], 43 | ['電機系', 'E2'], 44 | ['資訊系', 'F7'], 45 | ['建築系', 'E7'], 46 | ['都計系', 'F2'], 47 | ['工設系', 'F3'], 48 | ['生科系', 'C5'] 49 | #['中文系', 'chinese_literature'], 50 | #['外文系', 'foreign_languages'], 51 | #['歷史系', 'history'], 52 | #['台文系', 'taiwanese_literature'], 53 | #['數學系', 'mathematics'], 54 | #['物理系', 'physics'], 55 | #['化學系', 'chemistry'], 56 | #['地科系', 'earth_science'], 57 | #['光電系', 'photonics'], 58 | #['機械系', 'mechanical_engineering'], 59 | #['化工系', 'chemical_engineering'], 60 | #['資源系', 'resources_engineering'], 61 | #['材料系', 'materials_science_engineering'], 62 | #['土木系', 'civil_engineering'], 63 | #['水利系', 'hydraulic_ocean_engineering'], 64 | #['工科系', 'engineering_science'], 65 | #['系統系', 'system_engineering'], 66 | #['航太系', 'aeronautics_astronautics'], 67 | #['環工系', 'environmental_engineering'], 68 | #['測量系', 'geomatics'], 69 | #['醫工系', 'biomedical_engineering'], 70 | #['會計系', 'accounting'], 71 | #['統計系', 'statistics'], 72 | #['工資系', 'industrial_information_management'], 73 | #['企管系', 'business_administration'], 74 | #['交管系', 'transportation_management'], 75 | #['護理系', 'nursing'], 76 | #['醫技系', 'medical_laboratory_science'], 77 | #['醫學系', 'medicine'], 78 | #['物治系', 'physical_therapy'], 79 | #['職治系', 'occupational_therapy'], 80 | #['藥學系', 'pharmacy'], 81 | #['法律系', 'law'], 82 | #['政治系', 'political_science'], 83 | #['經濟系', 'economics'], 84 | #['心理系', 'psychology'], 85 | #['電機系', 'electrical_engineering'], 86 | #['資訊系', 'computer_science'], 87 | #['建築系', 'architecture'], 88 | #['都計系', 'urban_planning'], 89 | #['工設系', 'industrial_design'], 90 | #['生科系', 'life_science'] 91 | ] 92 | numbers_of_each_school = [4, 5, 13, 5, 6, 4, 2, 3, 1] 93 | schools = [ 94 | "文學院", "理學院", "工學院", "管理學院", "醫學院", 95 | "社會科學院", "電機資訊學院", "規劃與設計學院", "生物科學與科技學院" 96 | ] 97 | 98 | loop do 99 | break if departments.empty? 100 | 101 | departments_of_specific_school = departments.shift(numbers_of_each_school.shift) 102 | specific_school = schools.shift 103 | 104 | result << [specific_school, departments_of_specific_school] 105 | end 106 | 107 | result 108 | end 109 | 110 | # courses/_search_result.html.haml 111 | def course_column_names 112 | [ 113 | "icon", "id", "institute_code", "serial_number", "class_name", "year", 114 | "category", "taught_in_english", "course_name", "elective_or_required", 115 | "credits", "instructor","selected", "space_available", "schedule", 116 | "classroom", "remark" 117 | ] 118 | end 119 | 120 | # courses/_list_of_courses.html.haml, _search_result.html.haml 121 | def better_schedule_text(schedule) 122 | position = schedule.rindex('[') 123 | position = 0 if position.nil? 124 | schedule.insert(position, ' ') 125 | end 126 | 127 | # {'1' => {'1' => ['course_name', 'id']}} 128 | def course_table_data(courses) 129 | result = Hash.new do |hash, key| 130 | hash[key] = {'1' => {}, '2' => {}, '3' => {}, '4' => {}, '5' => {}} 131 | end 132 | ['1', '2', '3', '4', 'N', '5', '6', '7', '8', '9'].each do |number| 133 | result[number] 134 | end 135 | 136 | courses.each do |course| 137 | course.well_formatted_schedule.each do |day, schedule_time| 138 | schedule_time.each do |time| 139 | result[time][day][:course_name] = course.course_name 140 | result[time][day][:id] = course.id 141 | end 142 | end 143 | end 144 | 145 | result 146 | end 147 | end 148 | -------------------------------------------------------------------------------- /app/services/course_updates.rb: -------------------------------------------------------------------------------- 1 | require 'nokogiri' 2 | require 'json' 3 | require 'mechanize' 4 | 5 | class CourseUpdates 6 | def execute(departments) # departments should be an array of strings 7 | 8 | departments.each do |department_name| 9 | @courses_of_specific_department = [] 10 | 11 | extract_courses(department_name) 12 | save_courses(department_name) 13 | 14 | puts "#{department_name}: #{@courses_of_specific_department.count} // #{Course.count}" 15 | end 16 | end 17 | 18 | private 19 | 20 | def extract_courses(department_name) 21 | agent = Mechanize.new 22 | agent.ignore_bad_chunking = true 23 | 24 | page = agent.get('http://course-query.acad.ncku.edu.tw/qry/index.php?lang=zh_tw') 25 | department_courses_page = agent.page.links.find do |link| 26 | link.text.include? department_name 27 | end.click 28 | 29 | department_courses_page.search('table/tbody/tr').each do |row| 30 | course = {} 31 | [ 32 | [:institute_code, 'td[2]/text()'], 33 | [:serial_number, 'td[3]/text()'], 34 | [:class_name, 'td[6]/text()'], 35 | [:year, 'td[7]/a/text()'], 36 | [:category, 'td[8]/text()'], 37 | [:taught_in_english, 'td[10]/text()'], 38 | [:course_name, 'td[11]/a'], 39 | [:elective_or_required, 'td[12]/text()'], 40 | [:credits, 'td[13]/text()'], 41 | [:instructor, 'td[14]/text()'], 42 | [:selected, 'td[15]/text()'], 43 | [:space_available, 'td[16]/text()'], 44 | [:schedule, 'td[17]/text()'], 45 | [:classroom, 'td[18]/a/text()'], 46 | [:remark, 'td[19]/text()'] 47 | ].each do |column, xpath| 48 | case column 49 | when :course_name 50 | remain_course_name_link(course, row, xpath) 51 | when :taught_in_english 52 | taught_in_english = row.search(xpath).text.strip 53 | course[column] = (taught_in_english =~ /Y/ ? true : false) 54 | else 55 | course[column] = row.search(xpath).text.strip #.gsub(/\s+/, "") 56 | end 57 | end 58 | 59 | if course[:course_name].present? 60 | course[:classroom].gsub!(/\s+/, " ") 61 | 62 | if duplicate_course?(course) 63 | @courses_of_specific_department.last[:schedule] += " #{course[:schedule]}" 64 | else 65 | if course[:department] =~ /基礎國文/ 66 | course[:category] = '基礎國文' 67 | elsif course[:department] =~ /外國語言/ && 68 | course[:course_name] =~ /英文/ && 69 | course[:year] == '1' 70 | course[:category] = '大一英文' 71 | end 72 | 73 | @courses_of_specific_department << course 74 | end 75 | end 76 | end 77 | end 78 | 79 | def save_courses(department_name) 80 | courses_in_database = Course.where(institute_code: code_of(department_name)) 81 | 82 | if courses_in_database.count == 0 83 | @courses_of_specific_department.each {|course| Course.create!(course)} 84 | else 85 | ActiveRecord::Base.transaction do 86 | begin 87 | courses_in_database.zip(@courses_of_specific_department).each do |course_in_database, realtime_course_data| 88 | course_in_database.update(selected: realtime_course_data[:selected]) 89 | end 90 | rescue => e 91 | p e.message 92 | end 93 | end 94 | end 95 | end 96 | 97 | def remain_course_name_link(course, row, xpath) 98 | course[:course_name] = row.search(xpath).to_s.strip 99 | end 100 | 101 | def duplicate_course?(course) 102 | if course[:institute_code] == "" && 103 | course[:serial_number] == "" && 104 | course[:course_name] == @courses_of_specific_department.last[:course_name] 105 | return true 106 | else 107 | return false 108 | end 109 | end 110 | 111 | def code_of(department_name) 112 | hash = { 113 | '學士學程' => 'AN', 114 | '中文系' => 'B1', 115 | '外文系' => 'B2', 116 | '歷史系' => 'B3', 117 | '台文系' => 'B5', 118 | '數學系' => 'C1', 119 | '物理系' => 'C2', 120 | '化學系' => 'C3', 121 | '地科系' => 'C4', 122 | '光電系' => 'F8', 123 | '機械系' => 'E1', 124 | '化工系' => 'E3', 125 | '資源系' => 'E4', 126 | '材料系' => 'E5', 127 | '土木系' => 'E6', 128 | '水利系' => 'E8', 129 | '工科系' => 'E9', 130 | '能源學程' => 'F0', 131 | '系統系' => 'F1', 132 | '航太系' => 'F4', 133 | '環工系' => 'F5', 134 | '測量系' => 'F6', 135 | '醫工系' => 'F9', 136 | '會計系' => 'H1', 137 | '統計系' => 'H2', 138 | '工資系' => 'H3', 139 | '企管系' => 'H4', 140 | '交管系' => 'H5', 141 | '護理系' => 'I2', 142 | '醫技系' => 'I3', 143 | '醫學系' => 'I5', 144 | '物治系' => 'I6', 145 | '職治系' => 'I7', 146 | '藥學系' => 'I8', 147 | '法律系' => 'D2', 148 | '政治系' => 'D4', 149 | '經濟系' => 'D5', 150 | '心理系' => 'D8', 151 | '電機系' => 'E2', 152 | 'CSIE' => 'F7', 153 | '建築系' => 'E7', 154 | '都計系' => 'F2', 155 | '工設系' => 'F3', 156 | '生科系' => 'C5', 157 | '外國語言' => 'A1', 158 | '基礎國文' => 'A7', 159 | '通識中心' => 'A9', 160 | '公民歷史' => 'AG' 161 | } 162 | hash[department_name] 163 | end 164 | end 165 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actionmailer (4.2.0) 5 | actionpack (= 4.2.0) 6 | actionview (= 4.2.0) 7 | activejob (= 4.2.0) 8 | mail (~> 2.5, >= 2.5.4) 9 | rails-dom-testing (~> 1.0, >= 1.0.5) 10 | actionpack (4.2.0) 11 | actionview (= 4.2.0) 12 | activesupport (= 4.2.0) 13 | rack (~> 1.6.0) 14 | rack-test (~> 0.6.2) 15 | rails-dom-testing (~> 1.0, >= 1.0.5) 16 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 17 | actionview (4.2.0) 18 | activesupport (= 4.2.0) 19 | builder (~> 3.1) 20 | erubis (~> 2.7.0) 21 | rails-dom-testing (~> 1.0, >= 1.0.5) 22 | rails-html-sanitizer (~> 1.0, >= 1.0.1) 23 | activejob (4.2.0) 24 | activesupport (= 4.2.0) 25 | globalid (>= 0.3.0) 26 | activemodel (4.2.0) 27 | activesupport (= 4.2.0) 28 | builder (~> 3.1) 29 | activerecord (4.2.0) 30 | activemodel (= 4.2.0) 31 | activesupport (= 4.2.0) 32 | arel (~> 6.0) 33 | activesupport (4.2.0) 34 | i18n (~> 0.7) 35 | json (~> 1.7, >= 1.7.7) 36 | minitest (~> 5.1) 37 | thread_safe (~> 0.3, >= 0.3.4) 38 | tzinfo (~> 1.1) 39 | arel (6.0.0) 40 | autoprefixer-rails (5.1.7.1) 41 | execjs 42 | json 43 | bcrypt (3.1.9) 44 | better_errors (1.0.1) 45 | coderay (>= 1.0.0) 46 | erubis (>= 2.6.6) 47 | binding_of_caller (0.7.2) 48 | debug_inspector (>= 0.0.1) 49 | bootstrap-sass (3.3.4.1) 50 | autoprefixer-rails (>= 5.0.0.1) 51 | sass (>= 3.2.19) 52 | builder (3.2.2) 53 | byebug (4.0.5) 54 | columnize (= 0.9.0) 55 | coderay (1.1.0) 56 | coffee-rails (4.1.0) 57 | coffee-script (>= 2.2.0) 58 | railties (>= 4.0.0, < 5.0) 59 | coffee-script (2.4.1) 60 | coffee-script-source 61 | execjs 62 | coffee-script-source (1.9.1.1) 63 | columnize (0.9.0) 64 | debug_inspector (0.0.2) 65 | domain_name (0.5.24) 66 | unf (>= 0.0.5, < 1.0.0) 67 | erubis (2.7.0) 68 | execjs (2.5.2) 69 | figaro (1.1.0) 70 | thor (~> 0.14) 71 | globalid (0.3.5) 72 | activesupport (>= 4.1.0) 73 | haml (4.0.5) 74 | tilt 75 | haml-rails (0.5.3) 76 | actionpack (>= 4.0.1) 77 | activesupport (>= 4.0.1) 78 | haml (>= 3.1, < 5.0) 79 | railties (>= 4.0.1) 80 | http-cookie (1.0.2) 81 | domain_name (~> 0.5) 82 | i18n (0.7.0) 83 | jbuilder (2.2.13) 84 | activesupport (>= 3.0.0, < 5) 85 | multi_json (~> 1.2) 86 | jquery-rails (4.0.3) 87 | rails-dom-testing (~> 1.0) 88 | railties (>= 4.2.0) 89 | thor (>= 0.14, < 2.0) 90 | json (1.8.2) 91 | loofah (2.0.2) 92 | nokogiri (>= 1.5.9) 93 | mail (2.6.3) 94 | mime-types (>= 1.16, < 3) 95 | mechanize (2.7.3) 96 | domain_name (~> 0.5, >= 0.5.1) 97 | http-cookie (~> 1.0) 98 | mime-types (~> 2.0) 99 | net-http-digest_auth (~> 1.1, >= 1.1.1) 100 | net-http-persistent (~> 2.5, >= 2.5.2) 101 | nokogiri (~> 1.4) 102 | ntlm-http (~> 0.1, >= 0.1.1) 103 | webrobots (>= 0.0.9, < 0.2) 104 | method_source (0.8.2) 105 | mime-types (2.5) 106 | mini_portile (0.6.2) 107 | minitest (5.6.1) 108 | multi_json (1.11.0) 109 | net-http-digest_auth (1.4) 110 | net-http-persistent (2.9.4) 111 | nokogiri (1.6.6.2) 112 | mini_portile (~> 0.6.0) 113 | ntlm-http (0.1.1) 114 | pg (0.17.0) 115 | pry (0.9.12.3) 116 | coderay (~> 1.0) 117 | method_source (~> 0.8) 118 | slop (~> 3.4) 119 | puma (2.11.1) 120 | rack (>= 1.1, < 2.0) 121 | rack (1.6.0) 122 | rack-test (0.6.3) 123 | rack (>= 1.0) 124 | rails (4.2.0) 125 | actionmailer (= 4.2.0) 126 | actionpack (= 4.2.0) 127 | actionview (= 4.2.0) 128 | activejob (= 4.2.0) 129 | activemodel (= 4.2.0) 130 | activerecord (= 4.2.0) 131 | activesupport (= 4.2.0) 132 | bundler (>= 1.3.0, < 2.0) 133 | railties (= 4.2.0) 134 | sprockets-rails 135 | rails-deprecated_sanitizer (1.0.3) 136 | activesupport (>= 4.2.0.alpha) 137 | rails-dom-testing (1.0.6) 138 | activesupport (>= 4.2.0.beta, < 5.0) 139 | nokogiri (~> 1.6.0) 140 | rails-deprecated_sanitizer (>= 1.0.1) 141 | rails-html-sanitizer (1.0.2) 142 | loofah (~> 2.0) 143 | railties (4.2.0) 144 | actionpack (= 4.2.0) 145 | activesupport (= 4.2.0) 146 | rake (>= 0.8.7) 147 | thor (>= 0.18.1, < 2.0) 148 | rake (10.4.2) 149 | rdoc (4.2.0) 150 | json (~> 1.4) 151 | sass (3.4.13) 152 | sass-rails (5.0.3) 153 | railties (>= 4.0.0, < 5.0) 154 | sass (~> 3.1) 155 | sprockets (>= 2.8, < 4.0) 156 | sprockets-rails (>= 2.0, < 4.0) 157 | tilt (~> 1.1) 158 | sdoc (0.4.1) 159 | json (~> 1.7, >= 1.7.7) 160 | rdoc (~> 4.0) 161 | slop (3.6.0) 162 | spring (1.3.5) 163 | sprockets (3.0.3) 164 | rack (~> 1.0) 165 | sprockets-rails (2.2.4) 166 | actionpack (>= 3.0) 167 | activesupport (>= 3.0) 168 | sprockets (>= 2.8, < 4.0) 169 | thor (0.19.1) 170 | thread_safe (0.3.5) 171 | tilt (1.4.1) 172 | turbolinks (2.5.3) 173 | coffee-rails 174 | tzinfo (1.2.2) 175 | thread_safe (~> 0.1) 176 | uglifier (2.7.1) 177 | execjs (>= 0.3.0) 178 | json (>= 1.8.0) 179 | unf (0.1.4) 180 | unf_ext 181 | unf_ext (0.0.7.1) 182 | web-console (2.1.2) 183 | activemodel (>= 4.0) 184 | binding_of_caller (>= 0.7.2) 185 | railties (>= 4.0) 186 | sprockets-rails (>= 2.0, < 4.0) 187 | webrobots (0.1.1) 188 | 189 | PLATFORMS 190 | ruby 191 | 192 | DEPENDENCIES 193 | bcrypt 194 | better_errors 195 | bootstrap-sass (~> 3.3.4) 196 | byebug 197 | coffee-rails (~> 4.1.0) 198 | figaro 199 | haml-rails 200 | jbuilder (~> 2.0) 201 | jquery-rails 202 | mechanize 203 | pg 204 | pry 205 | puma 206 | rails (= 4.2.0) 207 | sass-rails (~> 5.0) 208 | sdoc (~> 0.4.0) 209 | spring 210 | turbolinks 211 | uglifier (>= 1.3.0) 212 | web-console (~> 2.0) 213 | --------------------------------------------------------------------------------