├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── Procfile ├── Rakefile ├── Readme.md ├── app ├── assets │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── assignments.js.coffee │ │ ├── auth.js.coffee │ │ ├── students.js.coffee │ │ └── submissions.js.coffee │ └── stylesheets │ │ ├── application.css.scss │ │ ├── bootstrap-generators.scss │ │ └── bootstrap-variables.scss ├── controllers │ ├── application_controller.rb │ ├── assignments_controller.rb │ ├── auth_controller.rb │ ├── concerns │ │ └── .keep │ ├── students_controller.rb │ └── submissions_controller.rb ├── helpers │ ├── application_helper.rb │ ├── assignments_helper.rb │ ├── auth_helper.rb │ ├── students_helper.rb │ └── submissions_helper.rb ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── assignment.rb │ ├── concerns │ │ └── .keep │ ├── student.rb │ └── submission.rb └── views │ ├── assignments │ ├── _form.html.haml │ ├── edit.html.haml │ ├── index.html.haml │ ├── index.json.jbuilder │ ├── new.html.haml │ ├── show.html.haml │ └── show.json.jbuilder │ ├── layouts │ ├── _navbar.html.haml │ └── application.html.haml │ ├── pages │ └── 401.html.haml │ ├── students │ ├── _form.html.haml │ ├── edit.html.haml │ ├── index.html.haml │ ├── index.json.jbuilder │ ├── new.html.haml │ ├── show.html.haml │ └── show.json.jbuilder │ └── submissions │ ├── _form.html.haml │ ├── edit.html.haml │ ├── index.html.haml │ ├── index.json.jbuilder │ ├── new.html.haml │ ├── show.html.haml │ └── show.json.jbuilder ├── bin ├── bundle ├── rails ├── rake └── spring ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── aaaaaa.rb │ ├── admin_users.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── friendly_id.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── octokit.rb │ ├── omniauth.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── routes.rb ├── secrets.yml └── unicorn.rb ├── db ├── migrate │ ├── 20140824155718_create_friendly_id_slugs.rb │ ├── 20140919162502_create_assignments.rb │ ├── 20140919162620_create_students.rb │ ├── 20140919163045_create_submissions.rb │ ├── 20140919163518_add_slug_to_students.rb │ ├── 20140921162741_add_tag_prefix_to_assignments.rb │ ├── 20140921174147_add_last_sync_to_students.rb │ ├── 20140922001500_add_feedback_to_submissions.rb │ └── 20141110170326_add_grade_to_assignments.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep ├── tasks │ ├── .keep │ └── db.rake └── templates │ └── haml │ ├── controller │ └── view.html.haml │ └── scaffold │ ├── _form.html.haml │ ├── edit.html.haml │ ├── index.html.haml │ ├── new.html.haml │ └── show.html.haml ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico └── robots.txt ├── scripts ├── scramble-names.rb └── students_to_csv.rb ├── test ├── controllers │ ├── .keep │ ├── assignments_controller_test.rb │ ├── auth_controller_test.rb │ ├── students_controller_test.rb │ └── submissions_controller_test.rb ├── fixtures │ ├── .keep │ ├── assignments.yml │ ├── students.yml │ └── submissions.yml ├── helpers │ ├── .keep │ ├── assignments_helper_test.rb │ ├── auth_helper_test.rb │ ├── students_helper_test.rb │ └── submissions_helper_test.rb ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── assignment_test.rb │ ├── student_test.rb │ └── submission_test.rb └── test_helper.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.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/*.log 16 | /tmp 17 | 18 | # Ignore students 19 | /students.rb 20 | 21 | # Ignore environment vars 22 | .env 23 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.1.4 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | ruby "2.1.4" 2 | source 'https://rubygems.org' 3 | source 'https://rails-assets.org' 4 | 5 | 6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 7 | gem 'rails', '4.1.5' 8 | # Use SCSS for stylesheets 9 | gem 'sass-rails', '~> 4.0.3' 10 | # Use Uglifier as compressor for JavaScript assets 11 | gem 'uglifier', '>= 1.3.0' 12 | # Use CoffeeScript for .js.coffee assets and views 13 | gem 'coffee-rails', '~> 4.0.0' 14 | # See https://github.com/sstephenson/execjs#readme for more supported runtimes 15 | # gem 'therubyracer', platforms: :ruby 16 | 17 | # Use jquery as the JavaScript library 18 | gem 'jquery-rails' 19 | # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks 20 | gem 'turbolinks' 21 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 22 | gem 'jbuilder', '~> 2.0' 23 | # bundle exec rake doc:rails generates the API under doc/api. 24 | gem 'sdoc', '~> 0.4.0', group: :doc 25 | 26 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 27 | gem 'spring', group: :development 28 | 29 | # Use ActiveModel has_secure_password 30 | # gem 'bcrypt', '~> 3.1.7' 31 | 32 | # Use unicorn as the app server 33 | # gem 'unicorn' 34 | 35 | # Use Capistrano for deployment 36 | # gem 'capistrano-rails', group: :development 37 | 38 | # Use debugger 39 | # gem 'debugger', group: [:development, :test] 40 | 41 | # Gems added by me 42 | gem 'bootstrap-sass', '~> 3.2.0' 43 | gem 'autoprefixer-rails' 44 | gem 'haml-rails' 45 | gem 'friendly_id', '~> 5.0.0' 46 | gem 'rinku' 47 | gem 'icomoon-rails' 48 | gem "omniauth-google-oauth2" 49 | gem 'dotenv' 50 | gem 'high_voltage' 51 | gem "octokit", "~> 3.0" 52 | gem "parallel" 53 | gem "rails-assets-chartjs", "1.0.1.beta.4" 54 | 55 | # install with "rails generate bootstrap:install -e haml" 56 | gem 'bootstrap-generators', '~> 3.1.1' 57 | gem 'hirb' 58 | 59 | gem 'pg' #postgres 60 | 61 | group :development do 62 | gem 'faker' 63 | end 64 | 65 | group :production do 66 | gem 'rails_12factor' 67 | gem 'unicorn' 68 | end 69 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | remote: https://rails-assets.org/ 4 | specs: 5 | actionmailer (4.1.5) 6 | actionpack (= 4.1.5) 7 | actionview (= 4.1.5) 8 | mail (~> 2.5.4) 9 | actionpack (4.1.5) 10 | actionview (= 4.1.5) 11 | activesupport (= 4.1.5) 12 | rack (~> 1.5.2) 13 | rack-test (~> 0.6.2) 14 | actionview (4.1.5) 15 | activesupport (= 4.1.5) 16 | builder (~> 3.1) 17 | erubis (~> 2.7.0) 18 | activemodel (4.1.5) 19 | activesupport (= 4.1.5) 20 | builder (~> 3.1) 21 | activerecord (4.1.5) 22 | activemodel (= 4.1.5) 23 | activesupport (= 4.1.5) 24 | arel (~> 5.0.0) 25 | activesupport (4.1.5) 26 | i18n (~> 0.6, >= 0.6.9) 27 | json (~> 1.7, >= 1.7.7) 28 | minitest (~> 5.1) 29 | thread_safe (~> 0.1) 30 | tzinfo (~> 1.1) 31 | addressable (2.3.6) 32 | arel (5.0.1.20140414130214) 33 | autoprefixer-rails (2.2.0.20140804) 34 | execjs 35 | bootstrap-generators (3.1.1.3) 36 | railties (>= 3.1.0) 37 | bootstrap-sass (3.2.0.1) 38 | sass (~> 3.2) 39 | builder (3.2.2) 40 | coffee-rails (4.0.1) 41 | coffee-script (>= 2.2.0) 42 | railties (>= 4.0.0, < 5.0) 43 | coffee-script (2.3.0) 44 | coffee-script-source 45 | execjs 46 | coffee-script-source (1.7.1) 47 | dotenv (0.11.1) 48 | dotenv-deployment (~> 0.0.2) 49 | dotenv-deployment (0.0.2) 50 | erubis (2.7.0) 51 | execjs (2.2.1) 52 | faker (1.4.3) 53 | i18n (~> 0.5) 54 | faraday (0.9.0) 55 | multipart-post (>= 1.2, < 3) 56 | friendly_id (5.0.4) 57 | activerecord (>= 4.0.0) 58 | haml (4.0.5) 59 | tilt 60 | haml-rails (0.5.3) 61 | actionpack (>= 4.0.1) 62 | activesupport (>= 4.0.1) 63 | haml (>= 3.1, < 5.0) 64 | railties (>= 4.0.1) 65 | hashie (3.3.1) 66 | high_voltage (2.2.1) 67 | hike (1.2.3) 68 | hirb (0.7.2) 69 | i18n (0.6.11) 70 | icomoon-rails (0.1.0) 71 | railties (>= 3.1.1) 72 | sass-rails (>= 3.1.1) 73 | jbuilder (2.1.3) 74 | activesupport (>= 3.0.0, < 5) 75 | multi_json (~> 1.2) 76 | jquery-rails (3.1.1) 77 | railties (>= 3.0, < 5.0) 78 | thor (>= 0.14, < 2.0) 79 | json (1.8.1) 80 | jwt (1.0.0) 81 | kgio (2.9.2) 82 | mail (2.5.4) 83 | mime-types (~> 1.16) 84 | treetop (~> 1.4.8) 85 | mime-types (1.25.1) 86 | minitest (5.4.0) 87 | multi_json (1.10.1) 88 | multi_xml (0.5.5) 89 | multipart-post (2.0.0) 90 | oauth2 (1.0.0) 91 | faraday (>= 0.8, < 0.10) 92 | jwt (~> 1.0) 93 | multi_json (~> 1.3) 94 | multi_xml (~> 0.5) 95 | rack (~> 1.2) 96 | octokit (3.3.1) 97 | sawyer (~> 0.5.3) 98 | omniauth (1.2.2) 99 | hashie (>= 1.2, < 4) 100 | rack (~> 1.0) 101 | omniauth-google-oauth2 (0.2.5) 102 | omniauth (> 1.0) 103 | omniauth-oauth2 (~> 1.1) 104 | omniauth-oauth2 (1.2.0) 105 | faraday (>= 0.8, < 0.10) 106 | multi_json (~> 1.3) 107 | oauth2 (~> 1.0) 108 | omniauth (~> 1.2) 109 | parallel (1.3.2) 110 | pg (0.17.1) 111 | polyglot (0.3.5) 112 | rack (1.5.2) 113 | rack-test (0.6.2) 114 | rack (>= 1.0) 115 | rails (4.1.5) 116 | actionmailer (= 4.1.5) 117 | actionpack (= 4.1.5) 118 | actionview (= 4.1.5) 119 | activemodel (= 4.1.5) 120 | activerecord (= 4.1.5) 121 | activesupport (= 4.1.5) 122 | bundler (>= 1.3.0, < 2.0) 123 | railties (= 4.1.5) 124 | sprockets-rails (~> 2.0) 125 | rails-assets-chartjs (1.0.1.beta.4) 126 | rails_12factor (0.0.2) 127 | rails_serve_static_assets 128 | rails_stdout_logging 129 | rails_serve_static_assets (0.0.2) 130 | rails_stdout_logging (0.0.3) 131 | railties (4.1.5) 132 | actionpack (= 4.1.5) 133 | activesupport (= 4.1.5) 134 | rake (>= 0.8.7) 135 | thor (>= 0.18.1, < 2.0) 136 | raindrops (0.13.0) 137 | rake (10.3.2) 138 | rdoc (4.1.1) 139 | json (~> 1.4) 140 | rinku (1.7.3) 141 | sass (3.2.19) 142 | sass-rails (4.0.3) 143 | railties (>= 4.0.0, < 5.0) 144 | sass (~> 3.2.0) 145 | sprockets (~> 2.8, <= 2.11.0) 146 | sprockets-rails (~> 2.0) 147 | sawyer (0.5.5) 148 | addressable (~> 2.3.5) 149 | faraday (~> 0.8, < 0.10) 150 | sdoc (0.4.1) 151 | json (~> 1.7, >= 1.7.7) 152 | rdoc (~> 4.0) 153 | spring (1.1.3) 154 | sprockets (2.11.0) 155 | hike (~> 1.2) 156 | multi_json (~> 1.0) 157 | rack (~> 1.0) 158 | tilt (~> 1.1, != 1.3.0) 159 | sprockets-rails (2.1.3) 160 | actionpack (>= 3.0) 161 | activesupport (>= 3.0) 162 | sprockets (~> 2.8) 163 | thor (0.19.1) 164 | thread_safe (0.3.4) 165 | tilt (1.4.1) 166 | treetop (1.4.15) 167 | polyglot 168 | polyglot (>= 0.3.1) 169 | turbolinks (2.3.0) 170 | coffee-rails 171 | tzinfo (1.2.2) 172 | thread_safe (~> 0.1) 173 | uglifier (2.5.3) 174 | execjs (>= 0.3.0) 175 | json (>= 1.8.0) 176 | unicorn (4.8.3) 177 | kgio (~> 2.6) 178 | rack 179 | raindrops (~> 0.7) 180 | 181 | PLATFORMS 182 | ruby 183 | 184 | DEPENDENCIES 185 | autoprefixer-rails 186 | bootstrap-generators (~> 3.1.1) 187 | bootstrap-sass (~> 3.2.0) 188 | coffee-rails (~> 4.0.0) 189 | dotenv 190 | faker 191 | friendly_id (~> 5.0.0) 192 | haml-rails 193 | high_voltage 194 | hirb 195 | icomoon-rails 196 | jbuilder (~> 2.0) 197 | jquery-rails 198 | octokit (~> 3.0) 199 | omniauth-google-oauth2 200 | parallel 201 | pg 202 | rails (= 4.1.5) 203 | rails-assets-chartjs (= 1.0.1.beta.4) 204 | rails_12factor 205 | rinku 206 | sass-rails (~> 4.0.3) 207 | sdoc (~> 0.4.0) 208 | spring 209 | turbolinks 210 | uglifier (>= 1.3.0) 211 | unicorn 212 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Gradebook 2 | 3 | Gradebook is a tool for managing assignments submitted via a GitHub repo, using tags to differentiate between assignments. This is a "scratch your own itch" project that I wrote after I got really fed up with trying to use Google Spreadsheets to input grades, but has the potential to be something actually quite useful. Contributions welcome! 4 | 5 | ## Features 6 | 7 | - Automatic submission tracking via the Github API 8 | - Progress graphs to show how many students are caught up 9 | - Supports assignment resubmission using git tags/semantic versioning 10 | - Secure private links for students to view their grades 11 | - Secure administrator login via Google OAuth 12 | - Nice admin UI (Bootstrap) 13 | 14 | ## Use 15 | 16 | 1. Clone the repo 17 | 2. Edit `config/initializers/admin_users.rb` to reflect the admins' google account info. 18 | 3. Set the `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GITHUB_CLIENT_ID`, and `GITHUB_CLIENT_SECRET` environment variables with your GitHub and Google API credentials (if you're deploying to heroku, you'll need to set those on the server as well). 19 | 4. Install dependencies with `bundle install` 20 | 5. Create the database with `rake db:create db:migrate` (you will need to have postgres installed) 21 | 6. Run this app with `rackup` or `shotgun` 22 | 23 | ## Screenshots 24 | 25 |  26 | 27 |  28 | 29 |  30 | 31 |  32 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/app/assets/images/.keep -------------------------------------------------------------------------------- /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 vendor/assets/javascripts of plugins, if any, 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 jquery_ujs 15 | //= require bootstrap-sprockets 16 | //= require_tree . 17 | -------------------------------------------------------------------------------- /app/assets/javascripts/assignments.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | 5 | $ -> 6 | 7 | options = { 8 | animationEasing: 'easeOutCubic' 9 | responsive: true 10 | showTooltips: false 11 | } 12 | 13 | $(".assignment-chart").each -> 14 | elem = $(this) 15 | id = elem.data('id') 16 | url = "/assignments/"+id+"/graph_data.json" 17 | $.getJSON url, null, (data)-> 18 | context = elem[0].getContext("2d") 19 | chart = new Chart(context).Doughnut(data, options) -------------------------------------------------------------------------------- /app/assets/javascripts/auth.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/students.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | 5 | #= require chartjs 6 | 7 | $ -> 8 | 9 | options = { 10 | animationEasing: 'easeOutCubic' 11 | responsive: true 12 | showTooltips: true 13 | } 14 | if (elem = $("#students-chart").get(0)) 15 | $.getJSON "/students/stats.json", null, (data)-> 16 | context = elem.getContext("2d") 17 | chart = new Chart(context).Doughnut(data, options) 18 | -------------------------------------------------------------------------------- /app/assets/javascripts/submissions.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-variables"; 2 | @import "bootstrap-sprockets"; 3 | @import "bootstrap"; 4 | @import "bootstrap-generators"; 5 | @import "icomoon-free/style"; 6 | 7 | 8 | #sidebar { 9 | background: $brand-primary; 10 | color: white; 11 | } 12 | 13 | h1 { 14 | text-transform: uppercase; 15 | font-weight: 300; 16 | } 17 | 18 | .col-user { 19 | @extend .text-right; 20 | a, a:hover { 21 | text-decoration: none; 22 | } 23 | } 24 | 25 | .text-big { 26 | font-size: 200%; 27 | } 28 | 29 | #footer { 30 | background-color: $gray-lighter; 31 | text-align: center; 32 | padding-top: 20px; 33 | } 34 | 35 | /* Panels! */ 36 | // Apply the mixin to the panel footers, too! 37 | .panel-success > .panel-footer { 38 | color: $panel-success-text; 39 | background-color: $panel-success-heading-bg; 40 | border-color: $panel-success-border; 41 | } 42 | 43 | 44 | #students-chart-wrapper { 45 | max-width: 50%; 46 | max-height: 400px; 47 | } 48 | 49 | .assignment-index-chart-wrapper { 50 | max-width: 120px; 51 | } 52 | 53 | .row.with-padding { 54 | margin-top: 32px; 55 | } 56 | 57 | 58 | 59 | /* Sticky footer styles 60 | -------------------------------------------------- */ 61 | 62 | html, 63 | body { 64 | height: 100%; 65 | /* The html and body elements cannot have any padding or margin. */ 66 | } 67 | 68 | $footer-height: 60px; 69 | 70 | /* Wrapper for page content to push down footer */ 71 | #wrap { 72 | min-height: 100%; 73 | height: auto !important; 74 | height: 100%; 75 | /* Negative indent footer by it's height */ 76 | margin: 0 0 0 -$footer-height; 77 | padding-bottom: $footer-height + 32px; 78 | } 79 | 80 | /* Set the fixed height of the footer here */ 81 | #footer { 82 | height: $footer-height; 83 | } 84 | #footer { 85 | background-color: #f5f5f5; 86 | } 87 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap-generators.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-variables"; 2 | @import "bootstrap.scss"; 3 | 4 | .page-header { 5 | a.btn { 6 | float: right; 7 | } 8 | 9 | a.btn + a.btn { 10 | margin-right: 8px; 11 | } 12 | } 13 | 14 | input[type="radio"], input[type="checkbox"] { 15 | width: initial; 16 | height: initial; 17 | margin-top: 7px; 18 | } 19 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap-variables.scss: -------------------------------------------------------------------------------- 1 | // a flag to toggle asset pipeline / compass integration 2 | // defaults to true if twbs-font-path function is present (no function => twbs-font-path('') parsed as string == right side) 3 | // in Sass 3.3 this can be improved with: function-exists(twbs-font-path) 4 | $bootstrap-sass-asset-helper: (twbs-font-path("") != unquote('twbs-font-path("")')) !default; 5 | // 6 | // Variables 7 | // -------------------------------------------------- 8 | 9 | 10 | //== Colors 11 | // 12 | //## Gray and brand colors for use across Bootstrap. 13 | 14 | $gray-darker: lighten(#000, 13.5%) !default; // #222 15 | $gray-dark: lighten(#000, 20%) !default; // #333 16 | $gray: lighten(#000, 33.5%) !default; // #555 17 | $gray-light: lighten(#000, 60%) !default; // #999 18 | $gray-lighter: lighten(#000, 93.5%) !default; // #eee 19 | 20 | 21 | $brand-primary: #428bca !default; 22 | $brand-success: #8DBF67 !default; 23 | $brand-info: #17A3A5 !default; 24 | $brand-warning: #f0ad4e !default; 25 | $brand-danger: #d9534f !default; 26 | 27 | 28 | //== Scaffolding 29 | // 30 | // ## Settings for some of the most global styles. 31 | 32 | //** Background color for `
`. 33 | $body-bg: #fff !default; 34 | //** Global text color on ``. 35 | $text-color: $gray-dark !default; 36 | 37 | //** Global textual link color. 38 | $link-color: $brand-primary !default; 39 | //** Link hover color set via `darken()` function. 40 | $link-hover-color: darken($link-color, 15%) !default; 41 | 42 | 43 | //== Typography 44 | // 45 | //## Font, line-height, and color for body text, headings, and more. 46 | 47 | $font-family-sans-serif: "Open Sans", Helvetica, Arial, sans-serif !default; 48 | $font-family-serif: Georgia, "Times New Roman", Times, serif !default; 49 | //** Default monospace fonts for ``, ``, and ``.
50 | $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace !default;
51 | $font-family-base: $font-family-sans-serif !default;
52 |
53 | $font-size-base: 14px !default;
54 | $font-size-large: ceil(($font-size-base * 1.25)) !default; // ~18px
55 | $font-size-small: ceil(($font-size-base * 0.85)) !default; // ~12px
56 |
57 | $font-size-h1: floor(($font-size-base * 2.6)) !default; // ~36px
58 | $font-size-h2: floor(($font-size-base * 2.15)) !default; // ~30px
59 | $font-size-h3: ceil(($font-size-base * 1.7)) !default; // ~24px
60 | $font-size-h4: ceil(($font-size-base * 1.25)) !default; // ~18px
61 | $font-size-h5: $font-size-base !default;
62 | $font-size-h6: ceil(($font-size-base * 0.85)) !default; // ~12px
63 |
64 | //** Unit-less `line-height` for use in components like buttons.
65 | $line-height-base: 1.428571429 !default; // 20/14
66 | //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
67 | $line-height-computed: floor(($font-size-base * $line-height-base)) !default; // ~20px
68 |
69 | //** By default, this inherits from the ``.
70 | $headings-font-family: lato, sans-serif !default;
71 | $headings-font-weight: 500 !default;
72 | $headings-line-height: 1.1 !default;
73 | $headings-color: inherit !default;
74 |
75 |
76 | //-- Iconography
77 | //
78 | //## Specify custom locations of the include Glyphicons icon font. Useful for those including Bootstrap via Bower.
79 |
80 | $icon-font-path: "bootstrap/" !default;
81 | $icon-font-name: "glyphicons-halflings-regular" !default;
82 | $icon-font-svg-id: "glyphicons_halflingsregular" !default;
83 |
84 | //== Components
85 | //
86 | //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
87 |
88 | $padding-base-vertical: 6px !default;
89 | $padding-base-horizontal: 12px !default;
90 |
91 | $padding-large-vertical: 10px !default;
92 | $padding-large-horizontal: 16px !default;
93 |
94 | $padding-small-vertical: 5px !default;
95 | $padding-small-horizontal: 10px !default;
96 |
97 | $padding-xs-vertical: 1px !default;
98 | $padding-xs-horizontal: 5px !default;
99 |
100 | $line-height-large: 1.33 !default;
101 | $line-height-small: 1.5 !default;
102 |
103 | $border-radius-base: 4px !default;
104 | $border-radius-large: 6px !default;
105 | $border-radius-small: 3px !default;
106 |
107 | //** Global color for active items (e.g., navs or dropdowns).
108 | $component-active-color: #fff !default;
109 | //** Global background color for active items (e.g., navs or dropdowns).
110 | $component-active-bg: $brand-primary !default;
111 |
112 | //** Width of the `border` for generating carets that indicator dropdowns.
113 | $caret-width-base: 4px !default;
114 | //** Carets increase slightly in size for larger components.
115 | $caret-width-large: 5px !default;
116 |
117 |
118 | //== Tables
119 | //
120 | //## Customizes the `.table` component with basic values, each used across all table variations.
121 |
122 | //** Padding for ``s and ` `s.
123 | $table-cell-padding: 8px !default;
124 | //** Padding for cells in `.table-condensed`.
125 | $table-condensed-cell-padding: 5px !default;
126 |
127 | //** Default background color used for all tables.
128 | $table-bg: transparent !default;
129 | //** Background color used for `.table-striped`.
130 | $table-bg-accent: #f9f9f9 !default;
131 | //** Background color used for `.table-hover`.
132 | $table-bg-hover: #f5f5f5 !default;
133 | $table-bg-active: $table-bg-hover !default;
134 |
135 | //** Border color for table and cell borders.
136 | $table-border-color: #ddd !default;
137 |
138 |
139 | //== Buttons
140 | //
141 | //## For each of Bootstrap's buttons, define text, background and border color.
142 |
143 | $btn-font-weight: normal !default;
144 |
145 | $btn-default-color: #333 !default;
146 | $btn-default-bg: #fff !default;
147 | $btn-default-border: #ccc !default;
148 |
149 | $btn-primary-color: #fff !default;
150 | $btn-primary-bg: $brand-primary !default;
151 | $btn-primary-border: darken($btn-primary-bg, 5%) !default;
152 |
153 | $btn-success-color: #fff !default;
154 | $btn-success-bg: $brand-success !default;
155 | $btn-success-border: darken($btn-success-bg, 5%) !default;
156 |
157 | $btn-info-color: #fff !default;
158 | $btn-info-bg: $brand-info !default;
159 | $btn-info-border: darken($btn-info-bg, 5%) !default;
160 |
161 | $btn-warning-color: #fff !default;
162 | $btn-warning-bg: $brand-warning !default;
163 | $btn-warning-border: darken($btn-warning-bg, 5%) !default;
164 |
165 | $btn-danger-color: #fff !default;
166 | $btn-danger-bg: $brand-danger !default;
167 | $btn-danger-border: darken($btn-danger-bg, 5%) !default;
168 |
169 | $btn-link-disabled-color: $gray-light !default;
170 |
171 |
172 | //== Forms
173 | //
174 | //##
175 |
176 | //** `` background color
177 | $input-bg: #fff !default;
178 | //** `` background color
179 | $input-bg-disabled: $gray-lighter !default;
180 |
181 | //** Text color for ``s
182 | $input-color: $gray !default;
183 | //** `` border color
184 | $input-border: #ccc !default;
185 | //** `` border radius
186 | $input-border-radius: $border-radius-base !default;
187 | //** Border color for inputs on focus
188 | $input-border-focus: #66afe9 !default;
189 |
190 | //** Placeholder text color
191 | $input-color-placeholder: $gray-light !default;
192 |
193 | //** Default `.form-control` height
194 | $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
195 | //** Large `.form-control` height
196 | $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
197 | //** Small `.form-control` height
198 | $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
199 |
200 | $legend-color: $gray-dark !default;
201 | $legend-border-color: #e5e5e5 !default;
202 |
203 | //** Background color for textual input addons
204 | $input-group-addon-bg: $gray-lighter !default;
205 | //** Border color for textual input addons
206 | $input-group-addon-border-color: $input-border !default;
207 |
208 |
209 | //== Dropdowns
210 | //
211 | //## Dropdown menu container and contents.
212 |
213 | //** Background for the dropdown menu.
214 | $dropdown-bg: #fff !default;
215 | //** Dropdown menu `border-color`.
216 | $dropdown-border: rgba(0,0,0,.15) !default;
217 | //** Dropdown menu `border-color` **for IE8**.
218 | $dropdown-fallback-border: #ccc !default;
219 | //** Divider color for between dropdown items.
220 | $dropdown-divider-bg: #e5e5e5 !default;
221 |
222 | //** Dropdown link text color.
223 | $dropdown-link-color: $gray-dark !default;
224 | //** Hover color for dropdown links.
225 | $dropdown-link-hover-color: darken($gray-dark, 5%) !default;
226 | //** Hover background for dropdown links.
227 | $dropdown-link-hover-bg: #f5f5f5 !default;
228 |
229 | //** Active dropdown menu item text color.
230 | $dropdown-link-active-color: $component-active-color !default;
231 | //** Active dropdown menu item background color.
232 | $dropdown-link-active-bg: $component-active-bg !default;
233 |
234 | //** Disabled dropdown menu item background color.
235 | $dropdown-link-disabled-color: $gray-light !default;
236 |
237 | //** Text color for headers within dropdown menus.
238 | $dropdown-header-color: $gray-light !default;
239 |
240 | // Note: Deprecated $dropdown-caret-color as of v3.1.0
241 | $dropdown-caret-color: #000 !default;
242 |
243 |
244 | //-- Z-index master list
245 | //
246 | // Warning: Avoid customizing these values. They're used for a bird's eye view
247 | // of components dependent on the z-axis and are designed to all work together.
248 | //
249 | // Note: These variables are not generated into the Customizer.
250 |
251 | $zindex-navbar: 1000 !default;
252 | $zindex-dropdown: 1000 !default;
253 | $zindex-popover: 1010 !default;
254 | $zindex-tooltip: 1030 !default;
255 | $zindex-navbar-fixed: 1030 !default;
256 | $zindex-modal-background: 1040 !default;
257 | $zindex-modal: 1050 !default;
258 |
259 |
260 | //== Media queries breakpoints
261 | //
262 | //## Define the breakpoints at which your layout will change, adapting to different screen sizes.
263 |
264 | // Extra small screen / phone
265 | // Note: Deprecated $screen-xs and $screen-phone as of v3.0.1
266 | $screen-xs: 480px !default;
267 | $screen-xs-min: $screen-xs !default;
268 | $screen-phone: $screen-xs-min !default;
269 |
270 | // Small screen / tablet
271 | // Note: Deprecated $screen-sm and $screen-tablet as of v3.0.1
272 | $screen-sm: 768px !default;
273 | $screen-sm-min: $screen-sm !default;
274 | $screen-tablet: $screen-sm-min !default;
275 |
276 | // Medium screen / desktop
277 | // Note: Deprecated $screen-md and $screen-desktop as of v3.0.1
278 | $screen-md: 992px !default;
279 | $screen-md-min: $screen-md !default;
280 | $screen-desktop: $screen-md-min !default;
281 |
282 | // Large screen / wide desktop
283 | // Note: Deprecated $screen-lg and $screen-lg-desktop as of v3.0.1
284 | $screen-lg: 1200px !default;
285 | $screen-lg-min: $screen-lg !default;
286 | $screen-lg-desktop: $screen-lg-min !default;
287 |
288 | // So media queries don't overlap when required, provide a maximum
289 | $screen-xs-max: ($screen-sm-min - 1) !default;
290 | $screen-sm-max: ($screen-md-min - 1) !default;
291 | $screen-md-max: ($screen-lg-min - 1) !default;
292 |
293 |
294 | //== Grid system
295 | //
296 | //## Define your custom responsive grid.
297 |
298 | //** Number of columns in the grid.
299 | $grid-columns: 12 !default;
300 | //** Padding between columns. Gets divided in half for the left and right.
301 | $grid-gutter-width: 30px !default;
302 | // Navbar collapse
303 | //** Point at which the navbar becomes uncollapsed.
304 | $grid-float-breakpoint: $screen-sm-min !default;
305 | //** Point at which the navbar begins collapsing.
306 | $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
307 |
308 |
309 | //== Container sizes
310 | //
311 | //## Define the maximum width of `.container` for different screen sizes.
312 |
313 | // Small screen / tablet
314 | $container-tablet: ((720px + $grid-gutter-width)) !default;
315 | //** For `$screen-sm-min` and up.
316 | $container-sm: $container-tablet !default;
317 |
318 | // Medium screen / desktop
319 | $container-desktop: ((720px + $grid-gutter-width)) !default;
320 | //** For `$screen-md-min` and up.
321 | $container-md: $container-desktop !default;
322 |
323 | // Large screen / wide desktop
324 | $container-large-desktop: ((720px + $grid-gutter-width)) !default;
325 | //** For `$screen-lg-min` and up.
326 | $container-lg: $container-large-desktop !default;
327 |
328 |
329 | //== Navbar
330 | //
331 | //##
332 |
333 | // Basics of a navbar
334 | $navbar-height: 50px !default;
335 | $navbar-margin-bottom: $line-height-computed !default;
336 | $navbar-border-radius: $border-radius-base !default;
337 | $navbar-padding-horizontal: floor(($grid-gutter-width / 2)) !default;
338 | $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) !default;
339 | $navbar-collapse-max-height: 340px !default;
340 |
341 | $navbar-default-color: #777 !default;
342 | $navbar-default-bg: #f8f8f8 !default;
343 | $navbar-default-border: darken($navbar-default-bg, 6.5%) !default;
344 |
345 | // Navbar links
346 | $navbar-default-link-color: #777 !default;
347 | $navbar-default-link-hover-color: #333 !default;
348 | $navbar-default-link-hover-bg: transparent !default;
349 | $navbar-default-link-active-color: #555 !default;
350 | $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%) !default;
351 | $navbar-default-link-disabled-color: #ccc !default;
352 | $navbar-default-link-disabled-bg: transparent !default;
353 |
354 | // Navbar brand label
355 | $navbar-default-brand-color: $navbar-default-link-color !default;
356 | $navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%) !default;
357 | $navbar-default-brand-hover-bg: transparent !default;
358 |
359 | // Navbar toggle
360 | $navbar-default-toggle-hover-bg: #ddd !default;
361 | $navbar-default-toggle-icon-bar-bg: #888 !default;
362 | $navbar-default-toggle-border-color: #ddd !default;
363 |
364 |
365 | // Inverted navbar
366 | // Reset inverted navbar basics
367 | $navbar-inverse-color: $gray-light !default;
368 | $navbar-inverse-bg: #222 !default;
369 | $navbar-inverse-border: darken($navbar-inverse-bg, 10%) !default;
370 |
371 | // Inverted navbar links
372 | $navbar-inverse-link-color: $gray-light !default;
373 | $navbar-inverse-link-hover-color: #fff !default;
374 | $navbar-inverse-link-hover-bg: transparent !default;
375 | $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color !default;
376 | $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%) !default;
377 | $navbar-inverse-link-disabled-color: #444 !default;
378 | $navbar-inverse-link-disabled-bg: transparent !default;
379 |
380 | // Inverted navbar brand label
381 | $navbar-inverse-brand-color: $navbar-inverse-link-color !default;
382 | $navbar-inverse-brand-hover-color: #fff !default;
383 | $navbar-inverse-brand-hover-bg: transparent !default;
384 |
385 | // Inverted navbar toggle
386 | $navbar-inverse-toggle-hover-bg: #333 !default;
387 | $navbar-inverse-toggle-icon-bar-bg: #fff !default;
388 | $navbar-inverse-toggle-border-color: #333 !default;
389 |
390 |
391 | //== Navs
392 | //
393 | //##
394 |
395 | //=== Shared nav styles
396 | $nav-link-padding: 10px 15px !default;
397 | $nav-link-hover-bg: $gray-lighter !default;
398 |
399 | $nav-disabled-link-color: $gray-light !default;
400 | $nav-disabled-link-hover-color: $gray-light !default;
401 |
402 | $nav-open-link-hover-color: #fff !default;
403 |
404 | //== Tabs
405 | $nav-tabs-border-color: #ddd !default;
406 |
407 | $nav-tabs-link-hover-border-color: $gray-lighter !default;
408 |
409 | $nav-tabs-active-link-hover-bg: $body-bg !default;
410 | $nav-tabs-active-link-hover-color: $gray !default;
411 | $nav-tabs-active-link-hover-border-color: #ddd !default;
412 |
413 | $nav-tabs-justified-link-border-color: #ddd !default;
414 | $nav-tabs-justified-active-link-border-color: $body-bg !default;
415 |
416 | //== Pills
417 | $nav-pills-border-radius: $border-radius-base !default;
418 | $nav-pills-active-link-hover-bg: $component-active-bg !default;
419 | $nav-pills-active-link-hover-color: $component-active-color !default;
420 |
421 |
422 | //== Pagination
423 | //
424 | //##
425 |
426 | $pagination-color: $link-color !default;
427 | $pagination-bg: #fff !default;
428 | $pagination-border: #ddd !default;
429 |
430 | $pagination-hover-color: $link-hover-color !default;
431 | $pagination-hover-bg: $gray-lighter !default;
432 | $pagination-hover-border: #ddd !default;
433 |
434 | $pagination-active-color: #fff !default;
435 | $pagination-active-bg: $brand-primary !default;
436 | $pagination-active-border: $brand-primary !default;
437 |
438 | $pagination-disabled-color: $gray-light !default;
439 | $pagination-disabled-bg: #fff !default;
440 | $pagination-disabled-border: #ddd !default;
441 |
442 |
443 | //== Pager
444 | //
445 | //##
446 |
447 | $pager-bg: $pagination-bg !default;
448 | $pager-border: $pagination-border !default;
449 | $pager-border-radius: 15px !default;
450 |
451 | $pager-hover-bg: $pagination-hover-bg !default;
452 |
453 | $pager-active-bg: $pagination-active-bg !default;
454 | $pager-active-color: $pagination-active-color !default;
455 |
456 | $pager-disabled-color: $pagination-disabled-color !default;
457 |
458 |
459 | //== Jumbotron
460 | //
461 | //##
462 |
463 | $jumbotron-padding: 30px !default;
464 | $jumbotron-color: inherit !default;
465 | $jumbotron-bg: $gray-lighter !default;
466 | $jumbotron-heading-color: inherit !default;
467 | $jumbotron-font-size: ceil(($font-size-base * 1.5)) !default;
468 |
469 |
470 | //== Form states and alerts
471 | //
472 | //## Define colors for form feedback states and, by default, alerts.
473 |
474 | $state-success-text: #3c763d !default;
475 | $state-success-bg: #dff0d8 !default;
476 | $state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) !default;
477 |
478 | $state-info-text: #31708f !default;
479 | $state-info-bg: #d9edf7 !default;
480 | $state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) !default;
481 |
482 | $state-warning-text: #8a6d3b !default;
483 | $state-warning-bg: #fcf8e3 !default;
484 | $state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) !default;
485 |
486 | $state-danger-text: #a94442 !default;
487 | $state-danger-bg: #f2dede !default;
488 | $state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) !default;
489 |
490 |
491 | //== Tooltips
492 | //
493 | //##
494 |
495 | //** Tooltip max width
496 | $tooltip-max-width: 200px !default;
497 | //** Tooltip text color
498 | $tooltip-color: #fff !default;
499 | //** Tooltip background color
500 | $tooltip-bg: #000 !default;
501 | $tooltip-opacity: .9 !default;
502 |
503 | //** Tooltip arrow width
504 | $tooltip-arrow-width: 5px !default;
505 | //** Tooltip arrow color
506 | $tooltip-arrow-color: $tooltip-bg !default;
507 |
508 |
509 | //== Popovers
510 | //
511 | //##
512 |
513 | //** Popover body background color
514 | $popover-bg: #fff !default;
515 | //** Popover maximum width
516 | $popover-max-width: 276px !default;
517 | //** Popover border color
518 | $popover-border-color: rgba(0,0,0,.2) !default;
519 | //** Popover fallback border color
520 | $popover-fallback-border-color: #ccc !default;
521 |
522 | //** Popover title background color
523 | $popover-title-bg: darken($popover-bg, 3%) !default;
524 |
525 | //** Popover arrow width
526 | $popover-arrow-width: 10px !default;
527 | //** Popover arrow color
528 | $popover-arrow-color: #fff !default;
529 |
530 | //** Popover outer arrow width
531 | $popover-arrow-outer-width: ($popover-arrow-width + 1) !default;
532 | //** Popover outer arrow color
533 | $popover-arrow-outer-color: fadein($popover-border-color, 5%) !default;
534 | //** Popover outer arrow fallback color
535 | $popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%) !default;
536 |
537 |
538 | //== Labels
539 | //
540 | //##
541 |
542 | //** Default label background color
543 | $label-default-bg: $gray-light !default;
544 | //** Primary label background color
545 | $label-primary-bg: $brand-primary !default;
546 | //** Success label background color
547 | $label-success-bg: $brand-success !default;
548 | //** Info label background color
549 | $label-info-bg: $brand-info !default;
550 | //** Warning label background color
551 | $label-warning-bg: $brand-warning !default;
552 | //** Danger label background color
553 | $label-danger-bg: $brand-danger !default;
554 |
555 | //** Default label text color
556 | $label-color: #fff !default;
557 | //** Default text color of a linked label
558 | $label-link-hover-color: #fff !default;
559 |
560 |
561 | //== Modals
562 | //
563 | //##
564 |
565 | //** Padding applied to the modal body
566 | $modal-inner-padding: 20px !default;
567 |
568 | //** Padding applied to the modal title
569 | $modal-title-padding: 15px !default;
570 | //** Modal title line-height
571 | $modal-title-line-height: $line-height-base !default;
572 |
573 | //** Background color of modal content area
574 | $modal-content-bg: #fff !default;
575 | //** Modal content border color
576 | $modal-content-border-color: rgba(0,0,0,.2) !default;
577 | //** Modal content border color **for IE8**
578 | $modal-content-fallback-border-color: #999 !default;
579 |
580 | //** Modal backdrop background color
581 | $modal-backdrop-bg: #000 !default;
582 | //** Modal backdrop opacity
583 | $modal-backdrop-opacity: .5 !default;
584 | //** Modal header border color
585 | $modal-header-border-color: #e5e5e5 !default;
586 | //** Modal footer border color
587 | $modal-footer-border-color: $modal-header-border-color !default;
588 |
589 | $modal-lg: 900px !default;
590 | $modal-md: 600px !default;
591 | $modal-sm: 300px !default;
592 |
593 |
594 | //== Alerts
595 | //
596 | //## Define alert colors, border radius, and padding.
597 |
598 | $alert-padding: 15px !default;
599 | $alert-border-radius: $border-radius-base !default;
600 | $alert-link-font-weight: bold !default;
601 |
602 | $alert-success-bg: $state-success-bg !default;
603 | $alert-success-text: $state-success-text !default;
604 | $alert-success-border: $state-success-border !default;
605 |
606 | $alert-info-bg: $state-info-bg !default;
607 | $alert-info-text: $state-info-text !default;
608 | $alert-info-border: $state-info-border !default;
609 |
610 | $alert-warning-bg: $state-warning-bg !default;
611 | $alert-warning-text: $state-warning-text !default;
612 | $alert-warning-border: $state-warning-border !default;
613 |
614 | $alert-danger-bg: $state-danger-bg !default;
615 | $alert-danger-text: $state-danger-text !default;
616 | $alert-danger-border: $state-danger-border !default;
617 |
618 |
619 | //== Progress bars
620 | //
621 | //##
622 |
623 | //** Background color of the whole progress component
624 | $progress-bg: #f5f5f5 !default;
625 | //** Progress bar text color
626 | $progress-bar-color: #fff !default;
627 |
628 | //** Default progress bar color
629 | $progress-bar-bg: $brand-primary !default;
630 | //** Success progress bar color
631 | $progress-bar-success-bg: $brand-success !default;
632 | //** Warning progress bar color
633 | $progress-bar-warning-bg: $brand-warning !default;
634 | //** Danger progress bar color
635 | $progress-bar-danger-bg: $brand-danger !default;
636 | //** Info progress bar color
637 | $progress-bar-info-bg: $brand-info !default;
638 |
639 |
640 | //== List group
641 | //
642 | //##
643 |
644 | //** Background color on `.list-group-item`
645 | $list-group-bg: #fff !default;
646 | //** `.list-group-item` border color
647 | $list-group-border: #ddd !default;
648 | //** List group border radius
649 | $list-group-border-radius: $border-radius-base !default;
650 |
651 | //** Background color of single list elements on hover
652 | $list-group-hover-bg: #f5f5f5 !default;
653 | //** Text color of active list elements
654 | $list-group-active-color: $component-active-color !default;
655 | //** Background color of active list elements
656 | $list-group-active-bg: $component-active-bg !default;
657 | //** Border color of active list elements
658 | $list-group-active-border: $list-group-active-bg !default;
659 | $list-group-active-text-color: lighten($list-group-active-bg, 40%) !default;
660 |
661 | $list-group-link-color: #555 !default;
662 | $list-group-link-heading-color: #333 !default;
663 |
664 |
665 | //== Panels
666 | //
667 | //##
668 |
669 | $panel-bg: #fff !default;
670 | $panel-body-padding: 15px !default;
671 | $panel-border-radius: $border-radius-base !default;
672 |
673 | //** Border color for elements within panels
674 | $panel-inner-border: #ddd !default;
675 | $panel-footer-bg: #f5f5f5 !default;
676 |
677 | $panel-default-text: $gray-dark !default;
678 | $panel-default-border: #ddd !default;
679 | $panel-default-heading-bg: #f5f5f5 !default;
680 |
681 | $panel-primary-text: #fff !default;
682 | $panel-primary-border: $brand-primary !default;
683 | $panel-primary-heading-bg: $brand-primary !default;
684 |
685 | $panel-success-text: $state-success-text !default;
686 | $panel-success-border: $state-success-border !default;
687 | $panel-success-heading-bg: $state-success-bg !default;
688 |
689 | $panel-info-text: $state-info-text !default;
690 | $panel-info-border: $state-info-border !default;
691 | $panel-info-heading-bg: $state-info-bg !default;
692 |
693 | $panel-warning-text: $state-warning-text !default;
694 | $panel-warning-border: $state-warning-border !default;
695 | $panel-warning-heading-bg: $state-warning-bg !default;
696 |
697 | $panel-danger-text: $state-danger-text !default;
698 | $panel-danger-border: $state-danger-border !default;
699 | $panel-danger-heading-bg: $state-danger-bg !default;
700 |
701 |
702 | //== Thumbnails
703 | //
704 | //##
705 |
706 | //** Padding around the thumbnail image
707 | $thumbnail-padding: 4px !default;
708 | //** Thumbnail background color
709 | $thumbnail-bg: $body-bg !default;
710 | //** Thumbnail border color
711 | $thumbnail-border: #ddd !default;
712 | //** Thumbnail border radius
713 | $thumbnail-border-radius: $border-radius-base !default;
714 |
715 | //** Custom text color for thumbnail captions
716 | $thumbnail-caption-color: $text-color !default;
717 | //** Padding around the thumbnail caption
718 | $thumbnail-caption-padding: 9px !default;
719 |
720 |
721 | //== Wells
722 | //
723 | //##
724 |
725 | $well-bg: #f5f5f5 !default;
726 | $well-border: darken($well-bg, 7%) !default;
727 |
728 |
729 | //== Badges
730 | //
731 | //##
732 |
733 | $badge-color: #fff !default;
734 | //** Linked badge text color on hover
735 | $badge-link-hover-color: #fff !default;
736 | $badge-bg: $gray-light !default;
737 |
738 | //** Badge text color in active nav link
739 | $badge-active-color: $link-color !default;
740 | //** Badge background color in active nav link
741 | $badge-active-bg: #fff !default;
742 |
743 | $badge-font-weight: bold !default;
744 | $badge-line-height: 1 !default;
745 | $badge-border-radius: 10px !default;
746 |
747 |
748 | //== Breadcrumbs
749 | //
750 | //##
751 |
752 | $breadcrumb-padding-vertical: 8px !default;
753 | $breadcrumb-padding-horizontal: 15px !default;
754 | //** Breadcrumb background color
755 | $breadcrumb-bg: #f5f5f5 !default;
756 | //** Breadcrumb text color
757 | $breadcrumb-color: #ccc !default;
758 | //** Text color of current page in the breadcrumb
759 | $breadcrumb-active-color: $gray-light !default;
760 | //** Textual separator for between breadcrumb elements
761 | $breadcrumb-separator: "/" !default;
762 |
763 |
764 | //== Carousel
765 | //
766 | //##
767 |
768 | $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6) !default;
769 |
770 | $carousel-control-color: #fff !default;
771 | $carousel-control-width: 15% !default;
772 | $carousel-control-opacity: .5 !default;
773 | $carousel-control-font-size: 20px !default;
774 |
775 | $carousel-indicator-active-bg: #fff !default;
776 | $carousel-indicator-border-color: #fff !default;
777 |
778 | $carousel-caption-color: #fff !default;
779 |
780 |
781 | //== Close
782 | //
783 | //##
784 |
785 | $close-font-weight: bold !default;
786 | $close-color: #000 !default;
787 | $close-text-shadow: 0 1px 0 #fff !default;
788 |
789 |
790 | //== Code
791 | //
792 | //##
793 |
794 | $code-color: #c7254e !default;
795 | $code-bg: #f9f2f4 !default;
796 |
797 | $kbd-color: #fff !default;
798 | $kbd-bg: #333 !default;
799 |
800 | $pre-bg: #f5f5f5 !default;
801 | $pre-color: $gray-dark !default;
802 | $pre-border-color: #ccc !default;
803 | $pre-scrollable-max-height: 340px !default;
804 |
805 |
806 | //== Type
807 | //
808 | //##
809 |
810 | //** Text muted color
811 | $text-muted: $gray-light !default;
812 | //** Abbreviations and acronyms border color
813 | $abbr-border-color: $gray-light !default;
814 | //** Headings small color
815 | $headings-small-color: $gray-light !default;
816 | //** Blockquote small color
817 | $blockquote-small-color: $gray-light !default;
818 | //** Blockquote font size
819 | $blockquote-font-size: ($font-size-base * 1.25) !default;
820 | //** Blockquote border color
821 | $blockquote-border-color: $gray-lighter !default;
822 | //** Page header border color
823 | $page-header-border-color: $gray-lighter !default;
824 |
825 |
826 | //== Miscellaneous
827 | //
828 | //##
829 |
830 | //** Horizontal line color.
831 | $hr-border: $gray-lighter !default;
832 |
833 | //** Horizontal offset for forms and lists.
834 | $component-offset-horizontal: 180px !default;
835 |
--------------------------------------------------------------------------------
/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 | def require_admin
7 | render "pages/401", :status => :unauthorized unless session[:admin] === true
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/controllers/assignments_controller.rb:
--------------------------------------------------------------------------------
1 | class AssignmentsController < ApplicationController
2 | before_action :set_assignment, only: [:show, :edit, :update, :destroy, :graph_data]
3 | before_action :require_admin
4 | # GET /assignments
5 | # GET /assignments.json
6 | def index
7 | @assignments = Assignment.all
8 | end
9 |
10 | # GET /assignments/1
11 | # GET /assignments/1.json
12 | def show
13 | end
14 |
15 | # GET /assignments/new
16 | def new
17 | @assignment = Assignment.new
18 | end
19 |
20 | # GET /assignments/1/edit
21 | def edit
22 | end
23 |
24 | # POST /assignments
25 | # POST /assignments.json
26 | def create
27 | @assignment = Assignment.new(assignment_params)
28 |
29 | respond_to do |format|
30 | if @assignment.save
31 | format.html { redirect_to @assignment, notice: 'Assignment was successfully created.' }
32 | format.json { render :show, status: :created, location: @assignment }
33 | else
34 | format.html { render :new }
35 | format.json { render json: @assignment.errors, status: :unprocessable_entity }
36 | end
37 | end
38 | end
39 |
40 | # PATCH/PUT /assignments/1
41 | # PATCH/PUT /assignments/1.json
42 | def update
43 | respond_to do |format|
44 | if @assignment.update(assignment_params)
45 | format.html { redirect_to @assignment, notice: 'Assignment was successfully updated.' }
46 | format.json { render :show, status: :ok, location: @assignment }
47 | else
48 | format.html { render :edit }
49 | format.json { render json: @assignment.errors, status: :unprocessable_entity }
50 | end
51 | end
52 | end
53 |
54 | # DELETE /assignments/1
55 | # DELETE /assignments/1.json
56 | def destroy
57 | @assignment.destroy
58 | respond_to do |format|
59 | format.html { redirect_to assignments_url, notice: 'Assignment was successfully destroyed.' }
60 | format.json { head :no_content }
61 | end
62 | end
63 |
64 | def graph_data
65 | count = @assignment.students_whose_latest_assignment_is_this().length
66 | graph_data = [
67 | {
68 | label: "this",
69 | value: count,
70 | color: "#2980b9"
71 | },
72 | {
73 | label: "other",
74 | value: Student.count - count,
75 | color: "#7f8c8d"
76 | }
77 | ]
78 | # graph_data = Submission.statuses.map do |status, status_value|
79 | # {
80 | # value: stats.select{|row| row.assignment_status == status_value}.count,
81 | # label: status.humanize,
82 | # color: Submission.color_for_status(status)
83 | # }
84 | # end
85 | # graph_data << {
86 | # value: Student.count - stats.size,
87 | # label: "not submitted",
88 | # color: "#fdae61"
89 | # }
90 | render json: graph_data
91 | end
92 |
93 | private
94 | # Use callbacks to share common setup or constraints between actions.
95 | def set_assignment
96 | @assignment = Assignment.find(params[:id])
97 | end
98 |
99 | # Never trust parameters from the scary internet, only allow the white list through.
100 | def assignment_params
101 | params.require(:assignment).permit(:title, :details, :tag_prefix, :grade)
102 | end
103 | end
104 |
--------------------------------------------------------------------------------
/app/controllers/auth_controller.rb:
--------------------------------------------------------------------------------
1 | class AuthController < ApplicationController
2 | def google_oauth2
3 | email = request.env["omniauth.auth"][:info][:email]
4 | session[:user] = email
5 | if ADMINS.include? email
6 | session[:admin] = true
7 | end
8 | redirect_to students_url
9 | end
10 |
11 | def logout
12 | reset_session
13 | redirect_to root_path
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/students_controller.rb:
--------------------------------------------------------------------------------
1 | class StudentsController < ApplicationController
2 | before_action :set_student, only: [:show, :edit, :update, :destroy]
3 | before_action :require_admin, except: [:show]
4 | before_action :sync_tags, only: [:show, :edit]
5 | before_action :sync_all_tags!, only: [:index]
6 |
7 | # GET /students
8 | # GET /students.json
9 | def index
10 | @students = Student.all.order(:last_name)
11 | end
12 |
13 | # GET /stats.json
14 | # Student progress
15 | def stats
16 | colors = ["#fdae61","#fee090","#e0f3f8","#abd9e9","#74add1","#4575b4"]
17 | stats = Assignment.all.order(:title).map.with_index do |assignment, index|
18 | c = colors[index % colors.size]
19 | {
20 | value: assignment.students_whose_latest_assignment_is_this.length,
21 | label: assignment.title,
22 | color: c
23 | }
24 | end
25 |
26 | stats.unshift({
27 | value: Student.count - stats.inject(0){|sum, stat| sum + stat[:value]},
28 | label: "None",
29 | color: "#eee"
30 | })
31 | count = {count: stats.inject(0){|sum, stat| sum + stat[:value]}}
32 | render json: stats
33 | end
34 |
35 | # GET /students/1
36 | # GET /students/1.json
37 | def show
38 | end
39 |
40 | # GET /students/new
41 | def new
42 | @student = Student.new
43 | end
44 |
45 | # GET /students/1/edit
46 | def edit
47 | end
48 |
49 | # POST /students
50 | # POST /students.json
51 | def create
52 | @student = Student.new(student_params)
53 |
54 | respond_to do |format|
55 | if @student.save
56 | format.html { redirect_to @student, notice: 'Student was successfully created.' }
57 | format.json { render :show, status: :created, location: @student }
58 | else
59 | format.html { render :new }
60 | format.json { render json: @student.errors, status: :unprocessable_entity }
61 | end
62 | end
63 | end
64 |
65 | # PATCH/PUT /students/1
66 | # PATCH/PUT /students/1.json
67 | def update
68 | respond_to do |format|
69 | if @student.update(student_params)
70 | format.html { redirect_to @student, notice: 'Profile was successfully updated.' }
71 | format.json { render :show, status: :ok, location: @student }
72 | else
73 | format.html { render :edit }
74 | format.json { render json: @student.errors, status: :unprocessable_entity }
75 | end
76 | end
77 | end
78 |
79 | # DELETE /students/1
80 | # DELETE /students/1.json
81 | def destroy
82 | @student.destroy
83 | respond_to do |format|
84 | format.html { redirect_to students_url, notice: 'Student was successfully destroyed.' }
85 | format.json { head :no_content }
86 | end
87 | end
88 |
89 | private
90 | # Use callbacks to share common setup or constraints between actions.
91 | def set_student
92 | @student = Student.find_by!(slug: params[:id])
93 | end
94 |
95 | # Never trust parameters from the scary internet, only allow the white list through.
96 | def student_params
97 | params.require(:student).permit(:first_name, :last_name, :github_username, :github_repo, :email)
98 | end
99 |
100 | def sync_tags
101 | @student.sync_tags(force_update: true) unless Assignment.submission_deadline_past?
102 | end
103 |
104 | def sync_all_tags!
105 | Student.sync_all_tags! unless Assignment.submission_deadline_past?
106 | end
107 | end
108 |
--------------------------------------------------------------------------------
/app/controllers/submissions_controller.rb:
--------------------------------------------------------------------------------
1 | class SubmissionsController < ApplicationController
2 | before_filter :load_student
3 | before_action :set_submission, only: [:show, :edit, :update, :destroy]
4 | before_action :require_admin
5 |
6 | # GET /submissions
7 | # GET /submissions.json
8 | def index
9 | # this is such an awful hack
10 | last_submissions = Student.all.map{|student| student.submissions.order(:tag).last }.compact
11 | last_not_graded_submissions = last_submissions.select{|s| s.status == "not_graded"}
12 | @submissions = last_not_graded_submissions.sort{|x, y| x.created_at <=> y.created_at}
13 | end
14 |
15 | # GET /submissions/1
16 | # GET /submissions/1.json
17 | def show
18 | end
19 |
20 | # GET /submissions/new
21 | def new
22 | @submission = @student.submissions.new
23 | end
24 |
25 | # GET /submissions/1/edit
26 | def edit
27 | end
28 |
29 | # POST /submissions
30 | # POST /submissions.json
31 | def create
32 | @submission = @student.submissions.new(submission_params)
33 |
34 | respond_to do |format|
35 | if @submission.save
36 | format.html { redirect_to @student, notice: 'Submission was successfully created.' }
37 | format.json { render :show, status: :created, location: @submission }
38 | else
39 | format.html { render :new }
40 | format.json { render json: @submission.errors, status: :unprocessable_entity }
41 | end
42 | end
43 | end
44 |
45 | # PATCH/PUT /submissions/1
46 | # PATCH/PUT /submissions/1.json
47 | def update
48 | puts "FEEDBACK #{submission_params['feedback']}"
49 | respond_to do |format|
50 | if @submission.update(submission_params)
51 | format.html { redirect_to @student, notice: 'Submission was successfully updated.' }
52 | format.json { render :show, status: :ok, location: @submission }
53 | else
54 | format.html { render :edit }
55 | format.json { render json: @submission.errors, status: :unprocessable_entity }
56 | end
57 | end
58 | end
59 |
60 | # DELETE /submissions/1
61 | # DELETE /submissions/1.json
62 | def destroy
63 | @submission.destroy
64 | respond_to do |format|
65 | format.html { redirect_to @student, notice: 'Submission was successfully destroyed.' }
66 | format.json { head :no_content }
67 | end
68 | end
69 |
70 | private
71 | # Use callbacks to share common setup or constraints between actions.
72 | def set_submission
73 | @submission = Submission.find(params[:id])
74 | end
75 |
76 | # Never trust parameters from the scary internet, only allow the white list through.
77 | def submission_params
78 | params.require(:submission).permit(:feedback, :tag, :assignment_id, :student_id, :status)
79 | end
80 |
81 | def load_student
82 | @student = Student.find_by(slug: params[:student_id])
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | def title(page_title)
3 | content_for :title, page_title.to_s
4 | end
5 |
6 | def admin?
7 | session[:admin] === true
8 | end
9 |
10 | end
11 |
--------------------------------------------------------------------------------
/app/helpers/assignments_helper.rb:
--------------------------------------------------------------------------------
1 | module AssignmentsHelper
2 | def assignment_status_class
3 |
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/app/helpers/auth_helper.rb:
--------------------------------------------------------------------------------
1 | module AuthHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/students_helper.rb:
--------------------------------------------------------------------------------
1 | module StudentsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/submissions_helper.rb:
--------------------------------------------------------------------------------
1 | module SubmissionsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/app/mailers/.keep
--------------------------------------------------------------------------------
/app/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/app/models/.keep
--------------------------------------------------------------------------------
/app/models/assignment.rb:
--------------------------------------------------------------------------------
1 | class Assignment < ActiveRecord::Base
2 | after_create :trigger_resync
3 | after_update :trigger_resync, if: :tag_prefix_changed?
4 |
5 | def to_s
6 | title
7 | end
8 |
9 | def trigger_resync
10 | Student.update_all(last_sync: nil)
11 | end
12 |
13 | def student_progress
14 | Student.assignment_status.where(submissions: {assignment: self})
15 | end
16 |
17 | def students_whose_latest_assignment_is_this
18 | Student.assignment_progress(self)
19 | end
20 |
21 | def self.submission_deadline_past?
22 | DateTime.new(2014, 11, 12, 0, 0, 0, '-8').past? # 00:00 Wednesday November 12 2014
23 | end
24 |
25 | def self.everything_is_graded?
26 | true # WOOOOOOOOO!
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/student.rb:
--------------------------------------------------------------------------------
1 | class Student < ActiveRecord::Base
2 | extend FriendlyId
3 | has_many :submissions
4 | friendly_id :random, use: [:slugged, :finders]
5 |
6 | def random
7 | s = ''
8 | loop do
9 | s = SecureRandom.hex(4)
10 | return s unless Student.exists?(s)
11 | end
12 | end
13 |
14 | def full_name
15 | first_name + " " + last_name
16 | end
17 |
18 | def github_url
19 | "https://github.com/"+github_username
20 | end
21 |
22 | def github_repo_url
23 | github_url+"/"+github_repo
24 | end
25 |
26 | def to_param
27 | slug
28 | end
29 |
30 | def to_s
31 | full_name
32 | end
33 |
34 | def assignment_progress
35 | submissions.completed.order(:tag).last.assignment rescue nil
36 | end
37 |
38 | def sync_tags(force_update: false, tags: nil)
39 | return unless github_username and github_repo #can't update a user's tags if their info isn't here
40 | return unless force_update or last_sync.nil? or last_sync < DateTime.now - 5.minutes
41 | tags ||= Octokit.tags(github_username+'/'+github_repo).map(&:name)
42 | puts tags
43 | Assignment.find_each do |assignment|
44 | assignment_tags = tags.select{|t| t.start_with? assignment.tag_prefix} unless assignment.tag_prefix.nil?
45 | puts assignment.to_s+" -> "+assignment_tags.join(", ")
46 | assignment_tags.each do |tag|
47 | submissions.find_or_create_by(tag: tag, assignment: assignment)
48 | end
49 | end
50 | update_attribute :last_sync, DateTime.now
51 | end
52 |
53 | def self.sync_all_tags!
54 | students = where.not(github_username: nil, github_repo: nil)
55 | data = Parallel.map(students, in_threads: 8) do |student|
56 | {student: student, tags: Octokit.tags(student.github_username+'/'+student.github_repo).map(&:name)}
57 | end
58 | data.each do |datum|
59 | datum[:student].sync_tags(tags: datum[:tags])
60 | end
61 | end
62 |
63 | def self.assignment_status
64 | Student.
65 | joins(submissions: :assignment).
66 | select('students.id, students.first_name, assignments.id AS assignment_id, assignments.title, MAX(submissions.status) AS assignment_status').
67 | order(['students.id', 'assignments.id']).
68 | group('assignments.id, students.id')
69 | end
70 |
71 | # get assignment progress for all students
72 | def self.assignment_progress(for_assignment = nil)
73 | query = Student.
74 | joins(submissions: :assignment).
75 | where('submissions.status = ?', Submission.statuses[:completed]).
76 | group('students.id').
77 | select('students.id, students.first_name, MAX(assignments.id) AS assignment_id')
78 |
79 | if for_assignment.nil?
80 | return query
81 | else
82 | return query.having('MAX(assignments.id) = ?', for_assignment.id)
83 | end
84 | end
85 |
86 | def self.where_assignment_progress(assignment)
87 | Student.
88 | joins(submissions: :assignment).
89 | where('submissions.status = ?', Submission.statuses[:completed]).
90 | group('students.id').
91 | select('students.*, MAX(assignments.id) AS assignment_id').
92 | having('MAX(assignments.id) = ?', assignment.id)
93 | end
94 | end
95 |
--------------------------------------------------------------------------------
/app/models/submission.rb:
--------------------------------------------------------------------------------
1 | class Submission < ActiveRecord::Base
2 | belongs_to :assignment
3 | belongs_to :student
4 |
5 | enum status: [ :not_graded, :incomplete, :completed ]
6 |
7 | validates :student, presence: true
8 |
9 | def github_url
10 |
11 | student.github_repo_url+"/releases/tag/"+tag
12 | end
13 |
14 | def to_s
15 | tag
16 | end
17 |
18 | def status_class
19 | {
20 | not_graded: 'default',
21 | incomplete: 'info',
22 | completed: 'success'
23 | }[status.to_sym]
24 | end
25 |
26 | def status_label
27 | "#{status.humanize}".html_safe
28 | end
29 |
30 | def self.color_for_status(status)
31 | {
32 | not_graded: '#999999',
33 | incomplete: '#17a3a5',
34 | completed: '#8dbf67'
35 | }.with_indifferent_access[status]
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/app/views/assignments/_form.html.haml:
--------------------------------------------------------------------------------
1 | = form_for @assignment, :html => { :class => "form-horizontal" } do |f|
2 | -if @assignment.errors.any?
3 | .alert.alert-danger.alert-dismissable
4 | %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} ×
5 | %h4= "#{pluralize(@assignment.errors.count, "error")} prohibited this assignment from being saved:"
6 |
7 | %ul
8 | - @assignment.errors.full_messages.each do |msg|
9 | %li= msg
10 |
11 | .form-group
12 | = f.label :title, :class => 'col-sm-2 control-label'
13 | .col-sm-10
14 | = f.text_field :title, :class => 'form-control'
15 | .form-group
16 | = f.label :tag_prefix, :class => 'col-sm-2 control-label'
17 | .col-sm-10
18 | = f.text_field :tag_prefix, :class => 'form-control'
19 | .form-group
20 | = f.label :details, :class => 'col-sm-2 control-label'
21 | .col-sm-10
22 | = f.text_field :details, :class => 'form-control'
23 | .form-group
24 | = f.label :grade, :class => 'col-sm-2 control-label'
25 | .col-sm-10
26 | = f.text_field :grade, :class => 'form-control'
27 | .form-group
28 | .col-sm-offset-2.col-sm-10
29 | = f.submit :class => 'btn btn-primary'
30 |
--------------------------------------------------------------------------------
/app/views/assignments/edit.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | = link_to assignments_path, :class => 'btn btn-default' do
3 | %span.glyphicon.glyphicon-list-alt
4 | Back
5 | = link_to @assignment, :class => 'btn btn-primary' do
6 | %span.glyphicon.glyphicon-info-sign
7 | Show
8 | %h1 Editing assignment
9 |
10 | = render 'form'
11 |
--------------------------------------------------------------------------------
/app/views/assignments/index.html.haml:
--------------------------------------------------------------------------------
1 | - title "Assignment Breakdown"
2 | .page-header
3 | = link_to new_assignment_path, :class => 'btn btn-primary' do
4 | %span.glyphicon.glyphicon-plus
5 | New Assignment
6 | %h1 Assignment Breakdown
7 |
8 | - Assignment.find_each do |assignment|
9 | - students = Student.where_assignment_progress(assignment)
10 | .row.with-padding
11 | .col-sm-3
12 | .center-block.text-center.assignment-index-chart-wrapper
13 | %canvas.assignment-chart{height: 100, width: 100, :'data-id' => assignment.id}
14 | .col-sm-9
15 | .panel.panel-default
16 | .panel-heading
17 | %h2.panel-title
18 | = students.length
19 | students have progressed to
20 | = link_to assignment.to_s, assignment
21 | %small.pull-right
22 | Grade:
23 | = assignment.grade
24 | - if students.length == 0
25 | .panel-body
26 | %em move along, nothing to see here
27 | .list-group
28 | - students.each do |student|
29 | %a.list-group-item{href: url_for(student)}= student.full_name
30 |
31 |
--------------------------------------------------------------------------------
/app/views/assignments/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array!(@assignments) do |assignment|
2 | json.extract! assignment, :id, :title, :details
3 | json.url assignment_url(assignment, format: :json)
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/assignments/new.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | = link_to assignments_path, :class => 'btn btn-default' do
3 | %span.glyphicon.glyphicon-list-alt
4 | Back
5 | %h1 New assignment
6 |
7 | = render 'form'
8 |
--------------------------------------------------------------------------------
/app/views/assignments/show.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | = link_to @assignment.details, class: 'btn btn-default' do
3 | %span.glyphicon.glyphicon-file
4 | Details
5 | = link_to assignments_path, :class => 'btn btn-default' do
6 | %span.glyphicon.glyphicon-list-alt
7 | Back
8 | = link_to edit_assignment_path(@assignment), :class => 'btn btn-primary' do
9 | %span.glyphicon.glyphicon-pencil
10 | Edit
11 | %h1= @assignment.title
12 |
13 | .row
14 | .col-sm-6
15 | .center-block.text-center
16 | %canvas.assignment-chart{height: 400, width: 400, :'data-id' => @assignment.id}
17 | .col-sm-6
18 | - students = Student.where_assignment_progress(@assignment)
19 | .panel.panel-default
20 | .panel-heading
21 | %h2.panel-title
22 | Students who have progressed to
23 | = link_to @assignment.to_s+":", @assignment
24 | - if students.length == 0
25 | .panel-body
26 | %p There are no students whose progress == this assignment
27 | .list-group
28 | - students.each do |student|
29 | %a.list-group-item{href: url_for(student)}= student.full_name
30 |
--------------------------------------------------------------------------------
/app/views/assignments/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! @assignment, :id, :title, :details, :created_at, :updated_at
2 |
--------------------------------------------------------------------------------
/app/views/layouts/_navbar.html.haml:
--------------------------------------------------------------------------------
1 | .navbar.navbar-default.navbar-static-top{:role => "navigation"}
2 | .container
3 | .navbar-header
4 | %button.navbar-toggle.collapsed{"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 | %a.navbar-brand{:href => "/"} Webdev Gradebook
10 | .navbar-collapse.collapse
11 | %ul.nav.navbar-nav
12 | %li
13 | %a{:href => "/"} Students
14 | %li
15 | %a{:href => "/assignments"} Assignments
16 | %li
17 | %a{:href => "/submissions"} Submissions
18 | %ul.nav.navbar-nav.navbar-right
19 | %li.dropdown
20 | %a.dropdown-toggle{"data-toggle" => "dropdown", :href => "#"}
21 | =session[:user]
22 | %span.caret
23 | %ul.dropdown-menu{:role => "menu"}
24 | %li
25 | %a{:href => "/logout"} Logout
26 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.haml:
--------------------------------------------------------------------------------
1 | !!!
2 | %html
3 | %head
4 | %meta{:charset => "utf-8"}
5 | %title= yield(:title)+" · webdev-gradebook".html_safe
6 | %meta{:content => "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no", :name => "viewport"}
7 | %meta{:content => "", :name => "description"}
8 | %meta{:content => "", :name => "author"}
9 |
10 | / Le HTML5 shim, for IE6-8 support of HTML5 elements
11 | /[if lt IE 9]
12 | = javascript_include_tag "https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js", "https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"
13 |
14 | = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
15 | = javascript_include_tag 'application', 'data-turbolinks-track' => true
16 | = csrf_meta_tags
17 | %body
18 | #wrap
19 | - if admin?
20 | = render partial: 'layouts/navbar'
21 | .container
22 | - flash.each do |name, msg|
23 | = content_tag :div, :class => "alert alert-#{name == :error ? "danger" : "success" } alert-dismissable" do
24 | %button.close{:type => "button", :data => {:dismiss => "alert"}, :aria => {:hidden => "true"} } ×
25 | = msg
26 | = yield
27 | - if admin?
28 | #footer
29 | .container
30 | %p.text-center
31 | Find a bug? Let me know.
32 | %script{:src => "//use.edgefonts.net/lato:n1,i1,n3,i3,n4,i4,n7,i7,n9,i9;open-sans:n3,i3,n4,i4,n6,i6,n7,i7,n8,i8.js"}
33 |
--------------------------------------------------------------------------------
/app/views/pages/401.html.haml:
--------------------------------------------------------------------------------
1 | - title "401 Unauthorized"
2 | .text-center
3 | .page-header
4 | %h1 401 Unauthorized
5 | - if session[:user]
6 | %p Sorry, you don't have permission to view this content
7 | %p
8 | %a.btn.btn-large.btn-default{href: '/logout'}
9 | Log out?
10 | - else
11 | %p Sorry, you'll need to sign in to go past this point
12 | %p
13 | %a.btn.btn-large.btn-primary{href: '/auth/google_oauth2/'}
14 | %span.icon-google
15 | Sign in with Google
16 |
--------------------------------------------------------------------------------
/app/views/students/_form.html.haml:
--------------------------------------------------------------------------------
1 | = form_for @student, :html => { :class => "form-horizontal" } do |f|
2 | -if @student.errors.any?
3 | .alert.alert-danger.alert-dismissable
4 | %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} ×
5 | %h4= "#{pluralize(@student.errors.count, "error")} prohibited this student from being saved:"
6 |
7 | %ul
8 | - @student.errors.full_messages.each do |msg|
9 | %li= msg
10 |
11 | .form-group
12 | = f.label :first_name, :class => 'col-sm-2 control-label'
13 | .col-sm-10
14 | = f.text_field :first_name, :class => 'form-control'
15 | .form-group
16 | = f.label :last_name, :class => 'col-sm-2 control-label'
17 | .col-sm-10
18 | = f.text_field :last_name, :class => 'form-control'
19 | .form-group
20 | = f.label :github_username, :class => 'col-sm-2 control-label'
21 | .col-sm-10
22 | = f.text_field :github_username, :class => 'form-control'
23 | .form-group
24 | = f.label :github_repo, :class => 'col-sm-2 control-label'
25 | .col-sm-10
26 | = f.text_field :github_repo, :class => 'form-control'
27 | .form-group
28 | = f.label :email, :class => 'col-sm-2 control-label'
29 | .col-sm-10
30 | = f.text_field :email, :class => 'form-control'
31 | .form-group
32 | .col-sm-offset-2.col-sm-10
33 | = f.submit :class => 'btn btn-primary'
34 |
--------------------------------------------------------------------------------
/app/views/students/edit.html.haml:
--------------------------------------------------------------------------------
1 | - title "#{@student.full_name} - Edit"
2 | .page-header
3 | = link_to @student, :class => 'btn btn-default' do
4 | %span.glyphicon.glyphicon-list-alt
5 | Back
6 | %h1 Editing student
7 |
8 | = render 'form'
9 |
--------------------------------------------------------------------------------
/app/views/students/index.html.haml:
--------------------------------------------------------------------------------
1 | - title "Student Index"
2 | .page-header
3 | %h1 Student Progress
4 | .center-block.text-center#students-chart-wrapper
5 | %canvas#students-chart{height: 400, width: 400}
6 | .page-header
7 | = link_to new_student_path, :class => 'btn btn-primary' do
8 | %span.glyphicon.glyphicon-plus
9 | New Student
10 | %h1 Listing students
11 | .list-group
12 | - @students.each do |student|
13 | %a.list-group-item{href: url_for(student)}
14 | = student.full_name
15 | - if student.submissions.count > 0
16 | - s = student.submissions.order(:tag).last
17 | %span.pull-right.label{class: "label-"+s.status_class}= s
18 |
--------------------------------------------------------------------------------
/app/views/students/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array!(@students) do |student|
2 | json.extract! student, :id, :first_name, :last_name, :github, :email
3 | json.url student_url(student, format: :json)
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/students/new.html.haml:
--------------------------------------------------------------------------------
1 | - title "Add Student"
2 | .page-header
3 | = link_to students_path, :class => 'btn btn-default' do
4 | %span.glyphicon.glyphicon-list-alt
5 | Back
6 | %h1 New student
7 |
8 | = render 'form'
9 |
--------------------------------------------------------------------------------
/app/views/students/show.html.haml:
--------------------------------------------------------------------------------
1 | - title "#{@student.full_name}"
2 | .page-header
3 | - if @student.github_username
4 | = link_to @student.github_repo_url, :class=> 'btn btn-default', target: '_blank' do
5 | %span.icon-github-2
6 | GitHub
7 | - if admin?
8 | = link_to "mailto:#{@student.email}", :class=> 'btn btn-default', target: '_blank' do
9 | %span.glyphicon.glyphicon-inbox
10 | Email
11 | = link_to students_path, :class => 'btn btn-default' do
12 | %span.glyphicon.glyphicon-list-alt
13 | Students
14 | = link_to edit_student_path(@student), :class => 'btn btn-default' do
15 | %span.glyphicon.glyphicon-pencil
16 | Edit
17 | %h1= @student.full_name
18 |
19 | - if Assignment.submission_deadline_past? or params[:show_grade_info]
20 | .alert.alert-info
21 | The deadline for submitting lab assignments has passed.
22 | - if Assignment.everything_is_graded?
23 | %br/
24 | Your final grade for the lab section of the course is
25 | =@student.assignment_progress.grade rescue nil
26 | - else
27 | Final lab grades will be posted shortly.
28 |
29 | - Assignment.find_each do |assignment|
30 | - submissions = Submission.where(assignment: assignment, student: @student).order(:tag)
31 | .panel{class: (submissions.completed.count > 0) ? 'panel-success' : 'panel-default'}
32 | .panel-heading
33 | %h2.panel-title= link_to assignment, assignment.details, target: '_blank'
34 | - if submissions.count == 0
35 | .panel-body
36 | %p No submissions for this assignment yet.
37 | .list-group
38 | - submissions.each do |submission|
39 | %div.list-group-item
40 | .btn-group.pull-right
41 | - unless submission.feedback.blank?
42 | %a.btn.btn-default{href: submission.feedback, target: '_blank'}
43 | %span.glyphicon.glyphicon-comment
44 | Feedback
45 | %a.btn.btn-default.pull-right{href: submission.github_url, target: '_blank'}
46 | %span{class: 'icon-github-2'}
47 | View
48 | %h4.list-group-item-heading
49 | - if admin?
50 | %a{href: edit_student_submission_path(@student, submission)}= submission
51 | - else
52 | = submission
53 | %p
54 | =submission.status_label
55 | - if submissions.completed.count > 0
56 | .panel-footer Lab Completed!
57 |
--------------------------------------------------------------------------------
/app/views/students/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! @student, :id, :first_name, :last_name, :github, :email, :created_at, :updated_at
2 |
--------------------------------------------------------------------------------
/app/views/submissions/_form.html.haml:
--------------------------------------------------------------------------------
1 | = form_for [@student, @submission], :html => { :class => "form-horizontal" } do |f|
2 | -if @submission.errors.any?
3 | .alert.alert-danger.alert-dismissable
4 | %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} ×
5 | %h4= "#{pluralize(@submission.errors.count, "error")} prohibited this submission from being saved:"
6 |
7 | %ul
8 | - @submission.errors.full_messages.each do |msg|
9 | %li= msg
10 |
11 | .form-group
12 | = f.label :tag, :class => 'col-sm-2 control-label'
13 | .col-sm-10
14 | = f.text_field :tag, :class => 'form-control'
15 | .form-group
16 | = f.label :assignment, :class => 'col-sm-2 control-label'
17 | .col-sm-10
18 | = f.select :assignment_id, options_for_select(Assignment.all.to_a.map{|a| [a.title, a.id] }, selected: @submission.assignment.id), {}, :class => 'form-control'
19 | .form-group
20 | = f.label :status, :class => 'col-sm-2 control-label'
21 | .col-sm-10
22 | = f.select :status, options_for_select(Submission.statuses.collect { |s| [s[0].humanize, s[0]] }, selected: @submission.status), {}, :class => 'form-control'
23 | .form-group
24 | = f.label :feedback, :class => 'col-sm-2 control-label'
25 | .col-sm-10
26 | = f.text_field :feedback, :class => 'form-control'
27 | .form-group
28 | .col-sm-offset-2.col-sm-10
29 | = f.submit :class => 'btn btn-primary'
30 |
--------------------------------------------------------------------------------
/app/views/submissions/edit.html.haml:
--------------------------------------------------------------------------------
1 | - title "#{@submission.tag} - Edit"
2 | .page-header
3 | = link_to @submission.github_url, :class => 'btn btn-default', target: '_blank' do
4 | %span.icon-github-2
5 | GitHub
6 | = link_to [@student, @submission], :class => 'btn btn-default', method: :delete do
7 | %span.glyphicon.glyphicon-remove
8 | Destroy
9 | = link_to @student, :class => 'btn btn-default' do
10 | %span.glyphicon.glyphicon-list-alt
11 | Back
12 | %h1="#{@student} - #{@submission}"
13 |
14 | = render 'form'
15 |
--------------------------------------------------------------------------------
/app/views/submissions/index.html.haml:
--------------------------------------------------------------------------------
1 | - title "Ungraded Submissions"
2 | .page-header
3 | %h1 Ungraded Submissions
4 |
5 | - if @submissions.count > 0
6 | .panel.panel-default
7 | .list-group
8 | - @submissions.each do |submission|
9 | %a.list-group-item{href: url_for(submission.student)}
10 | .label{class: "label-"+submission.status_class}= submission
11 | = submission.student.full_name
12 | %span.text-muted.pull-right
13 | = time_ago_in_words(submission.created_at)
14 | ago
15 | - else
16 | %h2.text-muted All done!
17 |
--------------------------------------------------------------------------------
/app/views/submissions/index.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.array!(@submissions) do |submission|
2 | json.extract! submission, :id, :link, :assignment_id, :student_id, :status
3 | json.url submission_url(submission, format: :json)
4 | end
5 |
--------------------------------------------------------------------------------
/app/views/submissions/new.html.haml:
--------------------------------------------------------------------------------
1 | - title "New Submission"
2 | .page-header
3 | = link_to student_submissions_path, :class => 'btn btn-default' do
4 | %span.glyphicon.glyphicon-list-alt
5 | Back
6 | %h1="#{@student.full_name}: Submit Assignment"
7 |
8 | = render 'form'
9 |
--------------------------------------------------------------------------------
/app/views/submissions/show.html.haml:
--------------------------------------------------------------------------------
1 | - title @submission.tag
2 | .page-header
3 | = link_to student_submissions_path(@student), :class => 'btn btn-default' do
4 | %span.glyphicon.glyphicon-list-alt
5 | Back
6 | = link_to edit_student_submission_path(@student, @submission), :class => 'btn btn-primary' do
7 | %span.glyphicon.glyphicon-pencil
8 | Edit
9 | %h1 Show submission
10 |
11 | %dl.dl-horizontal
12 | %dt Tag:
13 | %dd= @submission.tag
14 | %dt Assignment:
15 | %dd= @submission.assignment
16 | %dt Student:
17 | %dd= @submission.student
18 | %dt Status:
19 | %dd= @submission.status.humanize
20 |
--------------------------------------------------------------------------------
/app/views/submissions/show.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.extract! @submission, :id, :link, :assignment_id, :student_id, :status, :created_at, :updated_at
2 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
12 | ENV["GEM_HOME"] = ""
13 | Gem.paths = ENV
14 |
15 | gem "spring", match[1]
16 | require "spring/binstub"
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/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/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 Gradebook
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 | end
23 | end
24 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | # Set up gems listed in the Gemfile.
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3 |
4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5 |
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: postgresql
9 | pool: 5
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: gradebook-dev
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: gradebook-test
22 |
23 | production:
24 | adapter: postgresql
25 | encoding: unicode
26 | pool: 5
27 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | # Adds additional error checking when serving assets at runtime.
31 | # Checks for improperly declared sprockets dependencies.
32 | # Raises helpful error messages.
33 | config.assets.raise_runtime_errors = true
34 |
35 | # Raises error for missing translations
36 | # config.action_view.raise_on_missing_translations = true
37 | end
38 |
--------------------------------------------------------------------------------
/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 nginx, varnish or squid.
20 | # config.action_dispatch.rack_cache = true
21 |
22 | # Disable Rails's static asset server (Apache or nginx will already do this).
23 | config.serve_static_assets = false
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # Generate digests for assets URLs.
33 | config.assets.digest = true
34 |
35 | # `config.assets.precompile` has moved to config/initializers/assets.rb
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
40 |
41 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
42 | # config.force_ssl = true
43 |
44 | # Set to :debug to see everything in the log.
45 | config.log_level = :info
46 |
47 | # Prepend all log lines with the following tags.
48 | # config.log_tags = [ :subdomain, :uuid ]
49 |
50 | # Use a different logger for distributed setups.
51 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
52 |
53 | # Use a different cache store in production.
54 | # config.cache_store = :mem_cache_store
55 |
56 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
57 | # config.action_controller.asset_host = "http://assets.example.com"
58 |
59 | # Precompile additional assets.
60 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
61 | # config.assets.precompile += %w( search.js )
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 | # Disable automatic flushing of the log to improve performance.
75 | # config.autoflush_log = false
76 |
77 | # Use default logging formatter so that PID and timestamp are not suppressed.
78 | config.log_formatter = ::Logger::Formatter.new
79 |
80 | # Do not dump schema after migrations.
81 | config.active_record.dump_schema_after_migration = false
82 | end
83 |
--------------------------------------------------------------------------------
/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 asset server for tests with Cache-Control for performance.
16 | config.serve_static_assets = 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 | # Print deprecation notices to the stderr.
35 | config.active_support.deprecation = :stderr
36 |
37 | # Raises error for missing translations
38 | # config.action_view.raise_on_missing_translations = true
39 | end
40 |
--------------------------------------------------------------------------------
/config/initializers/aaaaaa.rb:
--------------------------------------------------------------------------------
1 | #obscure name because rails load initializers/* files based on alphabets
2 | require 'dotenv'
3 | Dotenv.load
4 |
--------------------------------------------------------------------------------
/config/initializers/admin_users.rb:
--------------------------------------------------------------------------------
1 | ADMINS=['qrohlf@gmail.com', 'qr@qrohlf.com', 'qrohlf@lclark.edu', 'jmache@lclark.edu']
2 |
--------------------------------------------------------------------------------
/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 | # Precompile additional assets.
7 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
8 | # Rails.application.config.assets.precompile += %w( search.js )
9 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/config/initializers/friendly_id.rb:
--------------------------------------------------------------------------------
1 | # FriendlyId Global Configuration
2 | #
3 | # Use this to set up shared configuration options for your entire application.
4 | # Any of the configuration options shown here can also be applied to single
5 | # models by passing arguments to the `friendly_id` class method or defining
6 | # methods in your model.
7 | #
8 | # To learn more, check out the guide:
9 | #
10 | # http://norman.github.io/friendly_id/file.Guide.html
11 |
12 | FriendlyId.defaults do |config|
13 | # ## Reserved Words
14 | #
15 | # Some words could conflict with Rails's routes when used as slugs, or are
16 | # undesirable to allow as slugs. Edit this list as needed for your app.
17 | config.use :reserved
18 |
19 | config.reserved_words = %w(new edit index session login logout users admin
20 | stylesheets assets javascripts images)
21 |
22 | # ## Friendly Finders
23 | #
24 | # Uncomment this to use friendly finders in all models. By default, if
25 | # you wish to find a record by its friendly id, you must do:
26 | #
27 | # MyModel.friendly.find('foo')
28 | #
29 | # If you uncomment this, you can do:
30 | #
31 | # MyModel.find('foo')
32 | #
33 | # This is significantly more convenient but may not be appropriate for
34 | # all applications, so you must explicity opt-in to this behavior. You can
35 | # always also configure it on a per-model basis if you prefer.
36 | #
37 | # Something else to consider is that using the :finders addon boosts
38 | # performance because it will avoid Rails-internal code that makes runtime
39 | # calls to `Module.extend`.
40 | #
41 | # config.use :finders
42 | #
43 | # ## Slugs
44 | #
45 | # Most applications will use the :slugged module everywhere. If you wish
46 | # to do so, uncomment the following line.
47 | #
48 | # config.use :slugged
49 | #
50 | # By default, FriendlyId's :slugged addon expects the slug column to be named
51 | # 'slug', but you can change it if you wish.
52 | #
53 | # config.slug_column = 'slug'
54 | #
55 | # When FriendlyId can not generate a unique ID from your base method, it appends
56 | # a UUID, separated by a single dash. You can configure the character used as the
57 | # separator. If you're upgrading from FriendlyId 4, you may wish to replace this
58 | # with two dashes.
59 | #
60 | # config.sequence_separator = '-'
61 | #
62 | # ## Tips and Tricks
63 | #
64 | # ### Controlling when slugs are generated
65 | #
66 | # As of FriendlyId 5.0, new slugs are generated only when the slug field is
67 | # nil, but if you're using a column as your base method can change this
68 | # behavior by overriding the `should_generate_new_friendly_id` method that
69 | # FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
70 | # more like 4.0.
71 | #
72 | # config.use Module.new {
73 | # def should_generate_new_friendly_id?
74 | # slug.blank? || _changed?
75 | # end
76 | # }
77 | #
78 | # FriendlyId uses Rails's `parameterize` method to generate slugs, but for
79 | # languages that don't use the Roman alphabet, that's not usually suffient. Here
80 | # we use the Babosa library to transliterate Russian Cyrillic slugs to ASCII. If
81 | # you use this, don't forget to add "babosa" to your Gemfile.
82 | #
83 | # config.use Module.new {
84 | # def normalize_friendly_id(text)
85 | # text.to_slug.normalize! :transliterations => [:russian, :latin]
86 | # end
87 | # }
88 | end
89 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/config/initializers/octokit.rb:
--------------------------------------------------------------------------------
1 | Octokit.configure do |c|
2 | c.client_id = Rails.application.secrets.github_client_id
3 | c.client_secret = Rails.application.secrets.github_client_secret
4 | end
5 |
--------------------------------------------------------------------------------
/config/initializers/omniauth.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.middleware.use OmniAuth::Builder do
2 | provider :google_oauth2, Rails.application.secrets.google_client_id, Rails.application.secrets.google_client_secret
3 | end
4 |
--------------------------------------------------------------------------------
/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: '_gradebook_session', expire_after: 1.week
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 |
3 |
4 | get '/students/stats.json' => 'students#stats'
5 |
6 | resources :assignments do
7 | member do
8 | get 'graph_data'
9 | end
10 | end
11 | resources :students do
12 | resources :submissions
13 | end
14 |
15 | get '/auth/google_oauth2/callback' => 'auth#google_oauth2'
16 | get '/logout' => 'auth#logout'
17 | get '/submissions' => 'submissions#index'
18 |
19 | root 'students#index'
20 | end
21 |
--------------------------------------------------------------------------------
/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: 5c4a39fc32c6a776ccba65a21ea6d20ab8a25a899ed0702dfd522f1b43812d7b4da93db6d7b1c2f89337392edcfbf60a55431fa623c0d21befc28898ba20ed80
15 | client_api_key: svxnfVxN3ertLwDsp20n63FqwhTvhhaz
16 | google_client_id: <%= ENV["GOOGLE_CLIENT_ID"] %>
17 | google_client_secret: <%= ENV["GOOGLE_CLIENT_SECRET"] %>
18 | github_client_id: <%= ENV["GITHUB_CLIENT_ID"] %>
19 | github_client_secret: <%= ENV["GITHUB_CLIENT_SECRET"] %>
20 |
21 | test:
22 | secret_key_base: fe4104f9dbd6bda45744eb949d06f54541d13e80e8c306b650c5a4b6cff81af0033f059c4eb6da33dec145f7c736408ca901650169e28b136c808e2aeb0f6971
23 | client_api_key: svxnfVxN3ertLwDsp20n63FqwhTvhhaz
24 |
25 | # Do not keep production secrets in the repository,
26 | # instead read values from the environment.
27 | production:
28 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
29 | client_api_key: <%= ENV["CLIENT_API_KEY"] %>
30 | google_client_id: <%= ENV["GOOGLE_CLIENT_ID"] %>
31 | google_client_secret: <%= ENV["GOOGLE_CLIENT_SECRET"] %>
32 | github_client_id: <%= ENV["GITHUB_CLIENT_ID"] %>
33 | github_client_secret: <%= ENV["GITHUB_CLIENT_SECRET"] %>
34 |
--------------------------------------------------------------------------------
/config/unicorn.rb:
--------------------------------------------------------------------------------
1 | # config/unicorn.rb
2 | worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
3 | timeout 15
4 | preload_app true
5 |
6 | before_fork do |server, worker|
7 | Signal.trap 'TERM' do
8 | puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
9 | Process.kill 'QUIT', Process.pid
10 | end
11 |
12 | defined?(ActiveRecord::Base) and
13 | ActiveRecord::Base.connection.disconnect!
14 | end
15 |
16 | after_fork do |server, worker|
17 | Signal.trap 'TERM' do
18 | puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
19 | end
20 |
21 | defined?(ActiveRecord::Base) and
22 | ActiveRecord::Base.establish_connection
23 | end
24 |
--------------------------------------------------------------------------------
/db/migrate/20140824155718_create_friendly_id_slugs.rb:
--------------------------------------------------------------------------------
1 | class CreateFriendlyIdSlugs < ActiveRecord::Migration
2 | def change
3 | create_table :friendly_id_slugs do |t|
4 | t.string :slug, :null => false
5 | t.integer :sluggable_id, :null => false
6 | t.string :sluggable_type, :limit => 50
7 | t.string :scope
8 | t.datetime :created_at
9 | end
10 | add_index :friendly_id_slugs, :sluggable_id
11 | add_index :friendly_id_slugs, [:slug, :sluggable_type]
12 | add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true
13 | add_index :friendly_id_slugs, :sluggable_type
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/db/migrate/20140919162502_create_assignments.rb:
--------------------------------------------------------------------------------
1 | class CreateAssignments < ActiveRecord::Migration
2 | def change
3 | create_table :assignments do |t|
4 | t.string :title
5 | t.string :details
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20140919162620_create_students.rb:
--------------------------------------------------------------------------------
1 | class CreateStudents < ActiveRecord::Migration
2 | def change
3 | create_table :students do |t|
4 | t.string :first_name
5 | t.string :last_name
6 | t.string :github_username
7 | t.string :github_repo
8 | t.string :email
9 |
10 | t.timestamps
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20140919163045_create_submissions.rb:
--------------------------------------------------------------------------------
1 | class CreateSubmissions < ActiveRecord::Migration
2 | def change
3 | create_table :submissions do |t|
4 | t.string :tag
5 | t.belongs_to :assignment, index: true
6 | t.belongs_to :student, index: true
7 | t.integer :status, default: 0
8 |
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/migrate/20140919163518_add_slug_to_students.rb:
--------------------------------------------------------------------------------
1 | class AddSlugToStudents < ActiveRecord::Migration
2 | def change
3 | add_column :students, :slug, :string
4 | add_index :students, :slug, unique: true
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/db/migrate/20140921162741_add_tag_prefix_to_assignments.rb:
--------------------------------------------------------------------------------
1 | class AddTagPrefixToAssignments < ActiveRecord::Migration
2 | def change
3 | add_column :assignments, :tag_prefix, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20140921174147_add_last_sync_to_students.rb:
--------------------------------------------------------------------------------
1 | class AddLastSyncToStudents < ActiveRecord::Migration
2 | def change
3 | add_column :students, :last_sync, :datetime
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20140922001500_add_feedback_to_submissions.rb:
--------------------------------------------------------------------------------
1 | class AddFeedbackToSubmissions < ActiveRecord::Migration
2 | def change
3 | add_column :submissions, :feedback, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/db/migrate/20141110170326_add_grade_to_assignments.rb:
--------------------------------------------------------------------------------
1 | class AddGradeToAssignments < ActiveRecord::Migration
2 | def change
3 | add_column :assignments, :grade, :string
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/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: 20141110170326) do
15 |
16 | # These are extensions that must be enabled in order to support this database
17 | enable_extension "plpgsql"
18 |
19 | create_table "assignments", force: true do |t|
20 | t.string "title"
21 | t.string "details"
22 | t.datetime "created_at"
23 | t.datetime "updated_at"
24 | t.string "tag_prefix"
25 | t.string "grade"
26 | end
27 |
28 | create_table "friendly_id_slugs", force: true do |t|
29 | t.string "slug", null: false
30 | t.integer "sluggable_id", null: false
31 | t.string "sluggable_type", limit: 50
32 | t.string "scope"
33 | t.datetime "created_at"
34 | end
35 |
36 | add_index "friendly_id_slugs", ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true, using: :btree
37 | add_index "friendly_id_slugs", ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type", using: :btree
38 | add_index "friendly_id_slugs", ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id", using: :btree
39 | add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree
40 |
41 | create_table "students", force: true do |t|
42 | t.string "first_name"
43 | t.string "last_name"
44 | t.string "github_username"
45 | t.string "github_repo"
46 | t.string "email"
47 | t.datetime "created_at"
48 | t.datetime "updated_at"
49 | t.string "slug"
50 | t.datetime "last_sync"
51 | end
52 |
53 | add_index "students", ["slug"], name: "index_students_on_slug", unique: true, using: :btree
54 |
55 | create_table "submissions", force: true do |t|
56 | t.string "tag"
57 | t.integer "assignment_id"
58 | t.integer "student_id"
59 | t.integer "status", default: 0
60 | t.datetime "created_at"
61 | t.datetime "updated_at"
62 | t.string "feedback"
63 | end
64 |
65 | add_index "submissions", ["assignment_id"], name: "index_submissions_on_assignment_id", using: :btree
66 | add_index "submissions", ["student_id"], name: "index_submissions_on_student_id", using: :btree
67 |
68 | end
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 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/lib/tasks/.keep
--------------------------------------------------------------------------------
/lib/tasks/db.rake:
--------------------------------------------------------------------------------
1 | namespace :db do
2 | desc "TODO"
3 | task nuke_production_data: :environment do
4 | system "heroku pg:reset --confirm gradebook-qr DATABASE_URL"
5 | system "heroku run rake db:migrate"
6 | end
7 |
8 | end
9 |
--------------------------------------------------------------------------------
/lib/templates/haml/controller/view.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | %h1 <%= class_name %>#<%= @action %>
3 | %p Find me in <%= @path %>
4 |
--------------------------------------------------------------------------------
/lib/templates/haml/scaffold/_form.html.haml:
--------------------------------------------------------------------------------
1 | = form_for @<%= singular_table_name %>, :html => { :class => "form-horizontal" } do |f|
2 | -if @<%= singular_table_name %>.errors.any?
3 | .alert.alert-danger.alert-dismissable
4 | %button.close{"aria-hidden" => "true", "data-dismiss" => "alert", :type => "button"} ×
5 | %h4= "#{pluralize(@<%= singular_table_name %>.errors.count, "error")} prohibited this <%= singular_table_name %> from being saved:"
6 |
7 | %ul
8 | - @<%= singular_table_name %>.errors.full_messages.each do |msg|
9 | %li= msg
10 |
11 | <% for attribute in attributes -%>
12 | .form-group
13 | = f.label :<%= attribute.name %>, :class => 'col-sm-2 control-label'
14 | .col-sm-10
15 | = f.<%= attribute.field_type %> :<%= attribute.name %>, :class => 'form-control'
16 | <% end -%>
17 | .form-group
18 | .col-sm-offset-2.col-sm-10
19 | = f.submit :class => 'btn btn-primary'
20 |
--------------------------------------------------------------------------------
/lib/templates/haml/scaffold/edit.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | = link_to <%= index_helper %>_path, :class => 'btn btn-default' do
3 | %span.glyphicon.glyphicon-list-alt
4 | Back
5 | = link_to @<%= singular_table_name %>, :class => 'btn btn-primary' do
6 | %span.glyphicon.glyphicon-info-sign
7 | Show
8 | %h1 Editing <%= singular_table_name %>
9 |
10 | = render 'form'
11 |
--------------------------------------------------------------------------------
/lib/templates/haml/scaffold/index.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | = link_to new_<%= singular_table_name %>_path, :class => 'btn btn-primary' do
3 | %span.glyphicon.glyphicon-plus
4 | New <%= human_name %>
5 | %h1 Listing <%= plural_table_name %>
6 |
7 | .table-responsive
8 | %table.table.table-striped.table-bordered.table-hover
9 | %thead
10 | %tr
11 | <% for attribute in attributes -%>
12 | %th <%= attribute.human_name %>
13 | <% end -%>
14 | %th
15 | %th
16 | %th
17 |
18 | %tbody
19 | - @<%= plural_table_name %>.each do |<%= singular_table_name %>|
20 | %tr
21 | <% for attribute in attributes -%>
22 | %td= <%= singular_table_name %>.<%= attribute.name %>
23 | <% end -%>
24 | %td= link_to 'Show', <%= singular_table_name %>
25 | %td= link_to 'Edit', edit_<%= singular_table_name %>_path(<%= singular_table_name %>)
26 | %td= link_to 'Destroy', <%= singular_table_name %>, :data => { confirm: 'Are you sure?' }, :method => :delete
27 |
--------------------------------------------------------------------------------
/lib/templates/haml/scaffold/new.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | = link_to <%= index_helper %>_path, :class => 'btn btn-default' do
3 | %span.glyphicon.glyphicon-list-alt
4 | Back
5 | %h1 New <%= singular_table_name %>
6 |
7 | = render 'form'
8 |
--------------------------------------------------------------------------------
/lib/templates/haml/scaffold/show.html.haml:
--------------------------------------------------------------------------------
1 | .page-header
2 | = link_to <%= index_helper %>_path, :class => 'btn btn-default' do
3 | %span.glyphicon.glyphicon-list-alt
4 | Back
5 | = link_to edit_<%= singular_table_name %>_path(@<%= singular_table_name %>), :class => 'btn btn-primary' do
6 | %span.glyphicon.glyphicon-pencil
7 | Edit
8 | %h1 Show <%= singular_table_name %>
9 |
10 | %dl.dl-horizontal
11 | <%- for attribute in attributes -%>
12 | %dt <%= attribute.human_name %>:
13 | %dd= @<%= singular_table_name %>.<%= attribute.name %>
14 | <%- end -%>
15 |
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/log/.keep
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/public/favicon.ico
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/scripts/scramble-names.rb:
--------------------------------------------------------------------------------
1 | # anonymize student names for screenshots
2 |
3 | Student.find_each do |s|
4 | s.update(first_name: Faker::Name.first_name, last_name: Faker::Name.last_name)
5 | end
--------------------------------------------------------------------------------
/scripts/students_to_csv.rb:
--------------------------------------------------------------------------------
1 | #usage: `rails runner students_to_csv.rb`
2 | require 'CSV'
3 |
4 | CSV.open('students.csv', "w") do |csv, args|
5 | csv << Student.attribute_names
6 | Student.all.order(:last_name).each do |user|
7 | csv << user.attributes.values
8 | end
9 | end
--------------------------------------------------------------------------------
/test/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/test/controllers/.keep
--------------------------------------------------------------------------------
/test/controllers/assignments_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AssignmentsControllerTest < ActionController::TestCase
4 | setup do
5 | @assignment = assignments(:one)
6 | end
7 |
8 | test "should get index" do
9 | get :index
10 | assert_response :success
11 | assert_not_nil assigns(:assignments)
12 | end
13 |
14 | test "should get new" do
15 | get :new
16 | assert_response :success
17 | end
18 |
19 | test "should create assignment" do
20 | assert_difference('Assignment.count') do
21 | post :create, assignment: { details: @assignment.details, title: @assignment.title }
22 | end
23 |
24 | assert_redirected_to assignment_path(assigns(:assignment))
25 | end
26 |
27 | test "should show assignment" do
28 | get :show, id: @assignment
29 | assert_response :success
30 | end
31 |
32 | test "should get edit" do
33 | get :edit, id: @assignment
34 | assert_response :success
35 | end
36 |
37 | test "should update assignment" do
38 | patch :update, id: @assignment, assignment: { details: @assignment.details, title: @assignment.title }
39 | assert_redirected_to assignment_path(assigns(:assignment))
40 | end
41 |
42 | test "should destroy assignment" do
43 | assert_difference('Assignment.count', -1) do
44 | delete :destroy, id: @assignment
45 | end
46 |
47 | assert_redirected_to assignments_path
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/test/controllers/auth_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AuthControllerTest < ActionController::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/controllers/students_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class StudentsControllerTest < ActionController::TestCase
4 | setup do
5 | @student = students(:one)
6 | end
7 |
8 | test "should get index" do
9 | get :index
10 | assert_response :success
11 | assert_not_nil assigns(:students)
12 | end
13 |
14 | test "should get new" do
15 | get :new
16 | assert_response :success
17 | end
18 |
19 | test "should create student" do
20 | assert_difference('Student.count') do
21 | post :create, student: { email: @student.email, first_name: @student.first_name, github: @student.github, last_name: @student.last_name }
22 | end
23 |
24 | assert_redirected_to student_path(assigns(:student))
25 | end
26 |
27 | test "should show student" do
28 | get :show, id: @student
29 | assert_response :success
30 | end
31 |
32 | test "should get edit" do
33 | get :edit, id: @student
34 | assert_response :success
35 | end
36 |
37 | test "should update student" do
38 | patch :update, id: @student, student: { email: @student.email, first_name: @student.first_name, github: @student.github, last_name: @student.last_name }
39 | assert_redirected_to student_path(assigns(:student))
40 | end
41 |
42 | test "should destroy student" do
43 | assert_difference('Student.count', -1) do
44 | delete :destroy, id: @student
45 | end
46 |
47 | assert_redirected_to students_path
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/test/controllers/submissions_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SubmissionsControllerTest < ActionController::TestCase
4 | setup do
5 | @submission = submissions(:one)
6 | end
7 |
8 | test "should get index" do
9 | get :index
10 | assert_response :success
11 | assert_not_nil assigns(:submissions)
12 | end
13 |
14 | test "should get new" do
15 | get :new
16 | assert_response :success
17 | end
18 |
19 | test "should create submission" do
20 | assert_difference('Submission.count') do
21 | post :create, submission: { assignment_id: @submission.assignment_id, link: @submission.link, status: @submission.status, student_id: @submission.student_id }
22 | end
23 |
24 | assert_redirected_to submission_path(assigns(:submission))
25 | end
26 |
27 | test "should show submission" do
28 | get :show, id: @submission
29 | assert_response :success
30 | end
31 |
32 | test "should get edit" do
33 | get :edit, id: @submission
34 | assert_response :success
35 | end
36 |
37 | test "should update submission" do
38 | patch :update, id: @submission, submission: { assignment_id: @submission.assignment_id, link: @submission.link, status: @submission.status, student_id: @submission.student_id }
39 | assert_redirected_to submission_path(assigns(:submission))
40 | end
41 |
42 | test "should destroy submission" do
43 | assert_difference('Submission.count', -1) do
44 | delete :destroy, id: @submission
45 | end
46 |
47 | assert_redirected_to submissions_path
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/test/fixtures/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/test/fixtures/.keep
--------------------------------------------------------------------------------
/test/fixtures/assignments.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | title: MyString
5 | details: MyString
6 |
7 | two:
8 | title: MyString
9 | details: MyString
10 |
--------------------------------------------------------------------------------
/test/fixtures/students.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | first_name: MyString
5 | last_name: MyString
6 | github: MyString
7 | email: MyString
8 |
9 | two:
10 | first_name: MyString
11 | last_name: MyString
12 | github: MyString
13 | email: MyString
14 |
--------------------------------------------------------------------------------
/test/fixtures/submissions.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | one:
4 | link: MyString
5 | assignment_id:
6 | student_id:
7 | status: 1
8 |
9 | two:
10 | link: MyString
11 | assignment_id:
12 | student_id:
13 | status: 1
14 |
--------------------------------------------------------------------------------
/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/test/helpers/.keep
--------------------------------------------------------------------------------
/test/helpers/assignments_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AssignmentsHelperTest < ActionView::TestCase
4 | end
5 |
--------------------------------------------------------------------------------
/test/helpers/auth_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AuthHelperTest < ActionView::TestCase
4 | end
5 |
--------------------------------------------------------------------------------
/test/helpers/students_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class StudentsHelperTest < ActionView::TestCase
4 | end
5 |
--------------------------------------------------------------------------------
/test/helpers/submissions_helper_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SubmissionsHelperTest < ActionView::TestCase
4 | end
5 |
--------------------------------------------------------------------------------
/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/test/integration/.keep
--------------------------------------------------------------------------------
/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/test/mailers/.keep
--------------------------------------------------------------------------------
/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/test/models/.keep
--------------------------------------------------------------------------------
/test/models/assignment_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AssignmentTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/models/student_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class StudentTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/models/submission_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SubmissionTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/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/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/vendor/assets/javascripts/.keep
--------------------------------------------------------------------------------
/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qrohlf/gradebook/b74f5599b067da14b902c7995188e897b14ff48f/vendor/assets/stylesheets/.keep
--------------------------------------------------------------------------------