├── .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 | ![screen shot 2014-09-23 at 1 28 18 pm](https://cloud.githubusercontent.com/assets/347189/4379384/2edd2180-4360-11e4-8e2a-b3f88cdcb531.png) 26 | 27 | ![screen shot 2014-10-25 at 10 44 24 am](https://cloud.githubusercontent.com/assets/347189/4780572/a6b33402-5c6e-11e4-9a66-42e32799bb98.png) 28 | 29 | ![screen shot 2014-09-23 at 12 54 04 pm](https://cloud.githubusercontent.com/assets/347189/4378829/991ac53e-435b-11e4-9986-918dff95eddc.png) 30 | 31 | ![screen shot 2014-09-24 at 8 58 33 am](https://cloud.githubusercontent.com/assets/347189/4391275/bb40e352-4403-11e4-8111-b33b7534828a.png) 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 --------------------------------------------------------------------------------