├── .env.example ├── .gitignore ├── .rspec ├── Gemfile ├── Gemfile.lock ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── .keep │ │ ├── favicon.ico │ │ ├── favicon.png │ │ └── hiredintech_logo.png │ ├── javascripts │ │ ├── application.js │ │ ├── classrooms.js.coffee │ │ ├── courses.js.coffee │ │ ├── google_analytics.js │ │ ├── metisMenu.js │ │ ├── quizzes.js.coffee │ │ ├── sb-admin-2.js │ │ ├── smooth_scroll.js │ │ ├── tasks.js.coffee │ │ └── uservoice.js │ └── stylesheets │ │ ├── application.scss │ │ ├── bootstrap-overrides.scss │ │ ├── bootstrap-social.scss │ │ ├── classrooms.scss │ │ ├── common.scss │ │ ├── courses.scss │ │ ├── devise-overrides.scss │ │ ├── github.scss │ │ ├── lessons.scss │ │ ├── log-in-sign-up.scss │ │ ├── metisMenu.scss │ │ ├── quizzes.scss │ │ ├── sb-admin-2.scss │ │ ├── sections.scss │ │ ├── sticky-footer-navbar.scss │ │ ├── tasks.scss │ │ └── users.scss ├── controllers │ ├── application_controller.rb │ ├── classrooms_controller.rb │ ├── concerns │ │ └── .keep │ ├── confirmations_controller.rb │ ├── courses_controller.rb │ ├── lessons_controller.rb │ ├── omniauth_callbacks_controller.rb │ ├── pages_controller.rb │ ├── passwords_controller.rb │ ├── quizzes_controller.rb │ ├── registrations_controller.rb │ ├── sections_controller.rb │ ├── sessions_controller.rb │ ├── tasks_controller.rb │ ├── user_invitations_controller.rb │ └── users_controller.rb ├── helpers │ ├── devise_helper.rb │ ├── highlight_helper.rb │ ├── menu_helper.rb │ ├── quiz_helper.rb │ ├── task_run_display_helper.rb │ └── theme_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ ├── .keep │ └── user_invitations_mailer.rb ├── models │ ├── .keep │ ├── application_record.rb │ ├── classroom.rb │ ├── classroom_record.rb │ ├── concerns │ │ └── .keep │ ├── course.rb │ ├── lesson.rb │ ├── lesson_record.rb │ ├── quiz.rb │ ├── quiz_answer.rb │ ├── quiz_attempt.rb │ ├── quiz_question.rb │ ├── role.rb │ ├── section.rb │ ├── settings.rb │ ├── task.rb │ ├── task_record.rb │ ├── task_run.rb │ ├── user.rb │ └── user_invitation.rb ├── services │ ├── create_course.rb │ ├── fetch_task_statistics.rb │ ├── grader_api.rb │ ├── process_task_run.rb │ ├── render_markdown.rb │ ├── score_quiz_attempt.rb │ ├── solve_task.rb │ └── update_user_with_task_run.rb └── views │ ├── classrooms │ ├── _compilation_error_details.html.erb │ ├── _enrol_or_login_modal.html.erb │ ├── _execution_codes_help.html.erb │ ├── _lesson_bottom_navigation.html.erb │ ├── _lesson_content.html.erb │ ├── _lesson_links_to_quizzes.html.erb │ ├── _lesson_links_to_tasks.html.erb │ ├── _lesson_runs.html.erb │ ├── _lesson_sidebar_content.html.erb │ ├── _lesson_tasks.html.erb │ ├── _lesson_title.html.erb │ ├── _lesson_top_links.html.erb │ ├── _runtime_errors_details.html.erb │ ├── _solution_sidebar.html.erb │ ├── _solve_task_panel.html.erb │ ├── _successful_runs_sidebar.html.erb │ ├── _task_limits_help.html.erb │ ├── _task_run.html.erb │ ├── _task_run_display_status.html.erb │ ├── _task_runs.html.erb │ ├── _task_runs_sidebar.html.erb │ ├── _task_sidebar.html.erb │ ├── _task_sidebar_limits.html.erb │ ├── _task_summary_panel.html.erb │ ├── attempt_quiz.html.erb │ ├── lesson.html.erb │ ├── lesson_quiz.html.erb │ ├── lesson_task.html.erb │ ├── progress.html.erb │ ├── quizzes │ │ ├── _attempt_quiz_sidebar.html.erb │ │ ├── _lesson_quizzes.html.erb │ │ ├── _quiz_sidebar.html.erb │ │ ├── _quiz_sidebar_limits.html.erb │ │ ├── _quiz_summary_panel.html.erb │ │ └── _user_attempts.html.erb │ ├── show_quiz_attempt.html.erb │ ├── student_quiz_attempt.html.erb │ ├── student_quiz_attempts.html.erb │ ├── student_task_runs.html.erb │ ├── task_run.js.erb │ ├── task_runs.html.erb │ ├── task_solution.html.erb │ ├── task_successful_runs.html.erb │ └── users.html.erb │ ├── courses │ ├── _course.html.erb │ ├── _course_call_to_action.html.erb │ ├── _edit_visibility.html.erb │ ├── _fields.html.erb │ ├── _menu.html.erb │ ├── _menu_new.html.erb │ ├── edit.html.erb │ ├── edit_structure.html.erb │ ├── index.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── devise │ ├── confirmations │ │ └── new.html.erb │ ├── mailer │ │ ├── confirmation_instructions.html.erb │ │ ├── reset_password_instructions.html.erb │ │ └── unlock_instructions.html.erb │ ├── passwords │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── registrations │ │ └── new.html.erb │ ├── sessions │ │ └── new.html.erb │ ├── shared │ │ └── _links.html.erb │ └── unlocks │ │ └── new.html.erb │ ├── layouts │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _quizzes_header_item.html.erb │ ├── _tasks_header_item.html.erb │ └── application.html.erb │ ├── lessons │ ├── _attach_quizzes.html.erb │ ├── _attach_tasks.html.erb │ ├── _change_position.html.erb │ ├── _edit_title.html.erb │ ├── _new.html.erb │ └── edit.html.erb │ ├── pages │ ├── about.html.erb │ ├── about_codemarathon.html.erb │ └── contact.html.erb │ ├── quizzes │ ├── _admin_header.html.erb │ ├── _attempt.html.erb │ ├── _fields.html.erb │ ├── _main_page.html.erb │ ├── _quiz.html.erb │ ├── _quiz_answer_fields.html.erb │ ├── _quiz_question.html.erb │ ├── _quiz_question_attempt.html.erb │ ├── _quiz_question_fields.html.erb │ ├── _show_attempt.html.erb │ ├── all_attempts.html.erb │ ├── attempt.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── show.html.erb │ └── show_attempt.html.erb │ ├── sections │ ├── _change_position.html.erb │ ├── _edit_title.html.erb │ └── _new.html.erb │ ├── shared │ └── _social_buttons.html.erb │ ├── tasks │ ├── _admin_panel.html.erb │ ├── _edit_runs_limit.html.erb │ ├── _fields.html.erb │ ├── _run.html.erb │ ├── _runs.html.erb │ ├── _solve.html.erb │ ├── _successful_run_source_code.html.erb │ ├── _task.html.erb │ ├── _task_run_source_code.html.erb │ ├── all_runs.html.erb │ ├── create.html.erb │ ├── destroy.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── runs.html.erb │ ├── runs_limits.html.erb │ ├── show.html.erb │ ├── solution.html.erb │ ├── solve.html.erb │ ├── stats.html.erb │ └── update.html.erb │ ├── user_invitations │ └── index.html.erb │ ├── user_invitations_mailer │ ├── invitation_with_user.html.erb │ └── invitation_without_user.html.erb │ └── users │ ├── _search_users.html.erb │ ├── _users_list.html.erb │ ├── _users_top_menu.html.erb │ ├── edit_profile.html.erb │ ├── inactive.html.erb │ ├── index.html.erb │ └── show.html.erb ├── backup ├── config.rb └── models │ └── codemarathon_backup.example.rb ├── bin ├── bundle ├── delayed_job ├── rails ├── rake ├── setup └── spring ├── config.ru ├── config ├── application.rb ├── application.yml ├── boot.rb ├── database.example.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── omniauth.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── routes.rb ├── secrets.yml └── unicorn.rb ├── db ├── migrate │ ├── 20150813135216_devise_create_users.rb │ ├── 20150813145458_add_omniauth_to_users.rb │ ├── 20150813152119_add_name_to_user.rb │ ├── 20151002053704_create_courses.rb │ ├── 20151002073002_add_long_description_to_course.rb │ ├── 20151002130501_modify_course_columns.rb │ ├── 20151002134106_create_roles.rb │ ├── 20151012135858_create_sections.rb │ ├── 20151013060709_make_section_fields_non_null.rb │ ├── 20151013161035_create_lessons.rb │ ├── 20151013163723_change_lesson_field_null.rb │ ├── 20151013194516_add_content_to_lesson.rb │ ├── 20151016153923_create_classrooms.rb │ ├── 20151016154207_make_classroom_fields_non_null.rb │ ├── 20151016154749_create_classroom_records.rb │ ├── 20151023135004_add_default_to_lesson_content.rb │ ├── 20151025194346_add_visible_flag_to_course.rb │ ├── 20151026143538_add_visible_to_sections.rb │ ├── 20151026143549_add_visible_to_lessons.rb │ ├── 20151029063854_create_tasks.rb │ ├── 20151105093612_create_task_runs.rb │ ├── 20151105120959_add_run_type_to_task_runs.rb │ ├── 20151105122008_add_points_to_task_run.rb │ ├── 20151130142452_create_table_lesson_tasks.rb │ ├── 20151203141019_make_points_non_null_in_task_run.rb │ ├── 20151208213102_create_task_records.rb │ ├── 20151208214540_create_lesson_records.rb │ ├── 20151210122605_make_task_fields_mandatory.rb │ ├── 20160104161048_make_source_code_nullable_in_task.rb │ ├── 20160303075349_add_main_flag_to_courses.rb │ ├── 20160305073957_add_unique_constraint_on_classroom_records.rb │ ├── 20160310070738_add_runs_limit_to_task_record.rb │ ├── 20160314071507_create_user_invitations.rb │ ├── 20160314074133_add_active_to_users.rb │ ├── 20160417163920_add_subtitle_to_course.rb │ ├── 20160418192655_add_slug_to_course.rb │ ├── 20160418200353_add_unique_index_on_course_slug.rb │ ├── 20160418200506_add_slug_to_classrooms.rb │ ├── 20160429170700_create_delayed_jobs.rb │ ├── 20160502194330_add_solution_to_task.rb │ ├── 20160608104441_add_active_to_classroom_record.rb │ ├── 20160608110642_add_user_limit_to_classroom.rb │ ├── 20160614144305_add_display_status_to_task_run.rb │ ├── 20160614152702_add_compilation_log_to_task_run.rb │ ├── 20160614201512_add_error_details_to_task_runs.rb │ ├── 20160713161017_add_wrapper_and_boilerplate_code_to_tasks.rb │ ├── 20160713183600_add_last_programming_language_to_user.rb │ ├── 20160716163403_set_last_programming_language_based_on_submissions.rb │ ├── 20160817091910_add_public_to_courses.rb │ ├── 20160826161231_increase_runs_limit.rb │ ├── 20160906111443_add_sidebar_content_to_lesson.rb │ ├── 20160920113459_create_quizzes.rb │ ├── 20160920113641_create_quiz_questions.rb │ ├── 20160920113733_create_quiz_answers.rb │ ├── 20160928125223_add_maximum_attempts_and_time_to_wait_to_quizzes.rb │ ├── 20160928140809_create_quiz_attempts.rb │ ├── 20160929185657_add_lessons_quizzes_table.rb │ ├── 20161018120810_add_explanation_to_quiz_questions.rb │ ├── 20161216140911_add_markdown_to_quiz_questions.rb │ ├── 20161220144607_add_markdown_explanation_to_quiz_questions.rb │ ├── 20161220161304_add_markdown_to_quiz_answers.rb │ ├── 20161221104754_transfer_question_content_to_markdown.rb │ ├── 20161226071326_transfer_quiz_answers_content_to_markdown.rb │ ├── 20170119113201_add_disable_task_submissions_to_courses.rb │ └── 20171101202536_add_public_to_task_run.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ ├── .keep │ └── update_runs_status.rake ├── log └── .keep ├── provisioning ├── deploy_production.yml ├── production.ini ├── restart_production.yml ├── roles │ ├── backup │ │ └── tasks │ │ │ └── main.yml │ ├── config │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ │ ├── nginx_config.j2 │ │ │ ├── nginx_config.no_ssl.j2 │ │ │ └── unicorn_init.j2 │ ├── createdb │ │ └── tasks │ │ │ └── main.yml │ ├── deploy │ │ └── tasks │ │ │ └── main.yml │ ├── install │ │ └── tasks │ │ │ └── main.yml │ ├── restart │ │ └── tasks │ │ │ └── main.yml │ ├── start │ │ └── tasks │ │ │ └── main.yml │ └── stop │ │ └── tasks │ │ └── main.yml ├── setup_backups.yml ├── setup_production.yml ├── setup_vagrant.yml ├── start_production.yml └── stop_production.yml ├── public ├── 404.html ├── 404_default.html ├── 422.html ├── 500.html ├── 500_default.html ├── css │ └── styles.css ├── img │ └── hiredintech_logo.png └── robots.txt ├── spec ├── controllers │ ├── classrooms_controller_spec.rb │ ├── confirmations_controller_spec.rb │ ├── courses_controller_spec.rb │ ├── lessons_controller_spec.rb │ ├── omniauth_callbacks_controller_spec.rb │ ├── pages_controller_spec.rb │ ├── passwords_controller_spec.rb │ ├── quizzes_controller_spec.rb │ ├── registrations_controller_spec.rb │ ├── sections_controller_spec.rb │ ├── sessions_controller_spec.rb │ ├── tasks_controller_spec.rb │ ├── user_invitations_controller_spec.rb │ └── users_controller_spec.rb ├── factories │ ├── classroom_records.rb │ ├── classrooms.rb │ ├── courses.rb │ ├── lesson_records.rb │ ├── lessons.rb │ ├── quiz_answers.rb │ ├── quiz_attempts.rb │ ├── quiz_questions.rb │ ├── quizzes.rb │ ├── roles.rb │ ├── sections.rb │ ├── task_records.rb │ ├── task_runs.rb │ ├── tasks.rb │ ├── user_invitations.rb │ └── users.rb ├── features │ ├── classroom_main_page_spec.rb │ ├── oauth_sign_in_spec.rb │ ├── solve_classroom_problems_spec.rb │ ├── user_sign_in_spec.rb │ └── user_sign_up_spec.rb ├── fixtures │ └── quiz_attempt_answers.json ├── models │ ├── classroom_spec.rb │ ├── course_spec.rb │ ├── lesson_spec.rb │ ├── role_spec.rb │ ├── section_spec.rb │ └── user_invitation_spec.rb ├── rails_helper.rb ├── services │ ├── fetch_task_statistics_spec.rb │ └── score_quiz_attempt_spec.rb ├── spec_helper.rb └── support │ ├── factory_girl.rb │ ├── grader_helper.rb │ ├── omniauth.rb │ ├── quiz_helper.rb │ ├── shared_db_connection.rb │ └── webmock.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.env.example: -------------------------------------------------------------------------------- 1 | HOST=codemarathon.com 2 | PORT=80 3 | 4 | # Omniauth details 5 | GITHUB_APP_ID= 6 | GITHUB_SECRET= 7 | 8 | GOOGLE_APP_ID= 9 | GOOGLE_SECRET= 10 | 11 | FACEBOOK_APP_ID= 12 | FACEBOOK_SECRET= 13 | 14 | SECRET_KEY_BASE= 15 | 16 | # Mailgun details 17 | MAILGUN_PASSWORD= 18 | 19 | # Grader details 20 | GRADER_HOST= 21 | GRADER_EMAIL= 22 | GRADER_TOKEN= 23 | GRADER_USE_SSL=no 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | !/log/.keep 17 | /tmp 18 | 19 | .env 20 | 21 | config/database.yml 22 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require rails_helper 3 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | runs_worker: bundle exec rake runs:update_status RAILS_ENV=production 2 | jobs_worker: bundle exec rake jobs:work RAILS_ENV=production 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeMarathon 2 | ## Platform for hosting Computer Science online courses 3 | 4 | ### What is CodeMarathon? 5 | 6 | This is an open source platform for creating and managing online courses in the Computer Science domain. 7 | 8 | It allows teachers to add online course content and practice programming tasks for their students. Teachers can observe the progress of their students and provide feedback. 9 | 10 | The source code for CodeMarathon is hosted publicly in this repo. 11 | 12 | ### Online Courses Organization 13 | 14 | Teachers define course content organized in lessons. They can also add programming tasks for solving. 15 | 16 | When students enrol they can learn from the course materials and practice by writing code to solve the programming tasks. 17 | 18 | ### Rich Course Content 19 | 20 | Course lessons are defined using [Markdown](https://help.github.com/articles/markdown-basics/), which gives an easy way to present any rich content. 21 | 22 | Courses are organized in sections, which contain lessons. Lessons can be locked until a student completes all previous lessons. 23 | 24 | ### Practice Programming Tasks Graded in Real-time 25 | 26 | Students get to test their knowledge by solving programming problems included in the course by the teachers. 27 | 28 | Solutions are graded with several programming languages supported. The support for this comes from the [CodeMarathon Grader project](https://github.com/antonrd/codemarathon-grader) that we have built for the purpose. 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/assets/images/favicon.png -------------------------------------------------------------------------------- /app/assets/images/hiredintech_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/assets/images/hiredintech_logo.png -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require bootstrap-sprockets 16 | //= require bootstrap-markdown-bundle 17 | //= require ace-rails-ap 18 | //= require ace/theme-textmate 19 | //= require ace/mode-c_cpp 20 | //= require ace/mode-java 21 | //= require ace/mode-python 22 | //= require ace/mode-ruby 23 | //= require_tree . 24 | -------------------------------------------------------------------------------- /app/assets/javascripts/classrooms.js.coffee: -------------------------------------------------------------------------------- 1 | window.Codemarathon ||= {} 2 | 3 | Codemarathon.observeTaskRun = (classroom_id, lesson_id, task_id, task_run_id) -> 4 | setTimeout(Codemarathon.updateTaskRunPartial( 5 | classroom_id, lesson_id, task_id, task_run_id), 3000); 6 | 7 | Codemarathon.updateTaskRunPartial = (classroom_id, lesson_id, task_id, task_run_id) -> 8 | () -> 9 | $.ajax(url: "/classrooms/" + classroom_id + "/lesson/" + lesson_id + 10 | "/task/" + task_id + "/task_run/" + task_run_id) 11 | -------------------------------------------------------------------------------- /app/assets/javascripts/courses.js.coffee: -------------------------------------------------------------------------------- 1 | window.Codemarathon ||= {} 2 | 3 | $ -> 4 | $('[data-toggle="tooltip"]').tooltip() 5 | -------------------------------------------------------------------------------- /app/assets/javascripts/google_analytics.js: -------------------------------------------------------------------------------- 1 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 2 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 3 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 4 | })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); 5 | 6 | ga('create', 'UA-38407531-3', 'auto'); 7 | ga('send', 'pageview'); 8 | -------------------------------------------------------------------------------- /app/assets/javascripts/quizzes.js.coffee: -------------------------------------------------------------------------------- 1 | window.Codemarathon ||= {} 2 | 3 | # remove attachment field 4 | window.Codemarathon.removeField = (link) -> 5 | $(link).prev("input[type=hidden]").val("true") 6 | tag = $(link).closest("li") 7 | tag.hide("fade in").addClass("deleted") 8 | 9 | # add attachment field 10 | window.Codemarathon.addField = (link, association, content) -> 11 | new_id = new Date().getTime() 12 | regexp = new RegExp("new_" + association, "g") 13 | html = $(content.replace(regexp, new_id)).hide() 14 | html.appendTo($(link).closest("div.field").find("ul").first()).slideDown("slow") 15 | $('.quiz-markdown-editor').markdown({autofocus:false,savable:false}) 16 | 17 | $ -> 18 | $(document).on('change', 'input[type=radio].question-type', -> 19 | if $(this).val() == 'multiple' 20 | $(this).closest('.field').children('.freetext-answer').hide() 21 | $(this).closest('.field').children('span.multiple-answers').show() 22 | else 23 | $(this).closest('.field').children('span.multiple-answers').hide() 24 | $(this).closest('.field').children('.freetext-answer').show() 25 | ) 26 | 27 | -------------------------------------------------------------------------------- /app/assets/javascripts/sb-admin-2.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - SB Admin 2 v3.3.7+1 (http://startbootstrap.com/template-overviews/sb-admin-2) 3 | * Copyright 2013-2016 Start Bootstrap 4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap/blob/gh-pages/LICENSE) 5 | */ 6 | $(function() { 7 | $('#side-menu').metisMenu(); 8 | }); 9 | 10 | //Loads the correct sidebar on window load, 11 | //collapses the sidebar on window resize. 12 | // Sets the min-height of #page-wrapper to window size 13 | $(function() { 14 | $(window).bind("load resize", function() { 15 | var topOffset = 50; 16 | var width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width; 17 | if (width < 768) { 18 | $('div.navbar-collapse').addClass('collapse'); 19 | topOffset = 100; // 2-row-menu 20 | } else { 21 | $('div.navbar-collapse').removeClass('collapse'); 22 | } 23 | 24 | var height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1; 25 | height = height - topOffset; 26 | if (height < 1) height = 1; 27 | if (height > topOffset) { 28 | $("#page-wrapper").css("min-height", (height) + "px"); 29 | } 30 | }); 31 | 32 | var element = $('ul.nav a.lesson-link-' + gon.lesson_id).addClass('active').parent(); 33 | 34 | while (true) { 35 | if (element.is('li')) { 36 | element = element.parent().addClass('in').parent(); 37 | } else { 38 | break; 39 | } 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /app/assets/javascripts/smooth_scroll.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | // Add smooth scrolling to all links 3 | $("a").on('click', function(event) { 4 | 5 | // Make sure this.hash has a value before overriding default behavior 6 | if (this.hash !== "") { 7 | // Prevent default anchor click behavior 8 | event.preventDefault(); 9 | 10 | // Store hash 11 | var hash = this.hash; 12 | 13 | // Using jQuery's animate() method to add smooth page scroll 14 | // The optional number (800) specifies the number of milliseconds it takes to scroll to the specified area 15 | $('html, body').animate({ 16 | scrollTop: $(hash).offset().top 17 | }, 800, function(){ 18 | 19 | // Add hash (#) to URL when done scrolling (default click behavior) 20 | window.location.hash = hash; 21 | }); 22 | } // End if 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | // "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables" 2 | @import "bootstrap-sprockets"; 3 | @import "bootstrap"; 4 | @import "sb-admin-2"; 5 | @import "metisMenu"; 6 | @import "rails_bootstrap_forms"; 7 | @import "font-awesome-sprockets"; 8 | @import "font-awesome"; 9 | @import "bootstrap-social"; 10 | @import "bootstrap-markdown"; 11 | @import "github"; 12 | @import "common"; 13 | @import "courses"; 14 | @import "classrooms"; 15 | @import "sections"; 16 | @import "tasks"; 17 | @import "lessons"; 18 | @import "quizzes"; 19 | @import "sticky-footer-navbar"; 20 | @import "log-in-sign-up"; 21 | @import "devise-overrides"; 22 | @import "bootstrap-overrides"; 23 | -------------------------------------------------------------------------------- /app/assets/stylesheets/bootstrap-overrides.scss: -------------------------------------------------------------------------------- 1 | .navbar { 2 | .navbar-brand { 3 | min-height: 60px; 4 | padding: 7px 7px; 5 | } 6 | 7 | ul.navbar-top-links { 8 | li { 9 | a.main-item { 10 | min-height: 60px; 11 | line-height: 28px; 12 | } 13 | } 14 | } 15 | } 16 | 17 | .sidebar ul li a { 18 | color: #4a4a4a; 19 | } 20 | 21 | .sidebar ul li.section a { 22 | font-weight: 500; 23 | } 24 | 25 | .sidebar ul li.lesson a { 26 | font-weight: 400; 27 | } 28 | -------------------------------------------------------------------------------- /app/assets/stylesheets/devise-overrides.scss: -------------------------------------------------------------------------------- 1 | #error_explanation { 2 | border: 1px solid #ccc; 3 | background: #eee; 4 | padding: 15px; 5 | @include border-top-radius(10px); 6 | @include border-bottom-radius(10px); 7 | margin: 10px; 8 | 9 | h2 { 10 | font-size: 14px; 11 | color: #444; 12 | margin: 5px; 13 | } 14 | 15 | ul { 16 | font-size: 12px; 17 | margin: 5px; 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/log-in-sign-up.scss: -------------------------------------------------------------------------------- 1 | .main-log-in-panel { 2 | margin-bottom: 20px; 3 | } 4 | 5 | .form-fields-box { 6 | padding-left: 30px; 7 | padding-right: 30px; 8 | padding: 30px; 9 | } 10 | 11 | .social-login-buttons { 12 | padding: 30px; 13 | padding-top: 50px; 14 | } 15 | 16 | a.btn-social { 17 | margin-bottom: 15px; 18 | max-width: 250px; 19 | } 20 | 21 | .social-buttons-comment { 22 | margin-bottom: 15px; 23 | font-size: 16px; 24 | color: #444; 25 | } 26 | 27 | input.log-in-sign-up-button { 28 | width: 100px; 29 | } 30 | 31 | label.control-label { 32 | color: #888; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /app/assets/stylesheets/metisMenu.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * metismenu - v1.1.3 3 | * Easy menu jQuery plugin for Twitter Bootstrap 3 4 | * https://github.com/onokumus/metisMenu 5 | * 6 | * Made by Osman Nuri Okumus 7 | * Under MIT License 8 | */ 9 | .arrow { 10 | float: right; 11 | line-height: 1.42857; 12 | } 13 | 14 | .glyphicon.arrow:before { 15 | content: "\e079"; 16 | } 17 | 18 | .active > a > .glyphicon.arrow:before { 19 | content: "\e114"; 20 | } 21 | 22 | 23 | /* 24 | * Require Font-Awesome 25 | * http://fortawesome.github.io/Font-Awesome/ 26 | */ 27 | 28 | 29 | .fa.arrow:before { 30 | content: "\f104"; 31 | } 32 | 33 | .active > a > .fa.arrow:before { 34 | content: "\f107"; 35 | } 36 | 37 | .plus-times { 38 | float: right; 39 | } 40 | 41 | .fa.plus-times:before { 42 | content: "\f067"; 43 | } 44 | 45 | .active > a > .fa.plus-times { 46 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 47 | -webkit-transform: rotate(45deg); 48 | -moz-transform: rotate(45deg); 49 | -ms-transform: rotate(45deg); 50 | -o-transform: rotate(45deg); 51 | transform: rotate(45deg); 52 | } 53 | 54 | .plus-minus { 55 | float: right; 56 | } 57 | 58 | .fa.plus-minus:before { 59 | content: "\f067"; 60 | } 61 | 62 | .active > a > .fa.plus-minus:before { 63 | content: "\f068"; 64 | } 65 | -------------------------------------------------------------------------------- /app/assets/stylesheets/quizzes.scss: -------------------------------------------------------------------------------- 1 | ul.quiz-questions { 2 | padding-left: 0px; 3 | li.quiz-question { 4 | list-style: none; 5 | border: #bbb 2px solid; 6 | padding: 15px; 7 | margin-top: 10px; 8 | margin-bottom: 40px; 9 | border-radius: 5px; 10 | } 11 | } 12 | 13 | ul.quiz-answers { 14 | padding-left: 0px; 15 | li { 16 | list-style: none; 17 | padding: 15px; 18 | margin-top: 10px; 19 | margin-bottom: 10px; 20 | border-radius: 5px; 21 | background-color: #e6fff2; 22 | border: #ddd 1px solid; 23 | } 24 | 25 | .remove-element { 26 | margin-bottom: 15px; 27 | } 28 | } 29 | 30 | .quiz-title { 31 | margin-bottom: 30px; 32 | } 33 | 34 | .quiz-limits, .quiz-attempt-details { 35 | font-size: 18px; 36 | margin-bottom: 10px; 37 | color: #555; 38 | } 39 | 40 | .quiz-start { 41 | margin-top: 40px; 42 | } 43 | 44 | .quiz-attempts { 45 | margin-top: 40px; 46 | 47 | h3 { 48 | margin-bottom: 25px; 49 | } 50 | } 51 | 52 | .quiz-question-text { 53 | margin-bottom: 10px; 54 | font-weight: 500; 55 | font-size: 16px; 56 | } 57 | 58 | .quiz-question { 59 | ul { 60 | li { 61 | list-style: none; 62 | font-size: 16px; 63 | } 64 | } 65 | } 66 | 67 | .quiz-question-answers { 68 | label { 69 | font-weight: normal; 70 | display: block; 71 | } 72 | } 73 | 74 | .quiz-question-score { 75 | margin-top: 30px; 76 | } 77 | 78 | .quiz-attempt input[type=checkbox]{ 79 | float: left; 80 | margin-right: 10px; 81 | } 82 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sections.scss: -------------------------------------------------------------------------------- 1 | tr.section { 2 | background-color: #99ccff; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sticky-footer-navbar.scss: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | html { 4 | position: relative; 5 | min-height: 100%; 6 | } 7 | body { 8 | /* Margin bottom by footer height */ 9 | margin-bottom: 60px; 10 | } 11 | .footer { 12 | position: absolute; 13 | bottom: 0; 14 | width: 100%; 15 | /* Set the fixed height of the footer here */ 16 | height: 60px; 17 | background-color: #f8f8f8; 18 | border-top: 1px solid #e7e7e7; 19 | } 20 | 21 | 22 | /* Custom page CSS 23 | -------------------------------------------------- */ 24 | /* Not required for template or sticky footer method. */ 25 | 26 | body > .container { 27 | padding: 60px 15px 0; 28 | } 29 | .container .text-muted { 30 | margin: 20px 0; 31 | } 32 | 33 | .footer > .container { 34 | padding-right: 15px; 35 | padding-left: 15px; 36 | } 37 | 38 | code { 39 | font-size: 80%; 40 | } 41 | -------------------------------------------------------------------------------- /app/assets/stylesheets/tasks.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the tasks controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass here: http://sass-lang.com/ 4 | .source-code-editor { 5 | height: 400px; 6 | } 7 | 8 | .source-code-editor-small { 9 | height: 200px; 10 | } 11 | 12 | .max-attempts-notice { 13 | font-size: 16px; 14 | font-weight: bold; 15 | } 16 | 17 | tr.error { 18 | background-color: #ffe6cc; 19 | } 20 | -------------------------------------------------------------------------------- /app/assets/stylesheets/users.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /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 | rescue_from ActiveRecord::RecordInvalid, with: :record_invalid 7 | rescue_from ActiveRecord::RecordNotFound, with: :record_not_found 8 | 9 | protected 10 | 11 | def after_sign_in_path_for(resource) 12 | stored_location_for(:user) || root_path 13 | end 14 | 15 | def require_admin_role 16 | require_role(User::ROLE_ADMIN) 17 | end 18 | 19 | def require_teacher_role 20 | require_role(User::ROLE_TEACHER) 21 | end 22 | 23 | def require_role role_type 24 | unless current_user.present? && current_user.has_role?(role_type) 25 | redirect_to root_path, alert: "Invalid page" 26 | end 27 | end 28 | 29 | def record_invalid(exception) 30 | render json: { errors: exception.record.errors.full_messages }, status: :unprocessable_entity 31 | end 32 | 33 | def record_not_found(exception) 34 | head :not_found 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/confirmations_controller.rb: -------------------------------------------------------------------------------- 1 | class ConfirmationsController < Devise::ConfirmationsController 2 | before_action :check_user_not_signed_in 3 | 4 | def show 5 | matching_user = User.confirm_by_token(params[:confirmation_token]) 6 | 7 | if matching_user.errors.empty? 8 | redirect_to new_user_session_path, notice: "Account confirmed successfully! Try to log in." 9 | else 10 | message = matching_user.errors.full_messages.join(". ") 11 | redirect_to new_user_session_path, alert: message 12 | end 13 | end 14 | 15 | def create 16 | user = User.find_by_email(params[:user][:email]) 17 | user.send_confirmation_instructions if user 18 | 19 | redirect_to new_user_session_path, notice: "Confirmation instructions sent." 20 | end 21 | 22 | protected 23 | 24 | def check_user_not_signed_in 25 | redirect_to root_path, alert: "You are already signed in" if user_signed_in? 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/controllers/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class OmniauthCallbacksController < Devise::OmniauthCallbacksController 2 | def google_oauth2 3 | handle_authentication("Google", "devise.google_data") 4 | end 5 | 6 | def github 7 | handle_authentication("GitHub", "devise.github_data") 8 | end 9 | 10 | def facebook 11 | handle_authentication("Facebook", "devise.facebook_data") 12 | end 13 | 14 | protected 15 | 16 | def handle_authentication(kind, session_key) 17 | @user = User.from_omniauth(request.env["omniauth.auth"]) 18 | 19 | if @user.persisted? 20 | if @user.active? 21 | sign_in_and_redirect @user, event: :authentication 22 | else 23 | redirect_to new_user_session_path, 24 | alert: "This is a limited private beta site yet. "\ 25 | "Your user is not activated at the moment. "\ 26 | "We will notify you once we open access to your account." 27 | end 28 | else 29 | session[session_key] = request.env["omniauth.auth"] 30 | redirect_to root_path 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | class PagesController < ApplicationController 2 | def home 3 | course = Course.main.first 4 | if course.present? 5 | redirect_to course_path(course) 6 | else 7 | redirect_to courses_path 8 | end 9 | end 10 | 11 | def about_codemarathon 12 | @wide_page = "wide" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/controllers/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | class PasswordsController < Devise::PasswordsController 2 | # This custom action is added in order not to show an error message when 3 | # there is no matching user. 4 | def create 5 | user = User.find_by_email(params[:user][:email]) 6 | user.send_reset_password_instructions if user 7 | 8 | redirect_to new_user_session_path, notice: "Instructions for resetting your password were sent" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class RegistrationsController < Devise::RegistrationsController 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/sections_controller.rb: -------------------------------------------------------------------------------- 1 | class SectionsController < ApplicationController 2 | before_action :authenticate_user! 3 | before_action :require_teacher_role 4 | 5 | def create 6 | course = Course.find(section_params[:course_id]) 7 | 8 | if Section.create!(section_params.merge(position: course.last_section_position + 1)) 9 | redirect_to edit_structure_course_path(course), notice: "Section created successfully" 10 | else 11 | redirect_to edit_structure_course_path(course), alert: "Failed to create new section" 12 | end 13 | end 14 | 15 | def update 16 | if section.update_attributes(section_params) 17 | redirect_to edit_structure_course_path(course), notice: "Course updated successfully" 18 | else 19 | redirect_to edit_structure_course_path(course), alert: "Failed to update course contents" 20 | end 21 | end 22 | 23 | def destroy 24 | section.destroy 25 | 26 | redirect_to edit_structure_course_path(course), notice: "Section deleted" 27 | end 28 | 29 | def move_up 30 | section.move_up 31 | 32 | redirect_to edit_structure_course_path(course) 33 | end 34 | 35 | def move_down 36 | section.move_down 37 | 38 | redirect_to edit_structure_course_path(course) 39 | end 40 | 41 | protected 42 | 43 | def section 44 | @section ||= Section.find(params[:id]) 45 | end 46 | 47 | def course 48 | section.course 49 | end 50 | 51 | def section_params 52 | params.require(:section).permit(:title, :course_id, :visible) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < Devise::SessionsController 2 | before_action :check_if_active_user, only: :create 3 | 4 | private 5 | 6 | def check_if_active_user 7 | user = User.find_by(email: params[:user][:email]) 8 | 9 | user.set_active_field if user 10 | 11 | if user && !user.active 12 | redirect_to new_user_session_path, 13 | alert: "This is a limited private beta site yet. "\ 14 | "Your user is not activated at the moment. "\ 15 | "We will notify you once we open access to your account." 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/helpers/devise_helper.rb: -------------------------------------------------------------------------------- 1 | module DeviseHelper 2 | def devise_error_messages! override_sentence: nil 3 | return "" if resource.errors.empty? 4 | 5 | messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join 6 | sentence = override_sentence || I18n.t("errors.messages.not_saved", 7 | :count => resource.errors.count, 8 | :resource => resource.class.model_name.human.downcase) 9 | 10 | html = <<-HTML 11 |
12 |

#{sentence}

13 | 14 |
15 | HTML 16 | 17 | html.html_safe 18 | end 19 | 20 | def devise_error_messages? 21 | resource.errors.empty? ? false : true 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /app/helpers/highlight_helper.rb: -------------------------------------------------------------------------------- 1 | module HighlightHelper 2 | def highlight_source_code(source_code, language) 3 | formatter = Rouge::Formatters::HTMLLegacy.new(css_class: 'highlight') 4 | lexer = language_lexer(language) 5 | formatter.format(lexer.lex(source_code)) 6 | end 7 | 8 | protected 9 | 10 | def language_lexer(language) 11 | case language 12 | when 'cpp' 13 | Rouge::Lexers::Cpp.new 14 | when 'python' 15 | Rouge::Lexers::Python.new 16 | when 'ruby' 17 | Rouge::Lexers::Ruby.new 18 | when 'java' 19 | Rouge::Lexers::Java.new 20 | else 21 | Rouge::Lexers::PlainText.new 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/helpers/menu_helper.rb: -------------------------------------------------------------------------------- 1 | module MenuHelper 2 | def lesson_menu_icon lesson 3 | if lesson.tasks.present? 4 | 'tasks' 5 | elsif lesson.quizzes.present? 6 | 'education' 7 | else 8 | 'book' 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/helpers/quiz_helper.rb: -------------------------------------------------------------------------------- 1 | module QuizHelper 2 | def link_to_remove_field(name, f) 3 | f.hidden_field(:_destroy) + 4 | __link_to_function(raw(name), "window.Codemarathon.removeField(this)", 5 | class: "btn btn-small btn-default remove-element") 6 | end 7 | 8 | def link_to_add_field(name, f, association) 9 | new_object = f.object.class.reflect_on_association(association).klass.new 10 | fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder| 11 | render(association.to_s.singularize + "_fields", :f => builder) 12 | end 13 | __link_to_function(name, "window.Codemarathon.addField(this, \"#{association}\", \"#{escape_javascript(fields)}\")", 14 | id: "add-attach", class: "btn btn-small btn-default") 15 | end 16 | 17 | private 18 | 19 | def __link_to_function(name, on_click_event, opts={}) 20 | link_to(name, 'javascript:;', opts.merge(onclick: on_click_event)) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/helpers/task_run_display_helper.rb: -------------------------------------------------------------------------------- 1 | module TaskRunDisplayHelper 2 | def task_run_display_class task_run 3 | if task_run.accepted? 4 | "success complete" 5 | elsif task_run.with_errors? 6 | "warning complete" 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/helpers/theme_helper.rb: -------------------------------------------------------------------------------- 1 | module ThemeHelper 2 | def classroom_mode? 3 | defined?(@classroom) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/mailers/.keep -------------------------------------------------------------------------------- /app/mailers/user_invitations_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserInvitationsMailer < ActionMailer::Base 2 | default from: 'HiredInTech ' 3 | 4 | def invitation_without_user(user_invitation) 5 | mail(to: user_invitation.email, subject: "Your invitation to HiredInTech's Interview Training Camp") 6 | end 7 | 8 | def invitation_with_user(user_invitation) 9 | mail(to: user_invitation.email, subject: "Your invitation to HiredInTech's Interview Training Camp") 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/models/.keep -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/classroom_record.rb: -------------------------------------------------------------------------------- 1 | class ClassroomRecord < ApplicationRecord 2 | belongs_to :classroom 3 | belongs_to :user 4 | 5 | validates :classroom, presence: true 6 | validates :user, presence: true 7 | validates :role, presence: true 8 | validates :user, uniqueness: { scope: :classroom } 9 | 10 | ROLE_ADMIN = :admin 11 | ROLE_STUDENT = :student 12 | 13 | scope :admin, -> { where(role: ROLE_ADMIN) } 14 | scope :student, -> { where(role: ROLE_STUDENT) } 15 | scope :active, -> { where(active: true) } 16 | scope :inactive, -> { where(active: false) } 17 | end 18 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/lesson_record.rb: -------------------------------------------------------------------------------- 1 | class LessonRecord < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :lesson 4 | belongs_to :classroom 5 | 6 | validates :lesson, presence: true 7 | validates :user, presence: true 8 | validates :classroom, presence: true 9 | validates :views, presence: true 10 | validates :covered, inclusion: { in: [true, false] } 11 | 12 | def add_view 13 | update_attributes( 14 | views: views + 1, 15 | covered: lesson.is_covered?(user) 16 | ) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/quiz.rb: -------------------------------------------------------------------------------- 1 | class Quiz < ApplicationRecord 2 | has_many :quiz_questions, inverse_of: :quiz 3 | has_many :quiz_attempts 4 | belongs_to :creator, class_name: "User", foreign_key: "creator_id" 5 | has_and_belongs_to_many :lessons 6 | 7 | accepts_nested_attributes_for :quiz_questions, allow_destroy: true 8 | 9 | validates :title, presence: true 10 | validates :creator, presence: true 11 | 12 | def self.unused_quizzes_for lesson 13 | where.not(id: lesson.quizzes.map(&:id)) 14 | end 15 | 16 | def attempts_depleted? user 17 | maximum_attempts && user_attempts(user).count >= maximum_attempts 18 | end 19 | 20 | def last_attempt_too_soon? user 21 | wait_time_seconds && user_attempts(user).present? && 22 | passed_time_since_last_attempt(user) < wait_time_seconds 23 | end 24 | 25 | def user_attempts user 26 | quiz_attempts.where(user: user) 27 | end 28 | 29 | def maximum_score user 30 | if user_attempts(user).present? 31 | user_attempts(user).maximum(:score).round(2) 32 | else 33 | 0.0 34 | end 35 | end 36 | 37 | def is_covered_by? user 38 | maximum_score(user) == max_quiz_points 39 | end 40 | 41 | def max_quiz_points 42 | maximum_points = quiz_questions.count 43 | end 44 | 45 | private 46 | 47 | def passed_time_since_last_attempt user 48 | Time.now - user_attempts(user).latest_first.first.created_at 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /app/models/quiz_answer.rb: -------------------------------------------------------------------------------- 1 | class QuizAnswer < ApplicationRecord 2 | before_save :render_markdown_content 3 | 4 | belongs_to :quiz_question, inverse_of: :quiz_answers 5 | 6 | validates :quiz_question, presence: true 7 | validates :markdown_content, presence: true 8 | validates :correct, inclusion: { in: [true, false] } 9 | 10 | def correct_answer? answer 11 | correct == answer 12 | end 13 | 14 | protected 15 | 16 | def render_markdown_content 17 | self.content = RenderMarkdown.new(markdown_content).call 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/quiz_attempt.rb: -------------------------------------------------------------------------------- 1 | class QuizAttempt < ApplicationRecord 2 | belongs_to :quiz 3 | belongs_to :user 4 | 5 | validates :quiz, presence: true 6 | validates :user, presence: true 7 | validates :score, presence: true 8 | validates :answers_json, presence: true 9 | 10 | scope :latest_first, -> { order('created_at DESC') } 11 | 12 | def deserialize_answers 13 | JSON.parse(answers_json).with_indifferent_access 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/quiz_question.rb: -------------------------------------------------------------------------------- 1 | class QuizQuestion < ApplicationRecord 2 | before_save :render_markdown_content 3 | before_save :render_markdown_explanation 4 | 5 | belongs_to :quiz, inverse_of: :quiz_questions 6 | has_many :quiz_answers, inverse_of: :quiz_question 7 | 8 | accepts_nested_attributes_for :quiz_answers, allow_destroy: true 9 | 10 | validates :quiz, presence: true 11 | validates :question_type, presence: true 12 | validates :markdown_content, presence: true 13 | 14 | TYPE_MULTIPLE_CHOICE = 'multiple' 15 | TYPE_FREETEXT = 'freetext' 16 | 17 | scope :ordered, -> { order("created_at ASC") } 18 | 19 | def multiple_choice? 20 | question_type == TYPE_MULTIPLE_CHOICE 21 | end 22 | 23 | def freetext? 24 | question_type == TYPE_FREETEXT 25 | end 26 | 27 | def correct_freetext_answer? answer 28 | freetext? && Regexp.new(freetext_regex).match(answer) 29 | end 30 | 31 | protected 32 | 33 | def render_markdown_content 34 | self.content = RenderMarkdown.new(markdown_content).call 35 | end 36 | 37 | def render_markdown_explanation 38 | self.explanation = RenderMarkdown.new(markdown_explanation).call if markdown_explanation.present? 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ApplicationRecord 2 | belongs_to :user 3 | 4 | validates :user, presence: true 5 | validates :role_type, presence: true 6 | validates :role_type, inclusion: { in: User::ROLES.map(&:to_s) } 7 | end 8 | -------------------------------------------------------------------------------- /app/models/settings.rb: -------------------------------------------------------------------------------- 1 | class Settings < Settingslogic 2 | source "#{Rails.root}/config/application.yml" 3 | namespace Rails.env 4 | 5 | def localized 6 | public_send(I18n.locale) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/models/task_record.rb: -------------------------------------------------------------------------------- 1 | class TaskRecord < ActiveRecord::Base 2 | DEFAULT_MAX_RUNS_LIMIT = 8 3 | 4 | belongs_to :user 5 | belongs_to :task 6 | belongs_to :best_run, foreign_key: 'best_run_id', class_name: "TaskRun" 7 | 8 | validates :user, presence: true 9 | validates :task, presence: true 10 | validates :best_score, presence: true 11 | validates :covered, inclusion: { in: [true, false] } 12 | validates :runs_limit, presence: true 13 | end 14 | -------------------------------------------------------------------------------- /app/models/user_invitation.rb: -------------------------------------------------------------------------------- 1 | class UserInvitation < ApplicationRecord 2 | validates :email, presence: true, uniqueness: true 3 | end 4 | -------------------------------------------------------------------------------- /app/services/create_course.rb: -------------------------------------------------------------------------------- 1 | class CreateCourse 2 | def initialize course_params, creating_user 3 | @course_params = course_params 4 | @creating_user = creating_user 5 | end 6 | 7 | def call 8 | course = nil 9 | Course.transaction do 10 | course = Course.create(course_params) 11 | classroom = course.classrooms.create(name: "Default classroom", slug: course.slug) 12 | return unless classroom.persisted? 13 | classroom.add_admin(creating_user) 14 | end 15 | 16 | course 17 | end 18 | 19 | protected 20 | 21 | attr_reader :course_params, :creating_user 22 | end 23 | -------------------------------------------------------------------------------- /app/services/render_markdown.rb: -------------------------------------------------------------------------------- 1 | require 'rouge/plugins/redcarpet' 2 | 3 | class RenderMarkdown 4 | class Renderer < Redcarpet::Render::HTML 5 | include Rouge::Plugins::Redcarpet 6 | end 7 | 8 | def initialize(markdown) 9 | @markdown = markdown 10 | end 11 | 12 | def call 13 | render 14 | end 15 | 16 | protected 17 | 18 | attr_reader :markdown 19 | 20 | def markdown_renderer 21 | @markdown_renderer ||= Redcarpet::Markdown.new(Renderer, 22 | autolink: true, tables: true, fenced_code_blocks: true, hard_wrap: true) 23 | end 24 | 25 | def render 26 | markdown_renderer.render(markdown) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/services/update_user_with_task_run.rb: -------------------------------------------------------------------------------- 1 | class UpdateUserWithTaskRun 2 | def initialize run 3 | @run = run 4 | end 5 | 6 | def call 7 | update_lesson_records if update_task_record 8 | end 9 | 10 | protected 11 | 12 | attr_reader :run 13 | 14 | def update_task_record 15 | return false unless task_record.present? 16 | 17 | covered = false 18 | if run.points > task_record.best_score 19 | puts "Updating task #{ task.id } for run #{ run.id } with #{ run.points } points ..." 20 | covered = task_record.covered || run.points == Task::TASK_MAX_POINTS 21 | 22 | task_record.update_attributes( 23 | best_score: run.points, 24 | best_run_id: run.id, 25 | covered: covered 26 | ) 27 | end 28 | 29 | covered 30 | end 31 | 32 | def task 33 | @task ||= run.task 34 | end 35 | 36 | def task_record 37 | @task_record ||= TaskRecord.find_or_create_by(user: user, task: task) 38 | end 39 | 40 | def user 41 | @user ||= run.user 42 | end 43 | 44 | def update_lesson_records 45 | task.lessons.each do |lesson| 46 | lesson.cover_lesson_records_if_lesson_covered(user) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /app/views/classrooms/_compilation_error_details.html.erb: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /app/views/classrooms/_enrol_or_login_modal.html.erb: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_bottom_navigation.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <% if @prev_lesson.present? %> 4 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @prev_lesson.id) do %> 5 | 6 | Previous 7 | <% end %> 8 | <% end %> 9 |
10 | 11 |
12 | <% if @next_lesson.present? %> 13 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @next_lesson.id) do %> 14 | Next 15 | 16 | <% end %> 17 | <% end %> 18 |
19 |
20 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_content.html.erb: -------------------------------------------------------------------------------- 1 |
<%= render inline: @lesson.content %>
2 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_links_to_quizzes.html.erb: -------------------------------------------------------------------------------- 1 |
2 | Practice quizzes in this section 3 |
4 | <% @section_quizzes.each do |lesson, quiz| %> 5 | 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_links_to_tasks.html.erb: -------------------------------------------------------------------------------- 1 |
2 | Practice tasks in this section 3 |
4 | <% @section_tasks.each do |lesson, task| %> 5 | 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_sidebar_content.html.erb: -------------------------------------------------------------------------------- 1 | <% if @lesson.sidebar_content.present? %> 2 |
3 | <%= render inline: @lesson.sidebar_content %> 4 |
5 | <% end %> 6 | <% unless @section_tasks.empty? || @lesson.tasks.any? %> 7 | 10 | <% end %> 11 | <% unless @section_quizzes.empty? || @lesson.quizzes.any? %> 12 | 15 | <% end %> 16 | 17 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_tasks.html.erb: -------------------------------------------------------------------------------- 1 | <% if @lesson.tasks.present? %> 2 |
3 |
4 | Tasks to solve in this lesson 5 |
6 | 7 |
8 |
9 | <% @lesson.tasks.by_creation.each do |task| %> 10 | <% if current_user && @classroom.has_access?(current_user) %> 11 | <%= link_to lesson_task_classroom_path(@classroom, lesson_id: @lesson, task_id: task), class: "list-group-item" do %> 12 | <%= render partial: 'task_summary_panel', locals: { task: task } %> 13 | <% end %> 14 | <% else %> 15 | 16 | <%= render partial: 'task_summary_panel', locals: { task: task } %> 17 | 18 | <%= render partial: 'enrol_or_login_modal', locals: { classroom: @classroom, lesson: @lesson } %> 19 | <% end %> 20 | <% end %> 21 |
22 |
23 |
24 | <% end %> 25 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_title.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <% if @prev_lesson.present? %> 3 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @prev_lesson.id) do %> 4 | 5 | <% end %> 6 | <% end %> 7 |
8 | 9 |
10 |

<%= @lesson.title %>

11 |
12 | 13 |
14 | <% if @next_lesson.present? %> 15 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @next_lesson.id) do %> 16 | 17 | <% end %> 18 | <% end %> 19 |
20 | -------------------------------------------------------------------------------- /app/views/classrooms/_lesson_top_links.html.erb: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/views/classrooms/_runtime_errors_details.html.erb: -------------------------------------------------------------------------------- 1 | 40 | -------------------------------------------------------------------------------- /app/views/classrooms/_solution_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render partial: 'task_sidebar_limits' %> 3 | 4 | <%= link_to lesson_task_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item' do %> 5 | 6 | Go to task 7 | <% end %> 8 | <%= link_to task_runs_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item' do %> 9 | 10 | Your task runs 11 | <%= @user_runs_count %> 12 | <% end %> 13 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @lesson.id), class: 'list-group-item' do %> 14 | 15 | Back to lesson 16 | <% end %> 17 | <%= link_to task_successful_runs_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item list-group-item-warning' do %> 18 | 19 | Successful solutions 20 | <% end %> 21 |
22 | -------------------------------------------------------------------------------- /app/views/classrooms/_solve_task_panel.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
Solve the task
3 | 4 |
5 | <% unless @classroom.course.disable_task_submissions %> 6 | <% if @user_runs_count >= @runs_limit %> 7 |

You have reached the maximum allowed number of <%= @runs_limit %> attempts for this task

8 | <% else %> 9 |

<%= @user_runs_count %> out of <%= @runs_limit %> attempts made for this task so far

10 | <%= form_for @classroom, url: solve_task_classroom_path(lesson_id: @lesson.id, task_id: @task.id), method: :post do |f| %> 11 | <%= render partial: 'tasks/solve', locals: { form: f, task: @task, run: @task_run } %> 12 | <% end %> 13 | <% end %> 14 | <% else %> 15 |

Task submissions are temporarily disabled for this course. Check again later, we are working on enabling them.

16 | <% end %> 17 |
18 |
19 | -------------------------------------------------------------------------------- /app/views/classrooms/_successful_runs_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render partial: 'task_sidebar_limits' %> 3 | 4 | <%= link_to lesson_task_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item' do %> 5 | 6 | Go to task 7 | <% end %> 8 | <%= link_to task_runs_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item' do %> 9 | 10 | Your task runs 11 | <%= @user_runs_count %> 12 | <% end %> 13 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @lesson.id), class: 'list-group-item' do %> 14 | 15 | Back to lesson 16 | <% end %> 17 | <%= link_to task_solution_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item list-group-item-warning' do %> 18 | 19 |  See the solution 20 | <% end %> 21 |
22 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_limits_help.html.erb: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_run.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= task_run.id %> 3 | 4 | <%= render partial: 'classrooms/task_run_display_status', locals: { classroom: classroom, lesson: lesson, task: task, task_run: task_run } %> 5 | 6 | <%= task_run.lang %> 7 | <%= task_run.memory_limit_kb %> KB 8 | <%= task_run.time_limit_ms %> ms 9 | <%= task_run.points %> 10 | <%= task_run.created_at.to_s(:db) %> 11 | 12 | <%= render partial: 'tasks/task_run_source_code', locals: { classroom: classroom, lesson: lesson, task: task, task_run: task_run } %> 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_run_display_status.html.erb: -------------------------------------------------------------------------------- 1 | <% if task_run.in_progress? %> 2 | <%= link_to task_run_classroom_path(classroom, lesson.id, task.id, task_run.id), remote: true do %> 3 | 4 | <% end %> 5 | 6 | 9 | <% end %> 10 | 11 | <% if task_run.display_status == "Compilation Error" %> 12 | 13 | <%= task_run.display_status %> 14 | 15 | 16 | <%= render partial: 'classrooms/compilation_error_details', locals: { task_run: task_run } %> 17 | <% elsif task_run.display_status == "Some Errors" %> 18 | 19 | <%= task_run.display_status %> 20 | 21 | 22 | <%= render partial: 'classrooms/runtime_errors_details', locals: { task_run: task_run } %> 23 | <% else %> 24 | <% if task_run.display_status == "Accepted" %> 25 | <%= task_run.display_status %> 26 | <% else %> 27 | <%= task_run.display_status %> 28 | <% end %> 29 | <% end %> 30 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_runs.html.erb: -------------------------------------------------------------------------------- 1 | <% if @user_runs.empty? %> 2 |
3 |

No task runs attempted yet

4 |
5 | <% else %> 6 | <%= paginate @user_runs, theme: 'twitter-bootstrap-3' %> 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @user_runs.each do |task_run| %> 19 | <%= render partial: 'task_run', locals: { classroom: @classroom, lesson: @lesson, task: @task, task_run: task_run } %> 20 | <% end %> 21 |
IDStatusLanguageMemoryTimePointsDate
22 | <%= paginate @user_runs, theme: 'twitter-bootstrap-3' %> 23 | <% end %> 24 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_runs_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render partial: 'task_sidebar_limits' %> 3 | 4 | <%= link_to lesson_task_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item' do %> 5 | 6 | Go to task 7 | <% end %> 8 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @lesson.id), class: 'list-group-item' do %> 9 | 10 | Back to lesson 11 | <% end %> 12 | <%= link_to task_solution_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item list-group-item-warning' do %> 13 | 14 |  See the solution 15 | <% end %> 16 | <%= link_to task_successful_runs_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item list-group-item-warning' do %> 17 | 18 | Successful solutions 19 | <% end %> 20 |
21 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= render partial: 'task_sidebar_limits' %> 3 | 4 | <%= link_to '#solve-task', class: 'list-group-item list-group-item-success' do %> 5 | 6 |  Solve the task 7 | <% end %> 8 | <%= link_to task_runs_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item' do %> 9 | 10 | Your task runs 11 | <%= @user_runs_count %> 12 | <% end %> 13 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @lesson.id), class: 'list-group-item' do %> 14 | 15 | Back to lesson 16 | <% end %> 17 | <%= link_to task_solution_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item list-group-item-warning' do %> 18 | 19 |  See the solution 20 | <% end %> 21 | <%= link_to task_successful_runs_classroom_path(@classroom, lesson_id: @lesson.id, task_id: @task.id), class: 'list-group-item list-group-item-warning' do %> 22 | 23 | Successful solutions 24 | <% end %> 25 |
26 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_sidebar_limits.html.erb: -------------------------------------------------------------------------------- 1 |
2 | Time limit 3 | 4 | 5 | 6 |

<%= number_with_delimiter(@task.time_limit_ms) %> ms

7 |
8 |
9 | Memory limit 10 | 11 | 12 | 13 |

<%= number_with_delimiter(@task.memory_limit_kb) %> KB

14 |
15 | 16 | <%= render partial: 'task_limits_help' %> 17 | -------------------------------------------------------------------------------- /app/views/classrooms/_task_summary_panel.html.erb: -------------------------------------------------------------------------------- 1 | <%= task.title %> 2 |
3 | <% if task.is_covered_by?(current_user) %> 4 | Solved 5 | <% else %> 6 | Pending 7 | <% end %> 8 | Attempts: <%= task.valid_user_runs(current_user).count %> 9 |
10 | -------------------------------------------------------------------------------- /app/views/classrooms/attempt_quiz.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

<%= @quiz.title %>

9 |
10 |
11 | 12 |
13 |
14 | <%= render partial: 'quizzes/attempt', locals: { quiz: @quiz, path_to_submit_quiz: submit_quiz_classroom_path(@classroom, lesson_id: @lesson.id, quiz_id: @quiz.id) } %> 15 |
16 | 17 |
18 |
19 | <%= render partial: 'classrooms/quizzes/attempt_quiz_sidebar' %> 20 |
21 |
22 |
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /app/views/classrooms/lesson.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <% if @lesson.present? %> 4 |
5 |
6 | 7 | <%= render partial: 'lesson_top_links' %> 8 | 9 |
10 | <%= render partial: 'lesson_title' %> 11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 | <%= render partial: 'lesson_tasks' %> 19 | <%= render partial: 'classrooms/quizzes/lesson_quizzes' %> 20 | <%= render partial: 'lesson_content' %> 21 |
22 |
23 |
24 |
25 | <%= render partial: 'lesson_sidebar_content' %> 26 |
27 |
28 |
29 | 30 |
31 |
32 | <%= render partial: 'lesson_bottom_navigation' %> 33 |
34 |
35 | <% else %> 36 |

This course has no lessons yet

37 | <% end %> 38 |
39 |
40 | -------------------------------------------------------------------------------- /app/views/classrooms/lesson_quiz.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

<%= @quiz.title %>

9 |
10 |
11 |
12 | 13 |
14 |
15 |
16 |
17 |
18 | <% if @quiz.attempts_depleted?(current_user) %> 19 |

You have no attempts left

20 | <% elsif @quiz.last_attempt_too_soon?(current_user) %> 21 |

Your last quiz attempt was too soon

22 | <% else %> 23 | <%= link_to 'Start the quiz', attempt_quiz_classroom_path(@classroom, lesson_id: @lesson.id, quiz_id: @quiz.id), class: 'btn btn-lg btn-success' %> 24 | <% end %> 25 |
26 |
27 |
28 | 29 | <%= render partial: 'classrooms/quizzes/user_attempts', locals: { quiz: @quiz, quiz_attempts: @quiz.quiz_attempts.where(user: current_user), user: current_user } %> 30 |
31 | 32 |
33 |
34 | <%= render partial: 'classrooms/quizzes/quiz_sidebar' %> 35 |
36 |
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /app/views/classrooms/lesson_task.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

Task: <%= @task.title %>

9 |
10 | 11 |
12 |
13 |
14 |
<%= render inline: @task.description %>
15 | 16 | <%= render partial: 'solve_task_panel' %> 17 |
18 |
19 |
20 |
21 | <%= render partial: 'task_sidebar' %> 22 |
23 |
24 |
25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /app/views/classrooms/quizzes/_attempt_quiz_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= render partial: 'classrooms/quizzes/quiz_sidebar_limits' %> 4 | 5 | <%= link_to lesson_quiz_classroom_path(@classroom, lesson_id: @lesson.id, quiz_id: @quiz.id), class: 'list-group-item' do %> 6 | 7 | Back to quiz page 8 | <% end %> 9 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @lesson.id), class: 'list-group-item' do %> 10 | 11 | Back to lesson 12 | <% end %> 13 |
14 | -------------------------------------------------------------------------------- /app/views/classrooms/quizzes/_lesson_quizzes.html.erb: -------------------------------------------------------------------------------- 1 | <% if @lesson.quizzes.present? %> 2 |
3 |
4 | Quizzes to solve in this lesson 5 |
6 | 7 |
8 |
9 | <% @lesson.quizzes.each do |quiz| %> 10 | <% if current_user && @classroom.has_access?(current_user) %> 11 | <%= link_to lesson_quiz_classroom_path(@classroom, lesson_id: @lesson, quiz_id: quiz), class: "list-group-item" do %> 12 | <%= render partial: 'classrooms/quizzes/quiz_summary_panel', locals: { quiz: quiz } %> 13 | <% end %> 14 | <% else %> 15 | 16 | <%= render partial: 'classrooms/quizzes/quiz_summary_panel', locals: { quiz: quiz } %> 17 | 18 | <%= render partial: 'enrol_or_login_modal', locals: { classroom: @classroom, lesson: @lesson } %> 19 | <% end %> 20 | <% end %> 21 |
22 |
23 |
24 | <% end %> 25 | -------------------------------------------------------------------------------- /app/views/classrooms/quizzes/_quiz_sidebar.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | <%= render partial: 'classrooms/quizzes/quiz_sidebar_limits' %> 4 | 5 | <%= link_to lesson_classroom_path(@classroom, lesson_id: @lesson.id), class: 'list-group-item' do %> 6 | 7 | Back to lesson 8 | <% end %> 9 |
10 | -------------------------------------------------------------------------------- /app/views/classrooms/quizzes/_quiz_sidebar_limits.html.erb: -------------------------------------------------------------------------------- 1 |
2 | Maximum points: 3 |

<%= @quiz.max_quiz_points %>

4 |
5 | <% if @quiz.maximum_attempts %> 6 |
7 | Attempts 8 |

<%= @quiz.user_attempts(current_user).count %> / <%= @quiz.maximum_attempts %>

9 |
10 | <% end %> 11 | <% if @quiz.wait_time_seconds %> 12 |
13 | Time between attempts 14 |

<%= number_with_delimiter(@quiz.wait_time_seconds) %> s

15 |
16 | <% end %> 17 | -------------------------------------------------------------------------------- /app/views/classrooms/quizzes/_quiz_summary_panel.html.erb: -------------------------------------------------------------------------------- 1 | <%= quiz.title %> 2 |
3 | <% if quiz.is_covered_by?(current_user) %> 4 | Solved 5 | <% else %> 6 | Pending 7 | <% end %> 8 | Attempts: <%= quiz.user_attempts(current_user).count %> 9 |
10 | -------------------------------------------------------------------------------- /app/views/classrooms/show_quiz_attempt.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

<%= @quiz.title %>

9 |
10 |
11 |
12 | 13 |
14 |
15 | <%= render partial: 'quizzes/show_attempt', locals: { quiz: @quiz, quiz_attempt: @quiz_attempt, attempt_answers: @attempt_answers } %> 16 |
17 | 18 |
19 |
20 | <%= render partial: 'classrooms/quizzes/attempt_quiz_sidebar' %> 21 |
22 |
23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /app/views/classrooms/student_quiz_attempt.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

<%= @quiz.title %>

9 |

User: <%= link_to @user.display_name, student_quiz_attempts_classroom_path(@classroom, lesson_id: @lesson.id, quiz_id: @quiz.id, user_id: @user.id) %>

10 |
11 |
12 |
13 | 14 |
15 |
16 | <%= render partial: 'quizzes/show_attempt', locals: { quiz: @quiz, quiz_attempt: @quiz_attempt, attempt_answers: @attempt_answers } %> 17 |
18 | 19 |
20 | 21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /app/views/classrooms/student_quiz_attempts.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

<%= @quiz.title %>

9 |

User: <%= link_to @user.display_name, student_progress_classroom_path(@classroom, user_id: @user.id) %>

10 |
11 |
12 |
13 | 14 |
15 |
16 | <%= render partial: 'classrooms/quizzes/user_attempts', locals: { quiz: @quiz, quiz_attempts: @quiz_attempts, user: @user } %> 17 |
18 |
19 |
20 | 21 |
22 | -------------------------------------------------------------------------------- /app/views/classrooms/student_task_runs.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |
7 |

Task runs for user <%= link_to @user.display_name, student_progress_classroom_path(@classroom, user_id: @user.id) %>

8 | 9 | <%= render partial: 'task_runs' %> 10 |
11 |
12 | -------------------------------------------------------------------------------- /app/views/classrooms/task_run.js.erb: -------------------------------------------------------------------------------- 1 | $('tr#task-run-<%= @task_run.id %> > td.task-run-status').html("<%= escape_javascript(render partial: 'classrooms/task_run_display_status', locals: { classroom: @classroom, lesson: @lesson, task: @task, task_run: @task_run }) %>"); 2 | $('tr#task-run-<%= @task_run.id %> > td.task-run-memory').html("<%= @task_run.memory_limit_kb %> KB"); 3 | $('tr#task-run-<%= @task_run.id %> > td.task-run-time').html("<%= @task_run.time_limit_ms %> ms"); 4 | $('tr#task-run-<%= @task_run.id %> > td.task-run-points').html("<%= @task_run.points %>"); 5 | $('tr#task-run-<%= @task_run.id %> > td.task-run-created-at').html("<%= @task_run.created_at.to_s(:db) %>"); 6 | 7 | <% if @task_run.accepted? %> 8 | $('tr#task-run-<%= @task_run.id %>').removeClass('success warning').addClass('complete success'); 9 | <% elsif @task_run.with_errors? %> 10 | $('tr#task-run-<%= @task_run.id %>').removeClass('success warning').addClass('complete warning'); 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/classrooms/task_runs.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

Your task runs: <%= @task.title %>

9 |
10 | 11 |
12 |
13 | <%= render partial: 'task_runs' %> 14 |
15 |
16 |
17 | <%= render partial: 'task_runs_sidebar' %> 18 |
19 |
20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /app/views/classrooms/task_solution.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | <%= render partial: 'lesson_top_links' %> 6 | 7 |
8 |

Solution: <%= @task.title %>

9 |
10 | 11 |
12 |
13 |
14 |
15 | <% if @task.solution.present? %> 16 | <%= render inline: @task.solution %> 17 | <% else %> 18 | There is no solution for this task yet. 19 | <% end %> 20 |
21 |
22 |
23 |
24 |
25 | <%= render partial: 'solution_sidebar' %> 26 |
27 |
28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /app/views/courses/_course.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
<%= link_to course.title, course_path(course) %>
3 |
4 | <%= render inline: course.description %> 5 |
6 | <% if user_signed_in? && current_user.is_teacher? %> 7 | 16 | <% end %> 17 |
18 | -------------------------------------------------------------------------------- /app/views/courses/_edit_visibility.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for(course_item, layout: :inline) do |f| %> 2 | <%= f.hidden_field :visible, value: !course_item.visible %> 3 | <%= button_tag class: "btn btn-sm btn-#{ course_item.visible ? 'primary' : 'default' }" do %> 4 | 5 | <% end %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/courses/_menu.html.erb: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /app/views/courses/_menu_new.html.erb: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /app/views/courses/edit.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
<%= link_to @course.title, course_path(@course) %>
4 |

Edit course details

5 |
6 |
7 | 8 |
9 | <%= link_to 'Edit course structure', edit_structure_course_path(@course), class: 'btn btn-primary' %> 10 | <% if @course.is_main %> 11 | <%= link_to 'Unset main', unset_main_course_path(@course), class: 'btn btn-primary', method: :post %> 12 | <% else %> 13 | <%= link_to 'Set main', set_main_course_path(@course), class: 'btn btn-primary', method: :post %> 14 | <% end %> 15 |
16 | 17 | <%= render partial: 'fields' %> 18 | -------------------------------------------------------------------------------- /app/views/courses/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <% unless @admin_courses.empty? && @student_courses.empty? && @other_courses.empty? %> 4 | <% unless @admin_courses.empty? %> 5 |
6 |

  Courses where you are an admin

7 | <%= render @admin_courses %> 8 |
9 | <% end %> 10 | <% unless @student_courses.empty? %> 11 |
12 |

  Your current courses

13 | <%= render @student_courses %> 14 |
15 | <% end %> 16 | <% unless @other_courses.empty? %> 17 |
18 |

19 |    20 | <% if @admin_courses.empty? && @student_courses.empty? %> 21 | Available courses 22 | <% else %> 23 | More available courses 24 | <% end %> 25 |

26 | <%= render @other_courses %> 27 |
28 | <% end %> 29 | <% else %> 30 |
No courses are available at this time
31 | <% end %> 32 |
33 |
34 | -------------------------------------------------------------------------------- /app/views/courses/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Start a new course

4 |
5 |
6 | 7 | <%= render partial: 'fields' %> 8 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Resend confirmation instructions

4 |
5 |
6 | 7 | 22 | 23 |
24 |
25 | <%= render "devise/shared/links" %> 26 |
27 |
28 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |

Welcome to HiredInTech's Interview Training Camp!

5 | 6 |

To finalize your account creation please confirm your email through the link below.

7 | 8 |

9 | <%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token), style: "padding: 10px 15px; background: #339933; color: #fff; text-decoration: none; font-size: 18px;" %> 10 |

11 |
12 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | <% if @resource.name.present? %> 7 |

Hello, <%= @resource.name %>!

8 | <% else %> 9 |

Hello!

10 | <% end %> 11 | 12 |

Someone has requested a link to change your password. You can do this through the link below.

13 | 14 |

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

15 | 16 |

If you didn't request this, please ignore this email.

17 |

Your password won't change until you access the link above and create a new one.

18 |
19 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 | <% if @resource.name.present? %> 7 |

Hello, <%= @resource.name %>!

8 | <% else %> 9 |

Hello!

10 | <% end %> 11 | 12 |

Your account has been locked due to an excessive number of unsuccessful log in attempts.

13 | 14 |

Click the link below to unlock your account:

15 | 16 |

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

17 |
18 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Change your password

4 |
5 |
6 | 7 | 27 | 28 |
29 |
30 | <%= render "devise/shared/links" %> 31 |
32 |
33 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Forgot your password?

4 |
5 |
6 | 7 | 22 | 23 |
24 |
25 | <%= render "devise/shared/links" %> 26 |
27 |
28 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

SIGN UP

4 |
5 |
6 | 7 | 32 | 33 |
34 |
35 | <%= render "devise/shared/links" %> 36 |
37 |
38 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

LOG IN

4 |
5 |
6 | 7 | 34 | 35 |
36 |
37 | <%= render "devise/shared/links" %> 38 |
39 |
40 | -------------------------------------------------------------------------------- /app/views/devise/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 2 | <%= link_to "Not registered yet? Sign up", new_user_registration_path %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> 6 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 10 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 14 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
15 | <% end -%> 16 | 17 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.submit "Resend unlock instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | CodeMarathon 2015 5 | 6 | <%= link_to 'About', about_path %> 7 | 8 | <%= link_to 'Contact', contact_path %> 9 |
10 |
11 |
12 | -------------------------------------------------------------------------------- /app/views/layouts/_quizzes_header_item.html.erb: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/views/layouts/_tasks_header_item.html.erb: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HiredInTech's Training Camp for Coding Interviews 5 | <%= Gon::Base.render_data %> 6 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 7 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 8 | <%= csrf_meta_tags %> 9 | <%= favicon_link_tag "favicon.png", type: "image/png", rel: "icon" %> 10 | <%= favicon_link_tag "favicon.ico", type: "image/x-icon", rel: "shortcut icon" %> 11 | 12 | 13 |
14 | 15 | <%= render partial: 'layouts/header' %> 16 | 17 |
18 | <% if notice.present? %> 19 | 23 | <% end %> 24 | 25 | <% if alert.present? %> 26 | 30 | <% end %> 31 | 32 | <%= yield %> 33 |
34 | 35 | <%= render partial: 'layouts/footer' %> 36 | 37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /app/views/lessons/_attach_quizzes.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | Attached quizzes to lesson 6 |
7 | 8 |
9 | <% if lesson.quizzes.present? %> 10 |
    11 | <% lesson.quizzes.each do |quiz| %> 12 |
  • 13 | <%= link_to quiz.title, quiz_path(quiz) %> 14 | <%= link_to "Detach", detach_quiz_lesson_path(lesson, quiz_id: quiz.id), method: :post, class: 'btn btn-danger btn-xs pull-right' %> 15 |
  • 16 | <% end %> 17 |
18 | <% else %> 19 | No quizzes in lesson 20 | <% end %> 21 |
22 | 23 | 33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /app/views/lessons/_attach_tasks.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 | Attached tasks to lesson 6 |
7 | 8 |
9 | <% if lesson.tasks.present? %> 10 |
    11 | <% lesson.tasks.each do |task| %> 12 |
  • 13 | <%= link_to task.title, task_path(task) %> 14 | <%= link_to "Detach", detach_task_lesson_path(lesson, task_id: task.id), method: :post, class: 'btn btn-danger btn-xs pull-right' %> 15 |
  • 16 | <% end %> 17 |
18 | <% else %> 19 | No tasks in lesson 20 | <% end %> 21 |
22 | 23 | 33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /app/views/lessons/_change_position.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <% unless lesson.is_first? %> 3 | <%= button_to move_up_lesson_path(lesson), method: :post, class: 'btn btn-sm btn-primary' do %> 4 | 5 | <% end %> 6 | <% end %> 7 | 8 | 9 | 10 | <% unless lesson.is_last? %> 11 | <%= button_to move_down_lesson_path(lesson), method: :post, class: 'btn btn-sm btn-primary' do %> 12 | 13 | <% end %> 14 | <% end %> 15 | 16 | -------------------------------------------------------------------------------- /app/views/lessons/_edit_title.html.erb: -------------------------------------------------------------------------------- 1 | <%= bootstrap_form_for(lesson, layout: :inline) do |f| %> 2 | <%= f.text_field :title, hide_label: true, class: "long-field input-sm" %> 3 | <%= f.submit "Update", class: "btn btn-sm btn-primary" %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/lessons/_new.html.erb: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /app/views/lessons/edit.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
<%= link_to 'Back to course structure', edit_structure_course_path(@lesson.section.course) %>
4 |

Edit lesson "<%= @lesson.title %>"

5 |
6 |
7 | 8 | <%= render partial: 'attach_tasks', locals: { lesson: @lesson } %> 9 | <%= render partial: 'attach_quizzes', locals: { lesson: @lesson } %> 10 | 11 | <%= bootstrap_form_for(@lesson) do |f| %> 12 |
13 |
14 | <%= f.text_area :markdown_content, rows: 20, label: "Lesson contents via markdown (preview doesn't support code blocks and tables)", "data-provide" => "markdown", placeholder: "Content of lesson" %> 15 |
16 |
17 | 18 |
19 |
20 | <%= f.text_area :markdown_sidebar_content, rows: 20, label: "Lesson sidebar contents via markdown (preview doesn't support code blocks and tables)", "data-provide" => "markdown", placeholder: "Sidebar content of lesson" %> 21 |
22 |
23 | 24 |
25 | <%= f.submit "Save", class: "btn btn-primary" %> 26 | <%= link_to "Cancel", edit_structure_course_path(@lesson.section.course), class: "btn btn-default" %> 27 |
28 | <% end %> 29 | -------------------------------------------------------------------------------- /app/views/pages/contact.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 |
7 |

8 |

9 | Any suggestions, comments, advice related to HiredInTech are very welcome. We may take some time to reply but we will do our best to be quick enough. 10 |

11 | 12 |

13 | Feel free to drop us a line at team@hiredintech.com. 14 |

15 | 16 |

17 | Or you could also use the contact widget at the bottom-right corner of the page. 18 |

19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /app/views/quizzes/_admin_header.html.erb: -------------------------------------------------------------------------------- 1 | <%= link_to 'Quiz page', quiz_path(@quiz) %> | 2 | <%= link_to 'Edit quiz', edit_quiz_path(@quiz) %> 3 | -------------------------------------------------------------------------------- /app/views/quizzes/_attempt.html.erb: -------------------------------------------------------------------------------- 1 | <%= bootstrap_form_tag(url: path_to_submit_quiz, method: :post) do |f| %> 2 | <% quiz.quiz_questions.shuffle.each do |quiz_question| %> 3 | <%= render partial: 'quizzes/quiz_question', locals: { quiz_question: quiz_question, f: f } %> 4 | <% end %> 5 | 6 |
7 |
8 | <%= f.submit 'Submit', class: 'btn btn-lg btn-primary' %> 9 |
10 |
11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/quizzes/_fields.html.erb: -------------------------------------------------------------------------------- 1 | <%= bootstrap_form_for(@quiz) do |f| %> 2 |
3 |
4 | <%= f.text_field :title, class: "input-lg", placeholder: "Quiz title", required: true %> 5 |
6 |
7 | 8 |
9 |
10 | <%= f.text_field :maximum_attempts, class: "input-lg", placeholder: "Maximum attempts" %> 11 |
12 | 13 |
14 | <%= f.text_field :wait_time_seconds, class: "input-lg", placeholder: "Wait time between attempts" %> 15 |
16 |
17 | 18 |
19 |
20 |
21 |
    22 | <%= f.fields_for :quiz_questions, f.object.quiz_questions.ordered do |qqf| %> 23 | <%= render "quiz_question_fields", f: qqf %> 24 | <% end %> 25 |
26 | 27 | <%= link_to_add_field 'Add question', f, :quiz_questions %> 28 |
29 |
30 |
31 | 32 |
33 | <%= f.submit "Save", class: "btn btn-primary" %> 34 |
35 | <% end %> 36 | -------------------------------------------------------------------------------- /app/views/quizzes/_quiz.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= link_to quiz.title, quiz_path(quiz) %> 3 | <%= link_to quiz.creator.display_name, user_path(quiz.creator) %> 4 | 5 | <%= link_to edit_quiz_path(quiz), class: "btn btn-sm btn-warning", title: "Edit" do %> 6 | 7 | <% end %> 8 | <%= link_to quiz_path(quiz), method: :delete, class: "btn btn-sm btn-danger", data: { confirm: "Are you sure you want to delete quiz \"#{ quiz.title }\"?" } do %> 9 | 10 | <% end %> 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/views/quizzes/_quiz_answer_fields.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 |
    3 |
    4 |
    5 | <%= f.text_area :markdown_content, label: "Question answer (using markdown)", "data-provide" => "markdown", placeholder: "Answer text", required: true, class: 'quiz-markdown-editor' %> 6 |
    7 |
    8 | <%= f.check_box :correct %> 9 |
    10 |
    11 | <%= link_to_remove_field('Remove answer', f) %> 12 |
    13 |
    14 |
    15 |
  • 16 | -------------------------------------------------------------------------------- /app/views/quizzes/_quiz_question.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 | <%= render inline: quiz_question.content %> 6 |
    7 |
    8 | <% if quiz_question.multiple_choice? %> 9 |
      10 | <% quiz_question.quiz_answers.each do |quiz_answer| %> 11 |
    • 12 | <%= f.check_box "quiz_attempt[#{ quiz_question.id }][multiple_answers][#{ quiz_answer.id }]", label: simple_format(quiz_answer.content) %> 13 |
    • 14 | <% end %> 15 |
    16 | <% else %> 17 | <%= f.text_field "quiz_attempt[#{ quiz_question.id }][freetext_answer]", hide_label: true %> 18 | <% end %> 19 |
    20 |
    21 |
    22 |
    23 | -------------------------------------------------------------------------------- /app/views/quizzes/_quiz_question_fields.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 |
    3 | <%= f.text_area :markdown_content, rows: 5, label: "Question text (using markdown)", "data-provide" => "markdown", placeholder: "Question text", required: true, class: 'quiz-markdown-editor' %> 4 | 5 | <%= f.text_area :markdown_explanation, rows:5, label: "Question explanation (using markdown)", "data-provide" => "markdown", placeholder: "Question text", class: 'quiz-markdown-editor' %> 6 | 7 | <%= f.form_group :question_type, label: { text: "Type" }, help: "Multiple choice versus free text" do %> 8 | <%= f.radio_button :question_type, QuizQuestion::TYPE_MULTIPLE_CHOICE, label: "Multiple choice", class: 'question-type' %> 9 | <%= f.radio_button :question_type, QuizQuestion::TYPE_FREETEXT, label: "Freetext", class: 'question-type' %> 10 | <% end %> 11 | 12 |
    13 | <%= f.text_area :freetext_regex, placeholder: 'Question freetext regex' %> 14 |
    15 | 16 | 17 |
      18 | <%= f.fields_for :quiz_answers do |qaf| %> 19 | <%= render "quiz_answer_fields", f: qaf %> 20 | <% end %> 21 |
    22 | <%= link_to_add_field 'Add answer', f, :quiz_answers %> 23 |
    24 | 25 | <%= link_to_remove_field('Remove question', f) %> 26 |
    27 |
  • 28 | -------------------------------------------------------------------------------- /app/views/quizzes/_show_attempt.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    Attempted at:
    4 |
    5 |
    6 |
    <%= quiz_attempt.created_at.to_s(:db) %>
    7 |
    8 |
    9 | 10 |
    11 |
    12 |
    Score:
    13 |
    14 |
    15 |
    <%= quiz_attempt.score.round(2) %>
    16 |
    17 |
    18 | 19 | <% quiz.quiz_questions.each do |quiz_question| %> 20 | <%= render partial: 'quizzes/quiz_question_attempt', locals: { quiz_question: quiz_question, attempt_answers: attempt_answers } %> 21 | <% end %> 22 | -------------------------------------------------------------------------------- /app/views/quizzes/all_attempts.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    3 | All attempts: <%= @all_count %> 4 |

    5 |
    6 | <% if @quiz_attempts.present? %> 7 | <%= paginate @quiz_attempts, theme: 'twitter-bootstrap-3' %> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% @quiz_attempts.each do |quiz_attempt| %> 16 | 17 | 18 | 19 | 20 | 21 | 22 | <% end %> 23 |
    Time of attemptUserQuizScore
    <%= link_to quiz_attempt.created_at.to_s(:db), show_attempt_quiz_path(quiz_attempt.quiz, quiz_attempt_id: quiz_attempt.id) %><%= link_to quiz_attempt.user.display_name, user_path(quiz_attempt.user) %><%= link_to quiz_attempt.quiz.title, quiz_path(quiz_attempt.quiz) %><%= quiz_attempt.score.round(2) %>
    24 | <%= paginate @quiz_attempts, theme: 'twitter-bootstrap-3' %> 25 | <% else %> 26 |

    No attempts so far

    27 | <% end %> 28 |
    29 |
    30 | -------------------------------------------------------------------------------- /app/views/quizzes/attempt.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: 'admin_header' %> 2 | 3 |
    4 |
    5 |

    <%= @quiz.title %>

    6 |
    7 |
    8 | 9 |
    10 |
    11 | <%= render partial: 'attempt', locals: { quiz: @quiz, path_to_submit_quiz: submit_quiz_path(@quiz) } %> 12 |
    13 |
    14 | -------------------------------------------------------------------------------- /app/views/quizzes/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: 'admin_header' %> 2 | 3 |
    4 |
    5 |

    Edit quiz

    6 |
    7 |
    8 | 9 | <%= render partial: 'fields' %> 10 | -------------------------------------------------------------------------------- /app/views/quizzes/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Quizzes list

    4 |
    5 |
    6 | 7 |
    8 |
    9 | <% if @quizzes.empty? %> 10 | No quizzes created yet 11 | <% else %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | <% @quizzes.each do |quiz| %> 19 | <%= render partial: 'quiz', locals: { quiz: quiz } %> 20 | <% end %> 21 |
    TitleCreatorActions
    22 | <% end %> 23 |
    24 |
    25 | -------------------------------------------------------------------------------- /app/views/quizzes/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Create new quiz

    4 |
    5 |
    6 | 7 | <%= render partial: 'fields' %> 8 | -------------------------------------------------------------------------------- /app/views/quizzes/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: 'admin_header' %> 2 | <%= render partial: 'main_page', locals: { quiz: @quiz } %> 3 | -------------------------------------------------------------------------------- /app/views/quizzes/show_attempt.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: 'admin_header' %> 2 | 3 |
    4 |
    5 |

    <%= @quiz.title %>

    6 |

    User: <%= link_to @quiz_attempt.user.display_name, user_path(@quiz_attempt.user) %>

    7 |
    8 |
    9 | 10 | <%= render partial: 'show_attempt', locals: { quiz: @quiz, quiz_attempt: @quiz_attempt, attempt_answers: @attempt_answers } %> 11 | -------------------------------------------------------------------------------- /app/views/sections/_change_position.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <% unless section.is_first? %> 3 | <%= button_to move_up_section_path(section), method: :post, class: 'btn btn-sm btn-primary' do %> 4 | 5 | <% end %> 6 | <% end %> 7 | 8 | 9 | 10 | <% unless section.is_last? %> 11 | <%= button_to move_down_section_path(section), method: :post, class: 'btn btn-sm btn-primary' do %> 12 | 13 | <% end %> 14 | <% end %> 15 | 16 | -------------------------------------------------------------------------------- /app/views/sections/_edit_title.html.erb: -------------------------------------------------------------------------------- 1 | <%= bootstrap_form_for(section, layout: :inline) do |f| %> 2 | <%= f.text_field :title, hide_label: true, class: "long-field input-sm" %> 3 | <%= f.submit "Update", class: "btn btn-sm btn-primary" %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/sections/_new.html.erb: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /app/views/shared/_social_buttons.html.erb: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /app/views/tasks/_admin_panel.html.erb: -------------------------------------------------------------------------------- 1 | <% if current_user.is_teacher? %> 2 |
    3 |
    4 | <%= link_to 'View', task_path(@task), class: "btn btn-primary" %> 5 | <%= link_to 'Edit', edit_task_path(@task), class: "btn btn-primary" %> 6 | <%= link_to 'Solve', solve_task_path(@task), class: "btn btn-primary" %> 7 | <% if @task.solution.present? %> 8 | <%= link_to 'Solution', solution_task_path(@task), class: "btn btn-primary" %> 9 | <% end %> 10 | <%= link_to 'Runs', runs_task_path(@task), class: "btn btn-primary" %> 11 | <%= link_to 'Run limits', runs_limits_task_path(@task), class: "btn btn-primary" %> 12 |

    13 | 14 | Memory limit: <%= @task.memory_limit_kb %> KB 15 | 16 |   17 | 18 | Time limit: <%= @task.time_limit_ms %> ms 19 | 20 |

    21 |
    22 |
    23 |

    <%= @task.title %>

    24 | <% end %> 25 | -------------------------------------------------------------------------------- /app/views/tasks/_edit_runs_limit.html.erb: -------------------------------------------------------------------------------- 1 | <%= bootstrap_form_tag(url: update_runs_limit_task_path(task, user_id: task_record.user.id), method: :post, layout: :inline) do |f| %> 2 | <%= f.text_field :new_runs_limit, value: task_record.runs_limit, hide_label: true, class: "long-field input-sm" %> 3 | <%= f.submit "Update", class: "btn btn-sm btn-primary" %> 4 | <% end %> 5 | -------------------------------------------------------------------------------- /app/views/tasks/_run.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= task_run.id %> 3 | <%= link_to task_run.task.title, task_path(task_run.task) %> 4 | <%= link_to truncate(task_run.user.display_name, length: 15), user_path(task_run.user) if task_run.user.present? %> 5 | <%= task_run.status %> 6 | <%= task_run.message %> 7 | <%= task_run.lang %> 8 | <%= task_run.memory_limit_kb %> KB 9 | <%= task_run.time_limit_ms %> ms 10 | <%= task_run.points %> 11 | <%= task_run.created_at.to_s(:db) %> 12 | <%= render partial: 'task_run_source_code', locals: { task_run: task_run } %> 13 | 14 | -------------------------------------------------------------------------------- /app/views/tasks/_runs.html.erb: -------------------------------------------------------------------------------- 1 | <%= paginate task_runs, theme: 'twitter-bootstrap-3' %> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% task_runs.each do |task_run| %> 17 | <%= render partial: 'run', locals: { task_run: task_run } %> 18 | <% end %> 19 |
    IDTaskUserStatusMessageLanguageMemoryTimePointsDate
    20 | <%= paginate task_runs, theme: 'twitter-bootstrap-3' %> 21 | -------------------------------------------------------------------------------- /app/views/tasks/_solve.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= label_tag :lang, "Choose programming language" %> 3 | <%= select_tag :lang, options_for_select(TaskRun::PROGRAMMING_LANGS, run.nil? ? current_user.last_programming_language : run.lang) %> 4 | Load boilerplate code 5 | 8 |
    9 |
    10 |
    11 |
    12 | <%= label_tag :source_code, "Type your source code here" %> 13 |
    14 | <%= text_area_tag :source_code, nil, style: "display:none;" %> 15 |
    16 |
    17 | 21 |
    22 |
    23 | <%= form.submit 'Submit solution', class: 'btn btn-primary' %> 24 |
    25 | -------------------------------------------------------------------------------- /app/views/tasks/_successful_run_source_code.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 | 5 |
    6 | 7 | 26 | -------------------------------------------------------------------------------- /app/views/tasks/_task.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= link_to task.title, task_path(task) %> 3 | <%= task.task_type %> 4 | <%= link_to task.creator.display_name, user_path(task.creator) %> 5 | <%= task.external_key %> 6 | 7 | <%= link_to 'Solve', solve_task_path(task), class: "btn btn-primary btn-sm" %> 8 | <%= link_to 'Runs', runs_task_path(task), class: "btn btn-primary btn-sm" %> 9 | <%= link_to edit_task_path(task), class: "btn btn-sm btn-warning", title: "Edit" do %> 10 | 11 | <% end %> 12 | <%= link_to task_path(task), method: :delete, class: "btn btn-sm btn-danger", data: { confirm: "Are you sure you want to delete task \"#{ task.title }\"?" } do %> 13 | 14 | <% end %> 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/views/tasks/all_runs.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= render partial: 'runs', locals: { task_runs: @task_runs } %> 4 |
    5 |
    6 | -------------------------------------------------------------------------------- /app/views/tasks/create.html.erb: -------------------------------------------------------------------------------- 1 |

    Tasks#create

    2 |

    Find me in app/views/tasks/create.html.erb

    3 | -------------------------------------------------------------------------------- /app/views/tasks/destroy.html.erb: -------------------------------------------------------------------------------- 1 |

    Tasks#destroy

    2 |

    Find me in app/views/tasks/destroy.html.erb

    3 | -------------------------------------------------------------------------------- /app/views/tasks/edit.html.erb: -------------------------------------------------------------------------------- 1 | 19 | 20 |
    21 |
    22 |

    Edit task '<%= @task.title %>'

    23 | Update checker 24 |
    25 |
    26 | 27 | <%= render partial: 'fields' %> 28 | -------------------------------------------------------------------------------- /app/views/tasks/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Tasks list

    4 |
    5 |
    6 | 7 |
    8 |
    9 | <% if @tasks.empty? %> 10 | No tasks created yet 11 | <% else %> 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <% @tasks.each do |task| %> 21 | <%= render partial: 'task', locals: { task: task } %> 22 | <% end %> 23 |
    TitleTypeCreatorKeyActions
    24 | <% end %> 25 |
    26 |
    27 | -------------------------------------------------------------------------------- /app/views/tasks/new.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Create new task

    4 |
    5 |
    6 | 7 | <%= render partial: 'fields' %> 8 | -------------------------------------------------------------------------------- /app/views/tasks/runs.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= render partial: 'admin_panel' %> 4 | <%= render partial: 'runs', locals: { task_runs: @task_runs } %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /app/views/tasks/runs_limits.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <%= @task_records.find_each do |task_record| %> 7 | 8 | 9 | 12 | 13 | <% end %> 14 |
    NameRuns limit
    <%= link_to truncate(task_record.user.display_name, length: 25), user_path(task_record.user) %> 10 | <%= render partial: 'edit_runs_limit', locals: { task: @task, task_record: task_record } %> 11 |
    15 | -------------------------------------------------------------------------------- /app/views/tasks/show.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= render partial: 'admin_panel' %> 4 | <%= render inline: @task.description %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /app/views/tasks/solution.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= render partial: 'admin_panel' %> 4 | <%= render inline: @task.solution %> 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /app/views/tasks/solve.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= render partial: 'admin_panel' %> 4 | <%= form_for @task, url: do_solve_task_path(@task), method: :post do |f| %> 5 | <%= render partial: 'solve', locals: { form: f, task: @task, run: nil } %> 6 | <% end %> 7 |
    8 |
    9 | -------------------------------------------------------------------------------- /app/views/tasks/stats.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |

    3 | Successfull Runs (100 points) 4 |

    5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% @runs_stats_by_task.each do |task_id, task_runs_stats| %> 16 | 17 | 18 | 19 | <% ['cpp', 'java', 'python', 'ruby'].each do |lang| %> 20 | 25 | <% end %> 26 | 27 | <% end %> 28 |
    TitleallC++JavaPythonRuby
    <%= task_runs_stats[:title] %><%= task_runs_stats[:successful_runs] %> / <%= task_runs_stats[:all_runs] %> ( <%= task_runs_stats[:success_percent] %> %)"> 21 | <%= task_runs_stats.fetch("#{ lang }_successful_runs".to_sym, 0) %> / 22 | <%= task_runs_stats.fetch("#{ lang }_all_runs".to_sym, 0) %> ( 23 | <%= task_runs_stats.fetch("#{ lang }_success_percent".to_sym, 0) %> %) 24 |
    29 |
    30 | -------------------------------------------------------------------------------- /app/views/tasks/update.html.erb: -------------------------------------------------------------------------------- 1 |

    Tasks#update

    2 |

    Find me in app/views/tasks/update.html.erb

    3 | -------------------------------------------------------------------------------- /app/views/user_invitations_mailer/invitation_with_user.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |

    HiredInTech's Training Camp for Coding Interviews!

    5 | 6 |

    This is an invitation to log in HiredInTech's new training camp.

    7 | 8 |

    We are still in private beta and will be honoured if it's useful in your tech interview preparation.

    9 | 10 |

    Use the link below to go to the log in page.

    11 | 12 |

    You'll be able to log in with the email address at which you received this invite.

    13 | 14 |

    15 | <%= link_to 'Log In', new_user_session_url, style: "padding: 10px 15px; background: #339933; color: #fff; text-decoration: none; font-size: 18px;" %> 16 |

    17 |
    18 | -------------------------------------------------------------------------------- /app/views/user_invitations_mailer/invitation_without_user.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |

    HiredInTech's Training Camp for Coding Interviews!

    5 | 6 |

    This is an invitation to sign up for HiredInTech's new training camp.

    7 | 8 |

    We are still in private beta and will be honoured if it's useful in your tech interview preparation.

    9 | 10 |

    Use the link below to go to the sign up page.

    11 | 12 |

    You'll be able to sign up with the email address at which you received this invite.

    13 | 14 |

    15 | <%= link_to 'Sign Up', new_user_registration_url, style: "padding: 10px 15px; background: #339933; color: #fff; text-decoration: none; font-size: 18px;" %> 16 |

    17 |
    18 | -------------------------------------------------------------------------------- /app/views/users/_search_users.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= form_tag '', method: :get do %> 3 | <%= text_field_tag :query, @user_query %> 4 | <%= submit_tag 'Search' %> 5 | <% end %> 6 |
    7 | -------------------------------------------------------------------------------- /app/views/users/_users_list.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= paginate users, theme: 'twitter-bootstrap-3' %> 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% users.each do |user| %> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | <% unless user == current_user || user.has_role?(User::ROLE_TEACHER) || user.has_role?(User::ROLE_ADMIN) %> 25 | 26 | <% end %> 27 | 28 | <% end %> 29 |
    IDEmailNameRolesTime registeredConfirmed atAuth providerAction
    <%= user.id %><%= link_to user.email, user_path(user) %><%= truncate(user.name, length: 25) %><%= user.display_roles %><%= user.created_at.to_s(:db) %><%= user.confirmed_at.to_s(:db) if user.confirmed_at %><%= user.provider %><%= link_to 'Delete', user, :class => "btn btn-xs btn-danger", method: :delete, data: {confirm: 'Are you sure?'} %>
    30 | <%= paginate users, theme: 'twitter-bootstrap-3' %> 31 |
    32 |
    33 | -------------------------------------------------------------------------------- /app/views/users/_users_top_menu.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <%= link_to 'Active users', users_path %> <%= @active_users_count %> | 3 | <%= link_to 'Inactive users', inactive_users_path %> <%= @inactive_users_count %> | 4 | <%= link_to 'Invitations', user_invitations_path %> <%= @user_invitations_count %> | 5 | User limit <%= Settings.users_limit.present? ? Settings.users_limit : 'None' %> 6 |
    7 | -------------------------------------------------------------------------------- /app/views/users/edit_profile.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |

    Edit profile

    4 |
    5 |
    6 | 7 | 39 | -------------------------------------------------------------------------------- /app/views/users/inactive.html.erb: -------------------------------------------------------------------------------- 1 |

    Inactive users

    2 | 3 | <%= render partial: 'users_top_menu' %> 4 | 5 | <%= render partial: 'search_users' %> 6 | 7 | <%= render partial: 'users_list', locals: { users: @inactive_users } %> 8 | -------------------------------------------------------------------------------- /app/views/users/index.html.erb: -------------------------------------------------------------------------------- 1 |

    Users list

    2 | 3 | <%= render partial: 'users_top_menu' %> 4 | 5 | <%= render partial: 'search_users' %> 6 | 7 | <%= render partial: 'users_list', locals: { users: @users } %> 8 | -------------------------------------------------------------------------------- /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/delayed_job: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) 4 | require 'delayed/command' 5 | Delayed::Command.new(ARGV).daemonize 6 | -------------------------------------------------------------------------------- /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/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require "rubygems" 8 | require "bundler" 9 | 10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m) 11 | Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq } 12 | gem "spring", match[1] 13 | require "spring/binstub" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /config.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 | # Added when upgrading from Rails 4.2 to 5.0 10 | ActiveSupport.halt_callback_chains_on_return_false = false 11 | 12 | module Codemarathon 13 | class Application < Rails::Application 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | 18 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 19 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 20 | # config.time_zone = 'Central Time (US & Canada)' 21 | 22 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 23 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 24 | # config.i18n.default_locale = :de 25 | 26 | # Do not swallow errors in after_commit/after_rollback callbacks. 27 | # Disabled when migrating to Rails 5.0 28 | # config.active_record.raise_in_transactional_callbacks = true 29 | 30 | config.sass.preferred_syntax = :sass 31 | config.sass.line_comments = false 32 | config.sass.cache = false 33 | 34 | config.active_job.queue_adapter = :delayed_job 35 | 36 | config.action_controller.forgery_protection_origin_check = true 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /config/application.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | users_limit: 200 3 | 4 | development: 5 | <<: *defaults 6 | 7 | test: 8 | <<: *defaults 9 | users_limit: 5 10 | 11 | staging: 12 | <<: *defaults 13 | 14 | production: 15 | <<: *defaults 16 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/database.example.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | pool: 5 4 | encoding: unicode 5 | 6 | development: 7 | <<: *default 8 | database: codemarathon_development 9 | host: localhost 10 | username: 11 | password: 12 | 13 | # Warning: The database defined as "test" will be erased and 14 | # re-generated from your development database when you run "rake". 15 | # Do not set this db to the same as development or production. 16 | test: 17 | <<: *default 18 | database: codemarathon_test 19 | host: localhost 20 | username: 21 | password: 22 | 23 | production: 24 | <<: *default 25 | database: codemarathon_production 26 | host: 27 | username: 28 | password: 29 | -------------------------------------------------------------------------------- /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/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /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 4 | -------------------------------------------------------------------------------- /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/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/omniauth.rb: -------------------------------------------------------------------------------- 1 | OmniAuth.config.test_mode = true if Rails.env.test? 2 | # OmniAuth.config.logger = Rails.logger 3 | 4 | # Rails.application.config.middleware.use OmniAuth::Builder do 5 | # provider :facebook, FACEBOOK_APP_ID, FACEBOOK_SECRET, 6 | # { 7 | # image_size: :square, 8 | # secure_image_url: true, 9 | # display: :popup 10 | # } 11 | # provider :google_oauth2, GOOGLE_APP_ID, GOOGLE_SECRET, 12 | # { 13 | # image_aspect_ratio: "square", 14 | # image_size: 50, 15 | # scoe: "email, profile" 16 | # } 17 | # provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET'], scope: "user:email" 18 | # provider :linkedin, LINKEDIN_API_KEY, LINKEDIN_SECRET_KEY, 19 | # :fields => ['id', 'email-address', 'first-name', 'last-name', 'picture-url'] 20 | # end 21 | -------------------------------------------------------------------------------- /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: '_codemarathon_session' 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/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: b39a3cb1da68e06e0dbe2d343dc1172a9bc9a13e33517fcfb73f516f139d45a7a072f37ac3e75ce25b7b37dfe8bcec2d190295268ac5274485de64891fe305bf 15 | grader_host: http://localhost:6543 16 | grader_email: anton@codemarathon.com 17 | grader_token: 0cbbf77d7b85d83fdaa848c654566b4b 18 | 19 | test: 20 | secret_key_base: a900722b3afcc864da57779e91997349204a90b83918604e8b1c8316293851b9d488f88b5596fb86715d6eca786e48e182dd5a743ff0dc2310720572fe39e402 21 | grader_host: http://grader 22 | 23 | # Do not keep production secrets in the repository, 24 | # instead read values from the environment. 25 | production: 26 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 27 | grader_host: <%= ENV["GRADER_HOST"] %> 28 | grader_email: <%= ENV["GRADER_EMAIL"] %> 29 | grader_token: <%= ENV["GRADER_TOKEN"] %> 30 | grader_use_ssl: <%= ENV["GRADER_USE_SSL"] %> 31 | -------------------------------------------------------------------------------- /config/unicorn.rb: -------------------------------------------------------------------------------- 1 | # root = "/home/grader/applications/grader" 2 | root = "/home/hiredintech/applications/codemarathon" 3 | working_directory root 4 | pid "#{root}/tmp/pids/unicorn.pid" 5 | stderr_path "#{root}/log/unicorn.log" 6 | stdout_path "#{root}/log/unicorn.log" 7 | 8 | listen "/tmp/unicorn.codemarathon.sock" 9 | worker_processes 2 10 | timeout 30 11 | -------------------------------------------------------------------------------- /db/migrate/20150813135216_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Confirmable 23 | t.string :confirmation_token 24 | t.datetime :confirmed_at 25 | t.datetime :confirmation_sent_at 26 | t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps null: false 35 | end 36 | 37 | add_index :users, :email, unique: true 38 | add_index :users, :reset_password_token, unique: true 39 | # add_index :users, :confirmation_token, unique: true 40 | # add_index :users, :unlock_token, unique: true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /db/migrate/20150813145458_add_omniauth_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddOmniauthToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :provider, :string 4 | add_index :users, :provider 5 | add_column :users, :uid, :string 6 | add_index :users, :uid 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20150813152119_add_name_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddNameToUser < ActiveRecord::Migration 2 | def change 3 | add_column :users, :name, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151002053704_create_courses.rb: -------------------------------------------------------------------------------- 1 | class CreateCourses < ActiveRecord::Migration 2 | def change 3 | create_table :courses do |t| 4 | t.string :title 5 | t.text :markdown_description 6 | t.text :description 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151002073002_add_long_description_to_course.rb: -------------------------------------------------------------------------------- 1 | class AddLongDescriptionToCourse < ActiveRecord::Migration 2 | def change 3 | add_column :courses, :markdown_long_description, :text 4 | add_column :courses, :long_description, :text 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151002130501_modify_course_columns.rb: -------------------------------------------------------------------------------- 1 | class ModifyCourseColumns < ActiveRecord::Migration 2 | def change 3 | change_column_null :courses, :title, false 4 | change_column_null :courses, :markdown_description, false 5 | change_column_null :courses, :description, false 6 | change_column_null :courses, :markdown_long_description, false 7 | change_column_null :courses, :long_description, false 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20151002134106_create_roles.rb: -------------------------------------------------------------------------------- 1 | class CreateRoles < ActiveRecord::Migration 2 | def change 3 | create_table :roles do |t| 4 | t.integer :user_id, null: false 5 | t.string :role_type, null: false 6 | 7 | t.timestamps null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20151012135858_create_sections.rb: -------------------------------------------------------------------------------- 1 | class CreateSections < ActiveRecord::Migration 2 | def change 3 | create_table :sections do |t| 4 | t.integer :course_id 5 | t.string :title 6 | t.integer :position 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151013060709_make_section_fields_non_null.rb: -------------------------------------------------------------------------------- 1 | class MakeSectionFieldsNonNull < ActiveRecord::Migration 2 | def change 3 | change_column_null(:sections, :title, false) 4 | change_column_null(:sections, :position, false) 5 | change_column_null(:sections, :course_id, false) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20151013161035_create_lessons.rb: -------------------------------------------------------------------------------- 1 | class CreateLessons < ActiveRecord::Migration 2 | def change 3 | create_table :lessons do |t| 4 | t.string :title 5 | t.integer :position 6 | t.integer :section_id 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151013163723_change_lesson_field_null.rb: -------------------------------------------------------------------------------- 1 | class ChangeLessonFieldNull < ActiveRecord::Migration 2 | def change 3 | change_column_null(:lessons, :title, false) 4 | change_column_null(:lessons, :position, false) 5 | change_column_null(:lessons, :section_id, false) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20151013194516_add_content_to_lesson.rb: -------------------------------------------------------------------------------- 1 | class AddContentToLesson < ActiveRecord::Migration 2 | def change 3 | add_column :lessons, :content, :text 4 | add_column :lessons, :markdown_content, :text 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151016153923_create_classrooms.rb: -------------------------------------------------------------------------------- 1 | class CreateClassrooms < ActiveRecord::Migration 2 | def change 3 | create_table :classrooms do |t| 4 | t.string :name 5 | t.integer :course_id 6 | 7 | t.timestamps null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20151016154207_make_classroom_fields_non_null.rb: -------------------------------------------------------------------------------- 1 | class MakeClassroomFieldsNonNull < ActiveRecord::Migration 2 | def change 3 | change_column_null(:classrooms, :name, false) 4 | change_column_null(:classrooms, :course_id, false) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20151016154749_create_classroom_records.rb: -------------------------------------------------------------------------------- 1 | class CreateClassroomRecords < ActiveRecord::Migration 2 | def change 3 | create_table :classroom_records do |t| 4 | t.integer :classroom_id, null: false 5 | t.integer :user_id, null: false 6 | t.string :role, null: false 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151023135004_add_default_to_lesson_content.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultToLessonContent < ActiveRecord::Migration 2 | def change 3 | change_column_default(:lessons, :content, "") 4 | change_column_default(:lessons, :markdown_content, "") 5 | 6 | Lesson.where(content: nil).update_all(content: "") 7 | Lesson.where(markdown_content: nil).update_all(markdown_content: "") 8 | 9 | change_column_null(:lessons, :content, false) 10 | change_column_null(:lessons, :markdown_content, false) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20151025194346_add_visible_flag_to_course.rb: -------------------------------------------------------------------------------- 1 | class AddVisibleFlagToCourse < ActiveRecord::Migration 2 | def change 3 | add_column :courses, :visible, :boolean, null: false, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151026143538_add_visible_to_sections.rb: -------------------------------------------------------------------------------- 1 | class AddVisibleToSections < ActiveRecord::Migration 2 | def change 3 | add_column :sections, :visible, :boolean, null: false, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151026143549_add_visible_to_lessons.rb: -------------------------------------------------------------------------------- 1 | class AddVisibleToLessons < ActiveRecord::Migration 2 | def change 3 | add_column :lessons, :visible, :boolean, null: false, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151029063854_create_tasks.rb: -------------------------------------------------------------------------------- 1 | class CreateTasks < ActiveRecord::Migration 2 | def change 3 | create_table :tasks do |t| 4 | t.string :title, null: false 5 | t.string :task_type, null: false 6 | t.text :markdown_description, null: false 7 | t.text :description, null: false 8 | t.integer :creator_id, null: false 9 | t.boolean :visible, null: false, default: false 10 | t.string :external_key 11 | t.integer :memory_limit_kb 12 | t.integer :time_limit_ms 13 | 14 | t.timestamps null: false 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20151105093612_create_task_runs.rb: -------------------------------------------------------------------------------- 1 | class CreateTaskRuns < ActiveRecord::Migration 2 | def change 3 | create_table :task_runs do |t| 4 | t.integer :task_id, null: false 5 | t.integer :user_id, null: false 6 | t.text :source_code, null: false 7 | t.string :lang, null: false 8 | t.string :status, null: false 9 | t.string :external_key 10 | t.string :message 11 | t.text :grader_log 12 | t.integer :memory_limit_kb, null: false 13 | t.integer :time_limit_ms, null: false 14 | 15 | t.timestamps null: false 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20151105120959_add_run_type_to_task_runs.rb: -------------------------------------------------------------------------------- 1 | class AddRunTypeToTaskRuns < ActiveRecord::Migration 2 | def change 3 | add_column :task_runs, :run_type, :string 4 | 5 | TaskRun.find_each do |task_run| 6 | task_run.update_attributes(run_type: TaskRun::TYPE_RUN_TASK) 7 | end 8 | 9 | change_column_null(:task_runs, :run_type, false) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20151105122008_add_points_to_task_run.rb: -------------------------------------------------------------------------------- 1 | class AddPointsToTaskRun < ActiveRecord::Migration 2 | def change 3 | add_column :task_runs, :points, :float 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151130142452_create_table_lesson_tasks.rb: -------------------------------------------------------------------------------- 1 | class CreateTableLessonTasks < ActiveRecord::Migration 2 | def change 3 | create_table :lessons_tasks do |t| 4 | t.integer :lesson_id 5 | t.integer :task_id 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20151203141019_make_points_non_null_in_task_run.rb: -------------------------------------------------------------------------------- 1 | class MakePointsNonNullInTaskRun < ActiveRecord::Migration 2 | def change 3 | TaskRun.where(points: nil).update_all(points: 0.0) 4 | change_column_default(:task_runs, :points, 0.0) 5 | change_column_null(:task_runs, :points, false) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20151208213102_create_task_records.rb: -------------------------------------------------------------------------------- 1 | class CreateTaskRecords < ActiveRecord::Migration 2 | def change 3 | create_table :task_records do |t| 4 | t.integer :user_id, null: false 5 | t.integer :task_id, null: false 6 | t.float :best_score, default: 0.0, null: false 7 | t.integer :best_run_id 8 | t.boolean :covered, default: false, null: false 9 | 10 | t.timestamps null: false 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20151208214540_create_lesson_records.rb: -------------------------------------------------------------------------------- 1 | class CreateLessonRecords < ActiveRecord::Migration 2 | def change 3 | create_table :lesson_records do |t| 4 | t.integer :lesson_id, null: false 5 | t.integer :user_id, null: false 6 | t.integer :classroom_id, null: false 7 | t.integer :views, default: 0, null: false 8 | t.boolean :covered, default: false, null: false 9 | 10 | t.timestamps null: false 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20151210122605_make_task_fields_mandatory.rb: -------------------------------------------------------------------------------- 1 | class MakeTaskFieldsMandatory < ActiveRecord::Migration 2 | def change 3 | change_column_null(:tasks, :memory_limit_kb, false) 4 | change_column_null(:tasks, :time_limit_ms, false) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20160104161048_make_source_code_nullable_in_task.rb: -------------------------------------------------------------------------------- 1 | class MakeSourceCodeNullableInTask < ActiveRecord::Migration 2 | def change 3 | change_column_null(:task_runs, :source_code, true) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160303075349_add_main_flag_to_courses.rb: -------------------------------------------------------------------------------- 1 | class AddMainFlagToCourses < ActiveRecord::Migration 2 | def change 3 | add_column :courses, :is_main, :boolean, null: false, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160305073957_add_unique_constraint_on_classroom_records.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueConstraintOnClassroomRecords < ActiveRecord::Migration 2 | def change 3 | add_index :classroom_records, [:classroom_id, :user_id], unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160310070738_add_runs_limit_to_task_record.rb: -------------------------------------------------------------------------------- 1 | class AddRunsLimitToTaskRecord < ActiveRecord::Migration 2 | def change 3 | add_column :task_records, :runs_limit, :integer, null: false, default: TaskRecord::DEFAULT_MAX_RUNS_LIMIT 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160314071507_create_user_invitations.rb: -------------------------------------------------------------------------------- 1 | class CreateUserInvitations < ActiveRecord::Migration 2 | def change 3 | create_table :user_invitations do |t| 4 | t.string :email, null: false 5 | t.boolean :used, null: false, default: false 6 | t.datetime :used_at 7 | 8 | t.timestamps null: false 9 | end 10 | 11 | add_index :user_invitations, :email, unique: true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20160314074133_add_active_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddActiveToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :active, :boolean, null: false, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160417163920_add_subtitle_to_course.rb: -------------------------------------------------------------------------------- 1 | class AddSubtitleToCourse < ActiveRecord::Migration 2 | def change 3 | add_column :courses, :subtitle, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160418192655_add_slug_to_course.rb: -------------------------------------------------------------------------------- 1 | class AddSlugToCourse < ActiveRecord::Migration 2 | def change 3 | add_column :courses, :slug, :string 4 | 5 | Course.all.each do |course| 6 | slug = course.title.downcase.gsub(/\s/, "-").gsub(/[^a-z0-9\-]/, "")[0..15] 7 | course.update_attributes(slug: slug) 8 | end 9 | 10 | change_column_null(:courses, :slug, false) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20160418200353_add_unique_index_on_course_slug.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueIndexOnCourseSlug < ActiveRecord::Migration 2 | def change 3 | add_index :courses, :slug, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160418200506_add_slug_to_classrooms.rb: -------------------------------------------------------------------------------- 1 | class AddSlugToClassrooms < ActiveRecord::Migration 2 | def change 3 | add_column :classrooms, :slug, :string 4 | 5 | Classroom.all.each do |classroom| 6 | classroom.update_attributes(slug: classroom.course.slug) 7 | end 8 | 9 | change_column_null :classrooms, :slug, false 10 | 11 | add_index :classrooms, :slug, unique: true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20160429170700_create_delayed_jobs.rb: -------------------------------------------------------------------------------- 1 | class CreateDelayedJobs < ActiveRecord::Migration 2 | def self.up 3 | create_table :delayed_jobs, force: true do |table| 4 | table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue 5 | table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually. 6 | table.text :handler, null: false # YAML-encoded string of the object that will do work 7 | table.text :last_error # reason for last failure (See Note below) 8 | table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. 9 | table.datetime :locked_at # Set when a client is working on this object 10 | table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) 11 | table.string :locked_by # Who is working on this object (if locked) 12 | table.string :queue # The name of the queue this job is in 13 | table.timestamps null: true 14 | end 15 | 16 | add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority" 17 | end 18 | 19 | def self.down 20 | drop_table :delayed_jobs 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20160502194330_add_solution_to_task.rb: -------------------------------------------------------------------------------- 1 | class AddSolutionToTask < ActiveRecord::Migration 2 | def change 3 | add_column :tasks, :markdown_solution, :text 4 | add_column :tasks, :solution, :text 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20160608104441_add_active_to_classroom_record.rb: -------------------------------------------------------------------------------- 1 | class AddActiveToClassroomRecord < ActiveRecord::Migration 2 | def change 3 | add_column :classroom_records, :active, :boolean 4 | 5 | ClassroomRecord.find_each do |record| 6 | record.update_attributes(active: true) 7 | end 8 | 9 | change_column_null(:classroom_records, :active, false) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20160608110642_add_user_limit_to_classroom.rb: -------------------------------------------------------------------------------- 1 | class AddUserLimitToClassroom < ActiveRecord::Migration 2 | def change 3 | add_column :classrooms, :user_limit, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160614144305_add_display_status_to_task_run.rb: -------------------------------------------------------------------------------- 1 | class AddDisplayStatusToTaskRun < ActiveRecord::Migration 2 | def change 3 | add_column :task_runs, :display_status, :string 4 | 5 | TaskRun.find_each do |task_run| 6 | task_run.update_attribute(:display_status, display_status(task_run)) 7 | end 8 | 9 | change_column_null :task_runs, :display_status, false 10 | end 11 | 12 | private 13 | 14 | def display_status task_run 15 | if task_run.status == TaskRun::STATUS_SUCCESS 16 | if task_run.points == Task::TASK_MAX_POINTS 17 | "Accepted" 18 | else 19 | "Some Errors" 20 | end 21 | else 22 | task_run.status.titlecase 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /db/migrate/20160614152702_add_compilation_log_to_task_run.rb: -------------------------------------------------------------------------------- 1 | class AddCompilationLogToTaskRun < ActiveRecord::Migration 2 | def change 3 | add_column :task_runs, :compilation_log, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160614201512_add_error_details_to_task_runs.rb: -------------------------------------------------------------------------------- 1 | class AddErrorDetailsToTaskRuns < ActiveRecord::Migration 2 | def change 3 | add_column :task_runs, :has_ml, :boolean, default: false, null: false 4 | add_column :task_runs, :has_tl, :boolean, default: false, null: false 5 | add_column :task_runs, :has_wa, :boolean, default: false, null: false 6 | add_column :task_runs, :has_re, :boolean, default: false, null: false 7 | add_column :task_runs, :re_details, :text 8 | 9 | TaskRun.find_each do |task_run| 10 | has_ml = task_run.message.present? && task_run.message.include?("ml") 11 | has_tl = task_run.message.present? && task_run.message.include?("tl") 12 | has_wa = task_run.message.present? && task_run.message.include?("wa") 13 | has_re = task_run.message.present? && task_run.message.include?("re") 14 | task_run.update_attributes(has_ml: has_ml, has_tl: has_tl, has_wa: has_wa, has_re: has_re) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20160713161017_add_wrapper_and_boilerplate_code_to_tasks.rb: -------------------------------------------------------------------------------- 1 | class AddWrapperAndBoilerplateCodeToTasks < ActiveRecord::Migration 2 | def change 3 | add_column :tasks, :cpp_boilerplate, :text 4 | add_column :tasks, :cpp_wrapper, :text 5 | add_column :tasks, :java_boilerplate, :text 6 | add_column :tasks, :java_wrapper, :text 7 | add_column :tasks, :python_boilerplate, :text 8 | add_column :tasks, :python_wrapper, :text 9 | add_column :tasks, :ruby_boilerplate, :text 10 | add_column :tasks, :ruby_wrapper, :text 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20160713183600_add_last_programming_language_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddLastProgrammingLanguageToUser < ActiveRecord::Migration 2 | def change 3 | add_column :users, :last_programming_language, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160716163403_set_last_programming_language_based_on_submissions.rb: -------------------------------------------------------------------------------- 1 | class SetLastProgrammingLanguageBasedOnSubmissions < ActiveRecord::Migration 2 | def change 3 | User.find_each do |user| 4 | latest_task_run = user.task_runs.newest_first.first 5 | if latest_task_run.present? 6 | user.update_attributes(last_programming_language: latest_task_run.lang) 7 | end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20160817091910_add_public_to_courses.rb: -------------------------------------------------------------------------------- 1 | class AddPublicToCourses < ActiveRecord::Migration 2 | def change 3 | add_column :courses, :public, :boolean, null: false, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160826161231_increase_runs_limit.rb: -------------------------------------------------------------------------------- 1 | class IncreaseRunsLimit < ActiveRecord::Migration 2 | def change 3 | change_column_default :task_records, :runs_limit, TaskRecord::DEFAULT_MAX_RUNS_LIMIT 4 | 5 | TaskRecord.where(runs_limit: 5).update_all(runs_limit: TaskRecord::DEFAULT_MAX_RUNS_LIMIT) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20160906111443_add_sidebar_content_to_lesson.rb: -------------------------------------------------------------------------------- 1 | class AddSidebarContentToLesson < ActiveRecord::Migration 2 | def change 3 | add_column :lessons, :sidebar_content, :text 4 | add_column :lessons, :markdown_sidebar_content, :text 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20160920113459_create_quizzes.rb: -------------------------------------------------------------------------------- 1 | class CreateQuizzes < ActiveRecord::Migration 2 | def change 3 | create_table :quizzes do |t| 4 | t.string :title, null: false 5 | t.integer :creator_id, null: false 6 | 7 | t.timestamps null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20160920113641_create_quiz_questions.rb: -------------------------------------------------------------------------------- 1 | class CreateQuizQuestions < ActiveRecord::Migration 2 | def change 3 | create_table :quiz_questions do |t| 4 | t.integer :quiz_id, null: false 5 | t.text :content, null: false 6 | t.string :question_type, null: false 7 | t.string :freetext_regex 8 | 9 | t.timestamps null: false 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20160920113733_create_quiz_answers.rb: -------------------------------------------------------------------------------- 1 | class CreateQuizAnswers < ActiveRecord::Migration 2 | def change 3 | create_table :quiz_answers do |t| 4 | t.integer :quiz_question_id, null: false 5 | t.text :content, null: false 6 | t.boolean :correct, null: false 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20160928125223_add_maximum_attempts_and_time_to_wait_to_quizzes.rb: -------------------------------------------------------------------------------- 1 | class AddMaximumAttemptsAndTimeToWaitToQuizzes < ActiveRecord::Migration 2 | def change 3 | add_column :quizzes, :maximum_attempts, :integer 4 | add_column :quizzes, :wait_time_seconds, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20160928140809_create_quiz_attempts.rb: -------------------------------------------------------------------------------- 1 | class CreateQuizAttempts < ActiveRecord::Migration 2 | def change 3 | create_table :quiz_attempts do |t| 4 | t.integer :quiz_id, null: false 5 | t.integer :user_id, null: false 6 | t.float :score, null: false 7 | t.text :answers_json, null: false 8 | t.text :message 9 | 10 | t.timestamps null: false 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20160929185657_add_lessons_quizzes_table.rb: -------------------------------------------------------------------------------- 1 | class AddLessonsQuizzesTable < ActiveRecord::Migration 2 | def change 3 | create_table :lessons_quizzes do |t| 4 | t.integer :lesson_id 5 | t.integer :quiz_id 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20161018120810_add_explanation_to_quiz_questions.rb: -------------------------------------------------------------------------------- 1 | class AddExplanationToQuizQuestions < ActiveRecord::Migration 2 | def change 3 | add_column :quiz_questions, :explanation, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161216140911_add_markdown_to_quiz_questions.rb: -------------------------------------------------------------------------------- 1 | class AddMarkdownToQuizQuestions < ActiveRecord::Migration 2 | def change 3 | add_column :quiz_questions, :markdown_content, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161220144607_add_markdown_explanation_to_quiz_questions.rb: -------------------------------------------------------------------------------- 1 | class AddMarkdownExplanationToQuizQuestions < ActiveRecord::Migration 2 | def change 3 | add_column :quiz_questions, :markdown_explanation, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161220161304_add_markdown_to_quiz_answers.rb: -------------------------------------------------------------------------------- 1 | class AddMarkdownToQuizAnswers < ActiveRecord::Migration 2 | def change 3 | add_column :quiz_answers, :markdown_content, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161221104754_transfer_question_content_to_markdown.rb: -------------------------------------------------------------------------------- 1 | class TransferQuestionContentToMarkdown < ActiveRecord::Migration 2 | def up 3 | QuizQuestion.find_each do |quiz_question| 4 | if quiz_question.content.present? 5 | say "Updating content of quiz questions #{ quiz_question.id }" 6 | quiz_question.update_attributes(markdown_content: quiz_question.content) 7 | else 8 | say "Error: empty quiz question content found!" 9 | end 10 | 11 | if quiz_question.explanation.present? 12 | say "Updating explanation of quiz questions #{ quiz_question.id }" 13 | quiz_question.update_attributes(markdown_explanation: quiz_question.explanation) 14 | end 15 | end 16 | 17 | 18 | change_column_null(:quiz_questions, :markdown_content, false) 19 | end 20 | 21 | def down 22 | change_column_null(:quiz_questions, :markdown_content, true) 23 | 24 | QuizQuestion.update_all(markdown_content: nil, markdown_explanation: nil) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /db/migrate/20161226071326_transfer_quiz_answers_content_to_markdown.rb: -------------------------------------------------------------------------------- 1 | class TransferQuizAnswersContentToMarkdown < ActiveRecord::Migration 2 | def up 3 | QuizAnswer.find_each do |quiz_answer| 4 | if quiz_answer.content.present? 5 | say "Updating content of quiz answers #{ quiz_answer.id }" 6 | quiz_answer.update_attributes(markdown_content: quiz_answer.content) 7 | else 8 | say "Error: empty quiz answer content found!" 9 | end 10 | end 11 | 12 | 13 | change_column_null(:quiz_answers, :markdown_content, false) 14 | end 15 | 16 | def down 17 | change_column_null(:quiz_answers, :markdown_content, true) 18 | 19 | QuizAnswer.update_all(markdown_content: nil) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /db/migrate/20170119113201_add_disable_task_submissions_to_courses.rb: -------------------------------------------------------------------------------- 1 | class AddDisableTaskSubmissionsToCourses < ActiveRecord::Migration 2 | def change 3 | add_column :courses, :disable_task_submissions, :boolean, default: false, null: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20171101202536_add_public_to_task_run.rb: -------------------------------------------------------------------------------- 1 | class AddPublicToTaskRun < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :task_runs, :show_source_code, :boolean, default: false, null: false 4 | add_column :task_runs, :show_user_name, :boolean, default: false, null: false 5 | 6 | TaskRun.update_all(show_source_code: true) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/update_runs_status.rake: -------------------------------------------------------------------------------- 1 | namespace :runs do 2 | desc "Update runs statuses" 3 | task :update_status => :environment do 4 | api = GraderApi.new 5 | 6 | running = true 7 | puts "Ready to update statuses" 8 | 9 | last_run_id = nil 10 | 11 | while running do 12 | ["INT", "TERM"].each do |signal| 13 | Signal.trap(signal) do 14 | puts "Stopping..." 15 | running = false 16 | end 17 | end 18 | 19 | found_run = ProcessTaskRun.new(api).call 20 | 21 | sleep 1 unless found_run && found_run.id != last_run_id 22 | 23 | last_run_id = found_run.id if found_run 24 | end 25 | end 26 | end 27 | 28 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/log/.keep -------------------------------------------------------------------------------- /provisioning/deploy_production.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | sudo: no 4 | 5 | vars: 6 | app_root: /home/hiredintech/applications/codemarathon 7 | app_name: codemarathon 8 | git_repo: git@bitbucket.org:hiredintechteam/codemarathon.git 9 | git_branch: hiredintech 10 | 11 | roles: 12 | - deploy 13 | # - restart 14 | -------------------------------------------------------------------------------- /provisioning/production.ini: -------------------------------------------------------------------------------- 1 | [hiredintech] 2 | 45.55.21.48 ansible_ssh_user=hiredintech 3 | -------------------------------------------------------------------------------- /provisioning/restart_production.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | sudo: no 4 | 5 | vars: 6 | app_name: codemarathon 7 | 8 | roles: 9 | - restart 10 | -------------------------------------------------------------------------------- /provisioning/roles/backup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Installs the backup gem 3 | command: bash -lc "gem install backup" 4 | args: 5 | chdir: "{{ app_root }}" 6 | 7 | # Set up crontab manually, or add the gem whenever for more automation. 8 | -------------------------------------------------------------------------------- /provisioning/roles/config/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Get project code from Github 3 | git: 4 | repo="{{ git_repo }}" 5 | dest="{{ app_root }}" 6 | accept_hostkey=yes 7 | version="{{ git_branch }}" 8 | force=yes 9 | 10 | - name: Bundle install 11 | command: bash -lc "bundle install" 12 | args: 13 | chdir: "{{ app_root }}" 14 | 15 | - name: Set up database 16 | shell: bash -lc "RAILS_ENV=production bundle exec rake db:setup" 17 | args: 18 | chdir: "{{ app_root }}" 19 | 20 | - name: Create pids directory 21 | file: path="{{ app_root }}"/tmp/pids state=directory 22 | 23 | - name: Create unicorn init script 24 | become: yes 25 | become_method: sudo 26 | template: src=unicorn_init.j2 dest=/etc/init.d/unicorn_{{ app_name }} mode=0744 27 | 28 | - name: Create nginx config 29 | become: yes 30 | become_method: sudo 31 | template: src=nginx_config.j2 dest=/etc/nginx/sites-enabled/{{ app_name }} mode=0744 32 | 33 | # This one will set PORT env vars for the jobs, and this can break the links 34 | # generated in emails that are sent through delayed jobs. 35 | - name: Set up upstart for hiredintech rake task 36 | shell: bash -lc "rvmsudo foreman export upstart /etc/init -a {{ app_name }} -u hiredintech" 37 | args: 38 | chdir: "{{ app_root }}" 39 | 40 | # - name: Start runs status update rake task for the first time 41 | # become: yes 42 | # become_method: sudo 43 | # shell: bash -lc "start {{ app_name }}" 44 | 45 | # Grader rake logs will go to /var/log/codemarathon_grader/worker-1.log 46 | -------------------------------------------------------------------------------- /provisioning/roles/config/templates/nginx_config.j2: -------------------------------------------------------------------------------- 1 | upstream codemarathon_unicorn { 2 | server unix:/tmp/unicorn.{{ app_name }}.sock fail_timeout=0; 3 | } 4 | 5 | server { 6 | listen 80; 7 | server_name {{ server_name }}; 8 | return 301 https://$server_name$request_uri; 9 | } 10 | 11 | server { 12 | listen 443 ssl; 13 | server_name {{ server_name }}; 14 | root {{ app_root }}/public; 15 | 16 | ssl on; 17 | ssl_certificate /etc/nginx/ssl/train.hiredintech.com.crt; 18 | ssl_certificate_key /etc/nginx/ssl/train.hiredintech.com.key; 19 | 20 | ssl_session_timeout 5m; 21 | 22 | ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2; 23 | ssl_ciphers HIGH:!aNULL:!MD5; 24 | ssl_prefer_server_ciphers on; 25 | 26 | location ^~ /assets/ { 27 | gzip_static on; 28 | expires max; 29 | add_header Cache-Control public; 30 | } 31 | 32 | try_files $uri/index.html $uri @codemarathon_unicorn; 33 | location @codemarathon_unicorn { 34 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 35 | proxy_set_header Host $http_host; 36 | proxy_set_header X-Forwarded-Proto https; 37 | proxy_redirect off; 38 | proxy_pass http://codemarathon_unicorn; 39 | } 40 | 41 | error_page 500 502 503 504 /500.html; 42 | client_max_body_size 4G; 43 | keepalive_timeout 10; 44 | } 45 | -------------------------------------------------------------------------------- /provisioning/roles/config/templates/nginx_config.no_ssl.j2: -------------------------------------------------------------------------------- 1 | upstream codemarathon_unicorn { 2 | server unix:/tmp/unicorn.{{ app_name }}.sock fail_timeout=0; 3 | } 4 | 5 | server { 6 | listen 80; 7 | # listen 443; 8 | server_name {{ server_name }}; 9 | root {{ app_root }}/public; 10 | 11 | #ssl on; 12 | #ssl_certificate /etc/ssl/server.crt; 13 | #ssl_certificate_key /etc/ssl/server.key; 14 | 15 | #ssl_session_timeout 5m; 16 | 17 | #ssl_protocols SSLv2 SSLv3 TLSv1; 18 | #ssl_ciphers HIGH:!aNULL:!MD5; 19 | #ssl_prefer_server_ciphers on; 20 | 21 | location ^~ /assets/ { 22 | gzip_static on; 23 | expires max; 24 | add_header Cache-Control public; 25 | } 26 | 27 | try_files $uri/index.html $uri @codemarathon_unicorn; 28 | location @codemarathon_unicorn { 29 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 30 | proxy_set_header Host $http_host; 31 | proxy_redirect off; 32 | proxy_pass http://codemarathon_unicorn; 33 | } 34 | 35 | error_page 500 502 503 504 /500.html; 36 | client_max_body_size 4G; 37 | keepalive_timeout 10; 38 | } 39 | -------------------------------------------------------------------------------- /provisioning/roles/deploy/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Update project code from Github 3 | git: 4 | repo="{{ git_repo }}" 5 | dest="{{ app_root }}" 6 | accept_hostkey=yes 7 | version="{{ git_branch }}" 8 | force=yes 9 | 10 | - name: Bundle install 11 | command: bash -lc "bundle install" 12 | args: 13 | chdir: "{{ app_root }}" 14 | 15 | - name: Run migrations 16 | shell: bash -lc "RAILS_ENV=production bundle exec rake db:migrate" 17 | args: 18 | chdir: "{{ app_root }}" 19 | 20 | - name: Precompile assets 21 | shell: bash -lc "RAILS_ENV=production bundle exec rake assets:precompile" 22 | args: 23 | chdir: "{{ app_root }}" 24 | -------------------------------------------------------------------------------- /provisioning/roles/restart/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Restart unicorn 3 | become: yes 4 | become_method: sudo 5 | shell: bash -lc "/etc/init.d/unicorn_{{ app_name }} restart" 6 | 7 | - name: Restart nginx 8 | become: yes 9 | become_method: sudo 10 | service: name=nginx state=restarted 11 | # shell: bash -lc "nginx -s reload" 12 | 13 | - name: Restart runs status update rake task 14 | become: yes 15 | become_method: sudo 16 | shell: bash -lc "restart {{ app_name }}" 17 | -------------------------------------------------------------------------------- /provisioning/roles/start/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Start unicorn 3 | become: yes 4 | become_method: sudo 5 | shell: bash -lc "/etc/init.d/unicorn_{{ app_name }} start" 6 | 7 | - name: Start nginx 8 | become: yes 9 | become_method: sudo 10 | service: name=nginx state=started 11 | 12 | - name: Start runs update statuses rake task 13 | become: yes 14 | become_method: sudo 15 | shell: bash -lc "start {{ app_name }}" 16 | -------------------------------------------------------------------------------- /provisioning/roles/stop/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Stop unicorn 3 | become: yes 4 | become_method: sudo 5 | shell: bash -lc "/etc/init.d/unicorn_{{ app_name }} stop" 6 | 7 | - name: Stop nginx 8 | become: yes 9 | become_method: sudo 10 | service: name=nginx state=stopped 11 | 12 | - name: Stop runs status update rake task 13 | become: yes 14 | become_method: sudo 15 | shell: bash -lc "stop {{ app_name }}" 16 | -------------------------------------------------------------------------------- /provisioning/setup_backups.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | sudo: no 4 | 5 | vars: 6 | app_root: /home/hiredintech/applications/codemarathon 7 | app_name: codemarathon 8 | 9 | roles: 10 | - backup 11 | # - restart 12 | -------------------------------------------------------------------------------- /provisioning/setup_vagrant.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | sudo: no 4 | 5 | vars: 6 | ruby_version: 2.2.2 7 | 8 | roles: 9 | - install 10 | -------------------------------------------------------------------------------- /provisioning/start_production.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | sudo: no 4 | 5 | vars: 6 | app_name: codemarathon 7 | 8 | roles: 9 | - start 10 | -------------------------------------------------------------------------------- /provisioning/stop_production.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | sudo: no 4 | 5 | vars: 6 | app_name: codemarathon 7 | 8 | roles: 9 | - stop 10 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 | 17 | 18 |

    19 |

    Hey, we're sorry but this page does not exist at the moment.

    20 | 21 |

    You could go back to the home page

    22 |
    23 | 24 | 25 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 | 17 | 18 |

    19 |

    Hm, something seems to be broken. We'll look into it and we apologize for the inconvenience!

    20 | 21 |

    You could go back to the home page

    22 |
    23 | 24 | 25 | -------------------------------------------------------------------------------- /public/css/styles.css: -------------------------------------------------------------------------------- 1 | div.dialog { 2 | width: 95%; 3 | max-width: 33em; 4 | margin: 1em auto 0; 5 | margin-top: 20%; 6 | text-align: center; 7 | font-family: medium-content-serif-font,Georgia,Cambria,"Times New Roman",Times,serif; 8 | font-weight: 400; 9 | font-style: normal; 10 | line-height: 1.58; 11 | letter-spacing: -.003em; 12 | font-size: 20px; 13 | } 14 | -------------------------------------------------------------------------------- /public/img/hiredintech_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/public/img/hiredintech_logo.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/controllers/pages_controller_spec.rb: -------------------------------------------------------------------------------- 1 | describe PagesController do 2 | describe "#home" do 3 | before do 4 | get :home 5 | end 6 | 7 | it { is_expected.to respond_with(:found) } 8 | end 9 | 10 | describe "#about" do 11 | before do 12 | get :about 13 | end 14 | 15 | it { is_expected.to respond_with(:success) } 16 | end 17 | 18 | describe "#about_codemarathon" do 19 | before do 20 | get :about_codemarathon 21 | end 22 | 23 | it { is_expected.to respond_with(:success) } 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/factories/classroom_records.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/spec/factories/classroom_records.rb -------------------------------------------------------------------------------- /spec/factories/classrooms.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :classroom do 3 | name "Classroom name" 4 | sequence(:slug) { |i| "classroom-slug-#{ i }" } 5 | 6 | course 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/courses.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :course do 3 | title "Some course title" 4 | subtitle "Some course subtitle" 5 | sequence(:slug) { |i| "course-slug-#{ i }" } 6 | markdown_description "Hello world!" 7 | markdown_long_description "Hello *world*! And everyone *else*" 8 | visible true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/lesson_records.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :lesson_record do 3 | views 10 4 | covered false 5 | 6 | user 7 | lesson 8 | classroom 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/factories/lessons.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :lesson do 3 | title "Some lesson title" 4 | sequence(:position) { |i| i } 5 | section 6 | markdown_content "***Great lesson***" 7 | visible true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/quiz_answers.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :quiz_answer do 3 | association :quiz_question 4 | markdown_content "**Quiz answer text**" 5 | correct true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/quiz_attempts.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :quiz_attempt do 3 | association :quiz 4 | association :user 5 | score 1.5 6 | answers_json "{}" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/quiz_questions.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :quiz_question do 3 | association :quiz 4 | markdown_content "**Quiz text**" 5 | markdown_explanation "Test explanation" 6 | question_type QuizQuestion::TYPE_MULTIPLE_CHOICE 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/quizzes.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :quiz do 3 | title "Some quiz title" 4 | association :creator, factory: :user 5 | maximum_attempts 10 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/roles.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :role do 3 | user 4 | 5 | trait :teacher do 6 | role_type User::ROLE_TEACHER 7 | end 8 | 9 | trait :admin do 10 | role_type User::ROLE_ADMIN 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/factories/sections.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :section do 3 | title "Some section title" 4 | sequence(:position) { |i| i } 5 | course 6 | visible true 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/task_records.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :task_record do 3 | user 4 | task 5 | best_score 50.0 6 | association :best_run, factory: :task_run 7 | covered true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/factories/task_runs.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :task_run do 3 | user 4 | task 5 | source_code "print \'hello world\'" 6 | lang "ruby" 7 | status TaskRun::STATUS_SUCCESS 8 | external_key "14" 9 | message "success" 10 | grader_log "some log" 11 | memory_limit_kb 1024 12 | time_limit_ms 1000 13 | run_type TaskRun::TYPE_RUN_TASK 14 | points 50.0 15 | display_status "Some Errors" 16 | compilation_log nil 17 | has_ml false 18 | has_tl true 19 | has_wa true 20 | has_re false 21 | re_details nil 22 | show_source_code true 23 | show_user_name false 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/factories/tasks.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :task do 3 | title "Some task title" 4 | task_type Task::TASK_TYPE_IOFILES 5 | markdown_description "**Task description**" 6 | association :creator, factory: :user 7 | visible true 8 | external_key "14" 9 | memory_limit_kb 1024 10 | time_limit_ms 1000 11 | cpp_boilerplate "int foo(int value) { // write code here }" 12 | cpp_wrapper "int main() { foo(); return 0; }" 13 | java_boilerplate "java boilerplate" 14 | java_wrapper "java wrapper" 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/factories/user_invitations.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :user_invitation do 3 | sequence(:email) { |i| "user-#{ i }@codemarathon.com" } 4 | used false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :user do 3 | sequence(:email) { |i| "user-#{ i }@codemarathon.com" } 4 | sequence(:name) { |i| "Some Name-#{ i }" } 5 | password "some_test_pass" 6 | active true 7 | 8 | trait :teacher do 9 | after(:create) do |user, evaluator| 10 | create(:role, :teacher, user: user) 11 | end 12 | end 13 | 14 | trait :admin do 15 | after(:create) do |user, evaluator| 16 | create(:role, :admin, user: user) 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/features/oauth_sign_in_spec.rb: -------------------------------------------------------------------------------- 1 | feature "OAuth log in" do 2 | 3 | given(:mock_user_data) do 4 | { uid: '12345', info: {email: 'test@test.com', name: 'Some Name'} } 5 | end 6 | 7 | background do 8 | OmniAuth.config.add_mock(:google_oauth2, mock_user_data) 9 | OmniAuth.config.add_mock(:github, mock_user_data) 10 | 11 | visit new_user_session_path 12 | end 13 | 14 | context "when user signs up" do 15 | scenario "using Google" do 16 | click_link "Log in with Google" 17 | 18 | expect(current_path).to eq(courses_path) 19 | expect(page).to have_text(mock_user_data[:info][:name]) 20 | expect(User.count).to eq(1) 21 | end 22 | 23 | scenario "using Github" do 24 | click_link "Log in with GitHub" 25 | 26 | expect(current_path).to eq(courses_path) 27 | expect(page).to have_text(mock_user_data[:info][:name]) 28 | expect(User.count).to eq(1) 29 | end 30 | end 31 | 32 | context "when user logs in" do 33 | given(:user) { FactoryGirl.create(:user, email: mock_user_data[:info][:email]) } 34 | 35 | background do 36 | user.confirmed_at = Time.now 37 | user.save 38 | end 39 | 40 | scenario "using Google" do 41 | click_link "Log in with Google" 42 | 43 | expect(current_path).to eq(courses_path) 44 | expect(page).to have_text(mock_user_data[:info][:name]) 45 | expect(User.count).to eq(1) 46 | end 47 | 48 | scenario "using Github" do 49 | click_link "Log in with GitHub" 50 | 51 | expect(current_path).to eq(courses_path) 52 | expect(page).to have_text(mock_user_data[:info][:name]) 53 | expect(User.count).to eq(1) 54 | end 55 | end 56 | 57 | end 58 | -------------------------------------------------------------------------------- /spec/features/user_sign_in_spec.rb: -------------------------------------------------------------------------------- 1 | feature "User log in" do 2 | 3 | given(:user) { FactoryGirl.create :user } 4 | 5 | context "user log in" do 6 | background do 7 | user.confirmed_at = Time.now 8 | user.save 9 | visit new_user_session_path 10 | end 11 | 12 | scenario "successful" do 13 | within "#new_user" do 14 | fill_in "Email", with: user.email 15 | fill_in "Password", with: user.password 16 | click_button "Log in" 17 | end 18 | 19 | expect(page).to have_text user.name 20 | end 21 | 22 | scenario "incorrect email" do 23 | within "#new_user" do 24 | fill_in "Email", with: user.email.reverse 25 | fill_in "Password", with: user.password 26 | click_button "Log in" 27 | end 28 | 29 | expect(page).to have_content (/Invalid email or password/i) 30 | end 31 | 32 | scenario "incorrect password" do 33 | within "#new_user" do 34 | fill_in "Email", with: user.email 35 | fill_in "Password", with: user.email.reverse 36 | click_button "Log in" 37 | end 38 | 39 | expect(page).to have_content (/Invalid email or password/i) 40 | end 41 | end 42 | 43 | end 44 | -------------------------------------------------------------------------------- /spec/fixtures/quiz_attempt_answers.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": { 3 | "1": { 4 | "user_answer": false, 5 | "correct_answer": false, 6 | "is_correct": true 7 | }, 8 | "2": { 9 | "user_answer": true, 10 | "correct_answer": true, 11 | "is_correct": true 12 | }, 13 | "3": { 14 | "user_answer": false, 15 | "correct_answer": true, 16 | "is_correct": false 17 | }, 18 | "score": 0.6666666666666666 19 | }, 20 | "2": { 21 | "user_answer": "sdf", 22 | "correct_answer": "\\A[a-z]+\\z", 23 | "is_correct": {}, 24 | "score": 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spec/models/role_spec.rb: -------------------------------------------------------------------------------- 1 | describe Role do 2 | it { is_expected.to validate_presence_of(:user) } 3 | it { is_expected.to validate_presence_of(:role_type) } 4 | 5 | it { is_expected.to belong_to(:user) } 6 | end 7 | -------------------------------------------------------------------------------- /spec/models/user_invitation_spec.rb: -------------------------------------------------------------------------------- 1 | describe UserInvitation do 2 | let!(:user_invitation) { FactoryGirl.create(:user_invitation) } 3 | 4 | it { is_expected.to validate_presence_of(:email) } 5 | it { is_expected.to validate_uniqueness_of(:email) } 6 | end 7 | -------------------------------------------------------------------------------- /spec/services/fetch_task_statistics_spec.rb: -------------------------------------------------------------------------------- 1 | describe FetchTaskStatistics do 2 | describe "#fetch_successful_runs_stats" do 3 | let!(:task) { FactoryGirl.create(:task) } 4 | let!(:task_run1) { FactoryGirl.create(:task_run, points: 0, lang: 'cpp', task: task) } 5 | let!(:task_run2) { FactoryGirl.create(:task_run, points: 100, lang: 'cpp', task: task) } 6 | let!(:task_run3) { FactoryGirl.create(:task_run, points: 100, lang: 'java', task: task) } 7 | let!(:task_run4) { FactoryGirl.create(:task_run, points: 0, lang: 'python', task: task) } 8 | 9 | subject { FetchTaskStatistics.new.call } 10 | 11 | it "computes total statistics" do 12 | expect(subject[task.id][:successful_runs]).to eq(2) 13 | expect(subject[task.id][:all_runs]).to eq(4) 14 | expect(subject[task.id][:success_percent]).to eq(50) 15 | end 16 | 17 | context "with a given langauge" do 18 | it "computes 100\% success when all submissions are successful" do 19 | expect(subject[task.id][:java_success_percent]).to eq(100) 20 | end 21 | 22 | it "computes partial success when there are failed submissions" do 23 | expect(subject[task.id][:cpp_success_percent]).to eq(50) 24 | end 25 | 26 | it "computes 0 percent success when there are no successful submissions" do 27 | expect(subject[task.id][:python_success_percent]).to eq(0) 28 | end 29 | 30 | it "contains no data if there are no submissions for a language" do 31 | expect(subject[task.id][:ruby_success_percent]).to be_nil 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/services/score_quiz_attempt_spec.rb: -------------------------------------------------------------------------------- 1 | describe ScoreQuizAttempt do 2 | let(:score_quiz_attempt) { ScoreQuizAttempt.new } 3 | let(:user) { FactoryGirl.create(:user) } 4 | 5 | let(:classroom) { FactoryGirl.create(:classroom) } 6 | let(:lesson) { FactoryGirl.create(:lesson) } 7 | let(:lesson_record) { lesson.lesson_record_for(classroom, user) } 8 | let(:quiz) { FactoryGirl.create(:quiz) } 9 | let(:quiz_question1) { FactoryGirl.create(:quiz_question, 10 | question_type: QuizQuestion::TYPE_MULTIPLE_CHOICE, quiz: quiz) } 11 | let(:quiz_question2) { FactoryGirl.create(:quiz_question, 12 | question_type: QuizQuestion::TYPE_FREETEXT, 13 | freetext_regex: "\\A[a-z]+\\z", quiz: quiz) } 14 | 15 | let!(:quiz_answer1) { FactoryGirl.create(:quiz_answer, 16 | quiz_question: quiz_question1, correct: false) } 17 | let!(:quiz_answer2) { FactoryGirl.create(:quiz_answer, 18 | quiz_question: quiz_question1, correct: true) } 19 | 20 | let(:params) { generate_quiz_answers([quiz_question1, quiz_question2]) } 21 | 22 | describe "#call" do 23 | before do 24 | lesson.quizzes << quiz 25 | ScoreQuizAttempt.new(quiz, user, params["quiz_attempt"]).call 26 | end 27 | 28 | it "computes the score for one quiz attempt" do 29 | expect(QuizAttempt.last.score).to eq(1.5) 30 | end 31 | 32 | it "stores the quiz attempt answers" do 33 | answers = JSON.parse(QuizAttempt.last.answers_json) 34 | expect(answers.has_key?(quiz_question1.id.to_s)).to be_truthy 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/support/factory_girl.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | # Make short `#create` or `#build` factory_girl methods available in specs 3 | config.include FactoryGirl::Syntax::Methods 4 | 5 | factories_to_lint = FactoryGirl.factories.reject do |factory| 6 | factory.name.in? [:course, :section, :lesson, :user, :role] 7 | end 8 | 9 | config.before(:suite) do 10 | begin 11 | DatabaseCleaner.start 12 | FactoryGirl.lint factories_to_lint 13 | ensure 14 | DatabaseCleaner.clean 15 | end 16 | end 17 | end 18 | 19 | # config.before(:each, js: true) do 20 | # # JS examples are run with Poltergeist which uses separate db 21 | # # connection. It's impossible to use :transaction strategy in this case 22 | # # because records shouldn't be available outside of the transaction (e.g. in 23 | # # another db connection). For such examples :truncation strategy is more 24 | # # appropriate. 25 | # DatabaseCleaner.strategy = :truncation 26 | # end 27 | -------------------------------------------------------------------------------- /spec/support/grader_helper.rb: -------------------------------------------------------------------------------- 1 | module GraderHelper 2 | def stub_grader_calls 3 | task_response = { "task_id" => 1, "status" => 0, "message" => "success" } 4 | stub_request(:any, /http:\/\/grader\/tasks.*/).to_return(status: 200, body: task_response.to_json) 5 | 6 | run_response = { "run_id" => 1, "status" => 0, "message" => "success" } 7 | stub_request(:any, /http:\/\/grader\/runs.*/).to_return(status: 200, body: run_response.to_json) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/support/omniauth.rb: -------------------------------------------------------------------------------- 1 | module OmniauthHelpers 2 | def mock_auth_hash provider_name, email=nil 3 | OmniAuth.config.mock_auth[provider_name] = OmniAuth::AuthHash.new({ 4 | provider: provider_name.to_s, 5 | uid: '123456', 6 | credentials: { 7 | token: "sample-token", 8 | expires_at: 1.hours.from_now.to_i 9 | }, 10 | info: { 11 | name: "Some Name", 12 | email: email || "some@email.com" 13 | } 14 | }) 15 | end 16 | end 17 | 18 | OmniAuth.config.test_mode = true 19 | -------------------------------------------------------------------------------- /spec/support/quiz_helper.rb: -------------------------------------------------------------------------------- 1 | module QuizHelper 2 | def generate_quiz_answers quiz_questions 3 | answers = {} 4 | 5 | quiz_questions.each do |quiz_question| 6 | if quiz_question.question_type == QuizQuestion::TYPE_MULTIPLE_CHOICE 7 | answers[quiz_question.id.to_s] = {} 8 | answers[quiz_question.id.to_s]["multiple_answers"] = {} 9 | 10 | quiz_question.quiz_answers.each do |quiz_answer| 11 | answers[quiz_question.id.to_s]["multiple_answers"][quiz_answer.id.to_s] = 1 12 | end 13 | 14 | else 15 | is_correct = quiz_question.correct_freetext_answer?("abc") 16 | 17 | answers[quiz_question.id.to_s] = {} 18 | answers[quiz_question.id.to_s]["freetext_answer"] = "abc" 19 | end 20 | end 21 | 22 | { 'quiz_attempt' => answers } 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/support/shared_db_connection.rb: -------------------------------------------------------------------------------- 1 | class ActiveRecord::Base 2 | mattr_accessor :shared_connection 3 | @@shared_connection = nil 4 | 5 | def self.connection 6 | @@shared_connection || retrieve_connection 7 | end 8 | end 9 | 10 | # Forces all threads to share the same connection. This works on 11 | # Capybara because it starts the web server in a thread. 12 | ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection 13 | -------------------------------------------------------------------------------- /spec/support/webmock.rb: -------------------------------------------------------------------------------- 1 | require 'webmock/rspec' 2 | 3 | WebMock.disable_net_connect!(allow_localhost: true) 4 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/antonrd/codemarathon/0d601b1d1fa5ada715a1e567a0bd55ed8f11170f/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------