├── .gitignore ├── .project ├── .pydevproject ├── Capfile ├── DEVNOTES.md ├── Gemfile ├── README.md ├── README.rdoc ├── Rakefile ├── app ├── assets │ ├── images │ │ ├── bg.png │ │ ├── curl-blue-small.png │ │ ├── curl-gray-small.png │ │ ├── curl-green-small.png │ │ ├── curl-red-small.png │ │ ├── curl-yellow-small.png │ │ ├── curl.png │ │ ├── landing-header-bg.png │ │ ├── navbar-separator.png │ │ └── snake-cap-300h.png │ ├── javascripts │ │ ├── activity_logs.js.coffee │ │ ├── application.js │ │ ├── assignment_offerings.js.coffee │ │ ├── assignment_repositories.js.coffee │ │ ├── assignments.js.coffee │ │ ├── authentications.js.coffee │ │ ├── code.js.coffee │ │ ├── color-picker.js.coffee │ │ ├── course_offerings.js.coffee │ │ ├── courses.js.coffee │ │ ├── environments.js.coffee │ │ ├── errors.js.coffee │ │ ├── example_repositories.js.coffee │ │ ├── global.js.coffee │ │ ├── highcharts-theme.js.coffee │ │ ├── home.js.coffee │ │ ├── internal │ │ │ ├── builtin.js │ │ │ ├── skulpt-uncomp.js │ │ │ └── skulpt.js │ │ ├── juggernaut.js │ │ ├── landing.js.coffee │ │ ├── media-library.js.coffee │ │ ├── organizations.js.coffee │ │ ├── picture_tool.js.coffee │ │ ├── setup.js.coffee │ │ ├── sound.js.coffee │ │ ├── sound_tool.js.coffee │ │ ├── system_configurations.js.coffee │ │ ├── terms.js.coffee │ │ ├── typeaheads.js.coffee │ │ └── upload_roster.js.coffee │ └── stylesheets │ │ ├── activity_logs.css.scss │ │ ├── application.css │ │ ├── assignment_offerings.css.scss │ │ ├── assignment_repositories.css.scss │ │ ├── assignments.css.scss │ │ ├── authentications.css.scss │ │ ├── bootstrap_and_overrides.css.less │ │ ├── code.css.scss │ │ ├── course_offerings.css.scss │ │ ├── courses.css.scss │ │ ├── environments.css.scss │ │ ├── errors.css.scss │ │ ├── example_repositories.css.scss │ │ ├── global.css.scss │ │ ├── home.css.scss │ │ ├── landing.css.scss │ │ ├── organizations.css.scss │ │ ├── scaffolds.css.scss │ │ ├── setup.css.scss │ │ ├── system_configurations.css.scss │ │ └── terms.css.scss ├── controllers │ ├── activity_logs_controller.rb │ ├── application_controller.rb │ ├── assignment_checks_controller.rb │ ├── assignment_offerings_controller.rb │ ├── assignment_repositories_controller.rb │ ├── assignments_controller.rb │ ├── authentications_controller.rb │ ├── code_controller.rb │ ├── course_enrollments_controller.rb │ ├── course_offerings_controller.rb │ ├── course_roles_controller.rb │ ├── courses_controller.rb │ ├── environments_controller.rb │ ├── errors_controller.rb │ ├── example_repositories_controller.rb │ ├── friendly_url_controller.rb │ ├── global_roles_controller.rb │ ├── home_controller.rb │ ├── landing_controller.rb │ ├── media_controller.rb │ ├── organizations_controller.rb │ ├── proxy_controller.rb │ ├── registrations_controller.rb │ ├── scratchpad_repositories_controller.rb │ ├── self_enrollment_controller.rb │ ├── sessions_controller.rb │ ├── setup_controller.rb │ ├── system_configurations_controller.rb │ ├── terms_controller.rb │ ├── typeaheads_controller.rb │ ├── upload_roster_controller.rb │ └── users_controller.rb ├── helpers │ ├── activity_logs_helper.rb │ ├── application_helper.rb │ ├── assignment_offerings_helper.rb │ ├── assignments_helper.rb │ ├── authentications_helper.rb │ ├── code_helper.rb │ ├── course_enrollments_helper.rb │ ├── course_offerings_helper.rb │ ├── courses_helper.rb │ ├── devise_helper.rb │ ├── environments_helper.rb │ ├── errors_helper.rb │ ├── example_repositories_helper.rb │ ├── friendly_url_helper.rb │ ├── global_roles_helper.rb │ ├── home_helper.rb │ ├── landing_helper.rb │ ├── media_helper.rb │ ├── organizations_helper.rb │ ├── setup_helper.rb │ ├── system_configurations_helper.rb │ ├── terms_helper.rb │ └── users_helper.rb ├── mailers │ └── .gitkeep ├── models │ ├── .gitkeep │ ├── ability.rb │ ├── activity_log.rb │ ├── assignment.rb │ ├── assignment_check.rb │ ├── assignment_offering.rb │ ├── assignment_reference_repository.rb │ ├── assignment_repository.rb │ ├── authentication.rb │ ├── check_outcome.rb │ ├── commit_collector.rb │ ├── course.rb │ ├── course_enrollment.rb │ ├── course_offering.rb │ ├── course_offering_scores.rb │ ├── course_role.rb │ ├── environment.rb │ ├── example_repository.rb │ ├── global_role.rb │ ├── media_item.rb │ ├── organization.rb │ ├── personal_repository.rb │ ├── repository.rb │ ├── resource_key_methods.rb │ ├── scratchpad_repository.rb │ ├── system_configuration.rb │ ├── term.rb │ ├── uploaded_roster.rb │ └── user.rb ├── uploaders │ └── media_item_uploader.rb ├── validators │ ├── changeable_validator.rb │ └── path_validator.rb ├── views │ ├── activity_logs │ │ ├── _activity_log.html.haml │ │ ├── _table.html.haml │ │ ├── filter.js.coffee │ │ ├── index.html.haml │ │ └── index.js.coffee │ ├── application │ │ ├── _flashbar.html.haml │ │ ├── _form_errors.html.haml │ │ ├── _index_search.html.haml │ │ ├── _navbar.html.haml │ │ ├── modal_form_errors.js.coffee │ │ └── place_flash.js.coffee │ ├── assignment_checks │ │ ├── _score_history_modal.html.haml │ │ ├── index.js.coffee │ │ └── show.js.coffee │ ├── assignment_offerings │ │ ├── _form.html.haml │ │ ├── _grades.html.haml │ │ ├── _modal.html.haml │ │ ├── create.js.coffee │ │ ├── edit.html.haml │ │ ├── new.js.coffee │ │ └── show.json.rabl │ ├── assignment_repositories │ │ └── show.html.haml │ ├── assignments │ │ ├── _form.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ ├── show.html.haml │ │ └── template_picker.html.haml │ ├── authentications │ │ └── index.html.haml │ ├── code │ │ ├── _check_outcomes_modal.html.haml │ │ ├── _check_results.html.haml │ │ ├── _choose_environment_modal.html.haml │ │ ├── _color_picker.haml │ │ ├── _commit.html.haml │ │ ├── _file_list.html.haml │ │ ├── _picture_tool.haml │ │ ├── _sound_tool.haml │ │ ├── _user_list.html.haml │ │ ├── add_history.js.coffee │ │ ├── check.js.coffee │ │ ├── check_results.js.coffee │ │ ├── prompt_for_environment.js.coffee │ │ ├── related_files │ │ │ └── _assignment_reference_repository.html.haml │ │ ├── show.html.haml │ │ ├── summary │ │ │ ├── _assignment_reference_repository.html.haml │ │ │ ├── _assignment_repository.html.haml │ │ │ ├── _example_repository.html.haml │ │ │ └── _scratchpad_repository.html.haml │ │ ├── update.js.coffee │ │ ├── update_code.js.coffee │ │ └── update_user_list.js.coffee │ ├── course_enrollments │ │ ├── _course_enrollment.html.haml │ │ ├── _modal.html.haml │ │ ├── _table.html.haml │ │ ├── create.js.coffee │ │ ├── destroy.js.coffee │ │ ├── index.js.coffee │ │ └── new.js.coffee │ ├── course_offerings │ │ ├── _form.html.haml │ │ ├── _index.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── new.html.haml │ │ ├── show.html.haml │ │ └── show.js.coffee │ ├── course_roles │ │ ├── _form.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── new.html.haml │ │ └── show.html.haml │ ├── courses │ │ ├── _form.html.haml │ │ ├── _index.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── new.html.haml │ │ └── show.html.haml │ ├── devise │ │ ├── _links.erb │ │ ├── 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 │ │ │ ├── edit.html.erb │ │ │ └── new.html.haml │ │ └── unlocks │ │ │ └── new.html.erb │ ├── environments │ │ ├── _form.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── new.html.haml │ │ └── show.html.haml │ ├── errors │ │ ├── _modal.html.haml │ │ ├── error_403.html.haml │ │ ├── error_404.html.haml │ │ ├── error_500.html.haml │ │ └── error_modal.js.coffee │ ├── example_repositories │ │ ├── _empty.html.haml │ │ ├── _example_repository.html.haml │ │ ├── _modal.html.haml │ │ ├── create.js.coffee │ │ ├── destroy.js.coffee │ │ ├── index.js.coffee │ │ └── new.js.coffee │ ├── global_roles │ │ ├── _form.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── new.html.haml │ │ └── show.html.haml │ ├── home │ │ ├── _course_staff_assignments.html.haml │ │ ├── _course_staff_grades.html.haml │ │ ├── _course_staff_offering_settings.html.haml │ │ ├── _course_staff_settings.html.haml │ │ ├── _course_staff_users.html.haml │ │ ├── _instructor_assignment.html.haml │ │ ├── _no_assignments.html.haml │ │ ├── _no_started_assignments.html.haml │ │ ├── _not_found.html.haml │ │ ├── _not_started_assignment.html.haml │ │ ├── _started_assignment.html.haml │ │ ├── course.js.coffee │ │ ├── course_staff.html.haml │ │ ├── course_student.html.haml │ │ ├── index.html.haml │ │ ├── load_grades.js.coffee │ │ └── refresh_examples.js.coffee │ ├── kaminari │ │ ├── _first_page.html.haml │ │ ├── _gap.html.haml │ │ ├── _last_page.html.haml │ │ ├── _next_page.html.haml │ │ ├── _page.html.haml │ │ ├── _paginator.html.haml │ │ └── _prev_page.html.haml │ ├── landing │ │ └── index.html.haml │ ├── layouts │ │ ├── code.html.haml │ │ ├── error.html.haml │ │ ├── landing.html.haml │ │ ├── logged_in.html.haml │ │ ├── no_container.html.haml │ │ ├── not_logged_in.html.haml │ │ └── setup.html.haml │ ├── media │ │ ├── _filter_item.html.haml │ │ ├── _index.html.haml │ │ ├── _modal.html.haml │ │ ├── destroy.js.coffee │ │ ├── index.html.haml │ │ └── index.js.coffee │ ├── organizations │ │ ├── _form.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── new.html.haml │ │ └── show.html.haml │ ├── registrations │ │ ├── _new.html.haml │ │ └── edit.html.haml │ ├── self_enrollment │ │ ├── _course_list.html.haml │ │ ├── _select_organization.html.haml │ │ ├── _select_term.html.haml │ │ ├── select_organization.js.coffee │ │ └── select_term.js.coffee │ ├── sessions │ │ ├── _sign_in.html.haml │ │ ├── new.html.haml │ │ └── new_initial.html.haml │ ├── setup │ │ └── index.html.haml │ ├── system_configurations │ │ ├── _form.html.haml │ │ ├── edit.html.haml │ │ ├── new.html.haml │ │ └── show.html.haml │ ├── terms │ │ ├── _form.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── new.html.haml │ │ └── show.html.haml │ ├── upload_roster │ │ ├── _modal.html.haml │ │ ├── _table.html.haml │ │ ├── index.js.coffee │ │ └── upload.js.coffee │ └── users │ │ ├── _form.html.haml │ │ ├── _user.html.haml │ │ ├── edit.html.haml │ │ ├── index.html.haml │ │ ├── index.js.coffee │ │ ├── new.html.haml │ │ └── show.html.haml └── workers │ └── check_code_worker.rb ├── code_examples └── image_manipulation.py ├── config.ru ├── config ├── application.rb ├── boot.rb ├── deploy.rb ├── deploy │ ├── production.rb │ └── staging.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── initializers │ ├── backtrace_silencers.rb │ ├── devise.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── omniauth.rb │ ├── rabl.rb │ ├── redis.rb │ ├── secret_token.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── routes.rb └── sidekiq.yml ├── db ├── migrate │ ├── 20120709021020_devise_create_users.rb │ ├── 20120924132009_create_institutions.rb │ ├── 20120924132121_create_departments.rb │ ├── 20120924132144_create_terms.rb │ ├── 20120924132208_create_courses.rb │ ├── 20120924132630_create_course_offerings.rb │ ├── 20120924132808_create_assignments.rb │ ├── 20120924133015_create_assignment_offerings.rb │ ├── 20120924141027_add_initial_columns_to_users.rb │ ├── 20120927011009_create_rails_admin_histories_table.rb │ ├── 20121114021257_create_global_roles.rb │ ├── 20121205005020_create_authentications.rb │ ├── 20121206145720_create_repositories.rb │ ├── 20121212151228_create_course_roles.rb │ ├── 20121212161741_create_course_enrollments.rb │ ├── 20121224211905_create_activity_logs.rb │ ├── 20130103133018_create_system_configurations.rb │ ├── 20130109175139_create_assignment_checks.rb │ ├── 20130111190807_create_check_outcomes.rb │ ├── 20130518031137_create_media_items.rb │ ├── 20130529205501_add_resource_key_to_users.rb │ ├── 20130529205532_add_resource_key_to_assignments.rb │ ├── 20130615112518_add_commit_sha_to_assignment_checks.rb │ ├── 20130617144912_add_self_enrollment_allowed_to_course_offerings.rb │ ├── 20130617145517_drop_departments_table.rb │ ├── 20130617145542_add_institution_id_to_courses.rb │ ├── 20130617204903_remove_institution_id_from_users.rb │ ├── 20130617205241_rename_institutions_to_organizations.rb │ ├── 20130619172521_merge_first_last_name_into_full_name.rb │ ├── 20130623191637_rename_course_offering_crn_and_label.rb │ ├── 20130625201013_create_environments.rb │ ├── 20130625202204_add_default_environment_id_to_courses.rb │ ├── 20130625202241_add_environment_id_to_repositories.rb │ ├── 20131014014924_add_term_id_to_assignments.rb │ ├── 20131119081535_split_full_name_into_first_and_last_name.rb │ └── 20140116001823_add_current_course_and_current_term_to_users.rb ├── schema.rb └── seeds.rb ├── doc ├── README_FOR_APP ├── other_features.md └── todolist.md ├── erd.pdf ├── lib ├── assets │ └── .gitkeep ├── custom_failure.rb ├── rails_extensions.rb ├── render_anywhere.rb └── tasks │ └── .gitkeep ├── notes.txt ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.png └── robots.txt ├── python ├── .gitignore ├── README.md ├── image │ ├── __init__.py │ ├── color.py │ ├── fonts │ │ ├── Comic_Sans_MS.ttf │ │ ├── Comic_Sans_MS_Bold.ttf │ │ ├── Comic_Sans_MS_Bold_Italic.ttf │ │ ├── Comic_Sans_MS_Italic.ttf │ │ ├── LiberationMono.ttf │ │ ├── LiberationMono_Bold.ttf │ │ ├── LiberationMono_Bold_Italic.ttf │ │ ├── LiberationMono_Italic.ttf │ │ ├── Times_New_Roman.ttf │ │ ├── Times_New_Roman_Bold.ttf │ │ ├── Times_New_Roman_Bold_Italic.ttf │ │ ├── Times_New_Roman_Italic.ttf │ │ ├── Verdana.ttf │ │ ├── Verdana_Bold.ttf │ │ ├── Verdana_Bold_Italic.ttf │ │ └── Verdana_Italic.ttf │ ├── picture.py │ ├── pixel.py │ └── style.py ├── media │ └── __init__.py ├── pythy │ ├── __init__.py │ ├── sandbox.py │ └── test.py ├── sound │ ├── __init__.py │ ├── sample.py │ └── sound.py ├── tests │ ├── image │ │ ├── README.md │ │ ├── color_test.py │ │ ├── imgs │ │ │ ├── addArcFilled_10_10_100_50_60_30.png │ │ │ ├── addArcFilled_10_10_100_50_60_30_magenta.png │ │ │ ├── addArc_10_10_100_50_60_30.png │ │ │ ├── addArc_10_10_100_50_60_30_magenta.png │ │ │ ├── addLine_10_10_20_20.png │ │ │ ├── addLine_10_10_20_20_pink.png │ │ │ ├── addOvalFilled_10_10_70_50.png │ │ │ ├── addOvalFilled_10_10_70_50_yellow.png │ │ │ ├── addOval_10_10_70_50.png │ │ │ ├── addOval_10_10_70_50_orange.png │ │ │ ├── addRectFilled_10_10_70_50.png │ │ │ ├── addRectFilled_10_10_70_50_red.png │ │ │ ├── addRect_10_10_70_50.png │ │ │ ├── addRect_10_10_70_50_red.png │ │ │ ├── addTextWithStyle_10_10_Hello_sansSerif_BOLDITALIC_10.png │ │ │ ├── addTextWithStyle_10_10_Hello_sansSerif_BOLDITALIC_10_green.png │ │ │ ├── addText_10_10_Hello.png │ │ │ ├── addText_10_10_Hello_cyan.png │ │ │ ├── copyInto.png │ │ │ ├── orangePic.png │ │ │ └── test.jpg │ │ ├── picture_test.py │ │ ├── pixel_test.py │ │ ├── run.sh │ │ └── style_test.py │ ├── media │ │ ├── README.md │ │ ├── init_test.py │ │ └── run.sh │ └── sound │ │ ├── README.md │ │ ├── run.sh │ │ ├── sample_test.py │ │ ├── sound_test.py │ │ └── sounds │ │ ├── test_mono.wav │ │ └── test_stereo.wav └── unittest │ ├── __init__.py │ ├── __main__.py │ ├── case.py │ ├── loader.py │ ├── main.py │ ├── mock.py │ ├── result.py │ ├── runner.py │ ├── signals.py │ ├── suite.py │ ├── test │ ├── __init__.py │ ├── _test_warnings.py │ ├── dummy.py │ ├── support.py │ ├── test_assertions.py │ ├── test_break.py │ ├── test_case.py │ ├── test_discovery.py │ ├── test_functiontestcase.py │ ├── test_loader.py │ ├── test_program.py │ ├── test_result.py │ ├── test_runner.py │ ├── test_setups.py │ ├── test_skipping.py │ ├── test_suite.py │ └── testmock │ │ ├── __init__.py │ │ ├── support.py │ │ ├── testcallable.py │ │ ├── testhelpers.py │ │ ├── testmagicmethods.py │ │ ├── testmock.py │ │ ├── testpatch.py │ │ ├── testsentinel.py │ │ └── testwith.py │ └── util.py ├── script └── rails ├── spec ├── factories │ ├── activity_log_factory.rb │ ├── course_factory.rb │ ├── course_role_factory.rb │ ├── environment_factory.rb │ ├── global_role_factory.rb │ ├── organization_factory.rb │ ├── system_configuration_factory.rb │ ├── term_factory.rb │ └── user_factory.rb ├── javascripts │ ├── README.md │ ├── index.html │ ├── sounds │ │ ├── test_mono.wav │ │ └── test_stereo.wav │ ├── src │ │ └── sound.js.coffee │ ├── stubs │ │ └── pythy_stubs.js │ ├── tests │ │ └── sound_test.js │ └── vendor │ │ ├── chai.js │ │ ├── coffee-script.js │ │ ├── mocha.css │ │ ├── mocha.js │ │ └── sinon.js ├── models │ ├── activity_log_spec.rb │ ├── course_role_spec.rb │ ├── course_spec.rb │ ├── coverage │ │ ├── .last_run.json │ │ ├── .resultset.json │ │ ├── assets │ │ │ └── 0.7.1 │ │ │ │ ├── application.css │ │ │ │ ├── application.js │ │ │ │ ├── fancybox │ │ │ │ ├── blank.gif │ │ │ │ ├── fancy_close.png │ │ │ │ ├── fancy_loading.png │ │ │ │ ├── fancy_nav_left.png │ │ │ │ ├── fancy_nav_right.png │ │ │ │ ├── fancy_shadow_e.png │ │ │ │ ├── fancy_shadow_n.png │ │ │ │ ├── fancy_shadow_ne.png │ │ │ │ ├── fancy_shadow_nw.png │ │ │ │ ├── fancy_shadow_s.png │ │ │ │ ├── fancy_shadow_se.png │ │ │ │ ├── fancy_shadow_sw.png │ │ │ │ ├── fancy_shadow_w.png │ │ │ │ ├── fancy_title_left.png │ │ │ │ ├── fancy_title_main.png │ │ │ │ ├── fancy_title_over.png │ │ │ │ ├── fancy_title_right.png │ │ │ │ ├── fancybox-x.png │ │ │ │ ├── fancybox-y.png │ │ │ │ └── fancybox.png │ │ │ │ ├── favicon_green.png │ │ │ │ ├── favicon_red.png │ │ │ │ ├── favicon_yellow.png │ │ │ │ ├── loading.gif │ │ │ │ ├── magnify.png │ │ │ │ └── smoothness │ │ │ │ └── images │ │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ │ │ ├── ui-bg_highlight-soft_75_cccccc_1x100.png │ │ │ │ ├── ui-icons_222222_256x240.png │ │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ │ ├── ui-icons_454545_256x240.png │ │ │ │ ├── ui-icons_888888_256x240.png │ │ │ │ └── ui-icons_cd0a0a_256x240.png │ │ └── index.html │ ├── environment_spec.rb │ ├── global_role_spec.rb │ ├── organization_spec.rb │ ├── system_configuration_spec.rb │ ├── term_spec.rb │ └── user_spec.rb └── spec_helper.rb └── vendor └── assets ├── javascripts ├── .gitkeep ├── cowboy │ └── jquery-hashchange │ │ └── jquery.ba-hashchange.min.js ├── eternicode │ └── bootstrap-datepicker │ │ └── bootstrap-datepicker.js ├── jherdman │ └── javascript-relative-time-helpers │ │ └── date.extensions.js ├── jquery │ └── jqueryui │ │ └── jquery-ui-1.10.3.custom.min.js ├── morr │ └── jquery-appear │ │ └── jquery.appear.js ├── silviomoreto │ └── bootstrap-select │ │ └── bootstrap-select.min.js ├── smalot │ └── bootstrap-datetimepicker │ │ └── bootstrap-datetimepicker.js └── twitter │ └── bootstrap │ └── bootstrap.js └── stylesheets ├── .gitkeep ├── eternicode └── bootstrap-datepicker │ └── datepicker.css ├── jquery └── jqueryui │ └── jquery-ui-1.10.3.custom.min.css ├── silviomoreto └── bootstrap-select │ └── bootstrap-select.min.css ├── smalot └── bootstrap-datetimepicker │ └── datetimepicker.css └── twitter └── bootstrap ├── accordion.less ├── alerts.less ├── bootstrap.less ├── breadcrumbs.less ├── button-groups.less ├── buttons.less ├── carousel.less ├── close.less ├── code.less ├── component-animations.less ├── dropdowns.less ├── forms.less ├── grid.less ├── hero-unit.less ├── labels-badges.less ├── layouts.less ├── media.less ├── mixins.less ├── modals.less ├── navbar.less ├── navs.less ├── pager.less ├── pagination.less ├── popovers.less ├── progress-bars.less ├── reset.less ├── responsive-1200px-min.less ├── responsive-767px-max.less ├── responsive-768px-979px.less ├── responsive-navbar.less ├── responsive-utilities.less ├── responsive.less ├── scaffolding.less ├── sprites.less ├── tables.less ├── thumbnails.less ├── tooltip.less ├── type.less ├── utilities.less ├── variables.less └── wells.less /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | *.sassc 3 | .sass-cache 4 | capybara-*.html 5 | .rspec 6 | /.bundle 7 | /vendor/bundle 8 | /log/* 9 | /tmp/* 10 | /db/*.sqlite3 11 | /public/assets/* 12 | /public/system/* 13 | /spec/tmp/* 14 | **.orig 15 | rerun.txt 16 | pickle-email-*.html 17 | 18 | *.rdb 19 | /public/m 20 | /public/uploads/tmp 21 | /storage/* 22 | /work/* 23 | 24 | # Ignore the entity relationship diagram 25 | erd.pdf 26 | 27 | # Ignore the database configuration 28 | /config/database.yml 29 | 30 | # Ignore all vim backup and swap files 31 | *~ 32 | *.swp 33 | 34 | # Ignore test coverage reports 35 | /coverage/ 36 | 37 | # Ignore node modules 38 | node_modules 39 | 40 | *.pyc 41 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | pythy 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | com.aptana.ide.core.unifiedBuilder 15 | 16 | 17 | 18 | 19 | 20 | org.radrails.rails.core.railsnature 21 | com.aptana.ruby.core.rubynature 22 | org.python.pydev.pythonNature 23 | 24 | 25 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Default 6 | python 2.7 7 | 8 | -------------------------------------------------------------------------------- /Capfile: -------------------------------------------------------------------------------- 1 | load 'deploy' if respond_to?(:namespace) # cap2 differentiator 2 | 3 | # Uncomment if you are using Rails' asset pipeline 4 | load 'deploy/assets' 5 | 6 | Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) } 7 | 8 | load 'config/deploy' # remove this line to skip loading any of the default tasks 9 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Pythy::Application.load_tasks 8 | -------------------------------------------------------------------------------- /app/assets/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/bg.png -------------------------------------------------------------------------------- /app/assets/images/curl-blue-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/curl-blue-small.png -------------------------------------------------------------------------------- /app/assets/images/curl-gray-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/curl-gray-small.png -------------------------------------------------------------------------------- /app/assets/images/curl-green-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/curl-green-small.png -------------------------------------------------------------------------------- /app/assets/images/curl-red-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/curl-red-small.png -------------------------------------------------------------------------------- /app/assets/images/curl-yellow-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/curl-yellow-small.png -------------------------------------------------------------------------------- /app/assets/images/curl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/curl.png -------------------------------------------------------------------------------- /app/assets/images/landing-header-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/landing-header-bg.png -------------------------------------------------------------------------------- /app/assets/images/navbar-separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/navbar-separator.png -------------------------------------------------------------------------------- /app/assets/images/snake-cap-300h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/images/snake-cap-300h.png -------------------------------------------------------------------------------- /app/assets/javascripts/activity_logs.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | 5 | $ -> 6 | $('#reset-filter').click (e) -> 7 | $form = $(this).closest('form') 8 | $form[0].reset() 9 | $form.submit() 10 | e.preventDefault() 11 | -------------------------------------------------------------------------------- /app/assets/javascripts/assignment_offerings.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/assignment_repositories.js.coffee: -------------------------------------------------------------------------------- 1 | google.load 'visualization', '1.0', packages: ['corechart'], callback: -> 2 | 3 | $.get(window.location.href + '.json').always (response) -> 4 | rows = ([new Date(Date.parse(row[0])), row[1]] for row in response) 5 | 6 | data = new google.visualization.DataTable 7 | data.addColumn 'date', 'Time' 8 | data.addColumn 'number', 'Score' 9 | #data.addColumn type: 'string', role: 'tooltip', p: { html: true } 10 | data.addRows rows 11 | 12 | formatter = new google.visualization.DateFormat(pattern: 'MM/dd/yyyy hh:mm aa') 13 | formatter.format data, 0 14 | 15 | options = { 16 | #width: 770, 17 | #height: 350, 18 | #tooltip: { isHtml: true }, 19 | #hAxis: { title: 'Time' }, 20 | vAxis: { minValue: 0 }, 21 | legend: { position: 'none' }, 22 | chartArea: { left: '0%', top: '0%', width: '100%', height: '88%' } 23 | } 24 | 25 | chart = new google.visualization.AreaChart($('#commit-time-chart')[0]) 26 | chart.draw data, options 27 | -------------------------------------------------------------------------------- /app/assets/javascripts/authentications.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/course_offerings.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/courses.js.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/javascripts/courses.js.coffee -------------------------------------------------------------------------------- /app/assets/javascripts/environments.js.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | # ------------------------------------------------------------- 3 | # Create CodeMirror widgets on the editing page. 4 | if $('#environment_preamble').length 5 | CodeMirror.fromTextArea $('#environment_preamble')[0], 6 | mode: { name: "python", version: 3, singleLineStringErrors: false }, 7 | lineNumbers: true, 8 | gutters: ["CodeMirror-linenumbers"] 9 | indentUnit: 2, 10 | tabSize: 2, 11 | indentWithTabs: false, 12 | matchBrackets: true, 13 | extraKeys: { Tab: (cm) -> cm.replaceSelection(' ', 'end') } 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/errors.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/example_repositories.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/landing.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/organizations.js.coffee: -------------------------------------------------------------------------------- 1 | $ -> 2 | updateHelpText = -> 3 | val = $('#organization_abbreviation').val() 4 | if val 5 | part = pythy.url_part_safe(val) 6 | $('#url_part').text("This organization will appear in URLs like .../#{part}/...").show() 7 | else 8 | $('#url_part').hide() 9 | 10 | $('#organization_abbreviation').keyup -> updateHelpText() 11 | $('#organization_abbreviation').change -> updateHelpText() 12 | 13 | updateHelpText() 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/setup.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/system_configurations.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/terms.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/upload_roster.js.coffee: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/assets/javascripts/upload_roster.js.coffee -------------------------------------------------------------------------------- /app/assets/stylesheets/activity_logs.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the UserLogs controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | tbody#activity_logs td { 6 | font-size: 12px; 7 | line-height: 14px; 8 | } 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require codemirror 13 | *= require eternicode/bootstrap-datepicker/datepicker.css 14 | //= require smalot/bootstrap-datetimepicker/datetimepicker 15 | *= require silviomoreto/bootstrap-select/bootstrap-select.min 16 | *= require bootstrap_and_overrides 17 | *= require jquery/jqueryui/jquery-ui-1.10.3.custom.min 18 | *= require font-awesome 19 | *= require jquery.fileupload-ui 20 | *= require global 21 | */ 22 | -------------------------------------------------------------------------------- /app/assets/stylesheets/assignment_offerings.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the AssignmentOfferings controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/assignment_repositories.css.scss: -------------------------------------------------------------------------------- 1 | .chart { 2 | width: 100%; 3 | height: 300px; 4 | } 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/assignments.css.scss: -------------------------------------------------------------------------------- 1 | #assignment_brief_summary_control_group .CodeMirror { 2 | height: 120px; 3 | } 4 | 5 | #assignment_description_control_group .CodeMirror { 6 | height: 300px; 7 | } 8 | 9 | .chart.grade-histogram { 10 | width: 100%; 11 | height: 200px; 12 | } 13 | 14 | table.grade-summary { 15 | td:first-child { width: 67%; } 16 | td:last-child { width: 33% }; 17 | } 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/authentications.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Authentications controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/course_offerings.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the CourseOfferings controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/courses.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Courses controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/environments.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Environments controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/errors.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the errors controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/example_repositories.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the examples controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/landing.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the landing controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/organizations.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Organizations controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/setup.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the setup controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/system_configurations.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the system_configurations controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/terms.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Terms controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/controllers/assignment_checks_controller.rb: -------------------------------------------------------------------------------- 1 | class AssignmentChecksController < ApplicationController 2 | 3 | before_filter :authenticate_user! 4 | load_and_authorize_resource :assignment_repository 5 | load_and_authorize_resource :assignment_check, 6 | through: :assignment_repository, shallow: true 7 | 8 | 9 | # ------------------------------------------------------------- 10 | def index 11 | # FIXME Why do I have to do this? The l_a_a_r above should take 12 | # care of it. 13 | @assignment_checks = @assignment_repository.assignment_checks 14 | 15 | @scores = [] 16 | @check_ids = [] 17 | @assignment_checks.each do |check| 18 | @scores << [check.number.to_i, check.overall_score.to_f] 19 | @check_ids << check.id 20 | end 21 | 22 | respond_to do |format| 23 | format.js 24 | end 25 | end 26 | 27 | 28 | # ------------------------------------------------------------- 29 | def show 30 | respond_to do |format| 31 | format.js 32 | end 33 | end 34 | 35 | end 36 | -------------------------------------------------------------------------------- /app/controllers/assignment_repositories_controller.rb: -------------------------------------------------------------------------------- 1 | class AssignmentRepositoriesController < ApplicationController 2 | 3 | before_filter :authenticate_user! 4 | load_and_authorize_resource :assignment_repository 5 | 6 | 7 | # ------------------------------------------------------------- 8 | def show 9 | #@assignment_checks = @assignment_repository.assignment_checks 10 | @assignment_offering = @assignment_repository.assignment_offering 11 | @assignment = @assignment_offering.assignment 12 | 13 | respond_to do |format| 14 | format.html 15 | format.json do 16 | # TODO Handle assignments that are open forever, or never open 17 | if @assignment_offering.opens_at && @assignment_offering.effectively_due_at 18 | collector = CommitCollector.new(@assignment_repository, 19 | @assignment_offering.opens_at, @assignment_offering.effectively_due_at, 1) 20 | buckets = collector.commit_time_data 21 | end 22 | 23 | render json: buckets 24 | end 25 | end 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/errors_controller.rb: -------------------------------------------------------------------------------- 1 | class ErrorsController < ApplicationController 2 | 3 | layout 'error' 4 | 5 | 6 | # ------------------------------------------------------------- 7 | def error_404 8 | @not_found_path = params[:not_found] 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/landing_controller.rb: -------------------------------------------------------------------------------- 1 | class LandingController < ApplicationController 2 | 3 | layout 'landing' 4 | 5 | def index 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/proxy_controller.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | 3 | class ProxyController < ApplicationController 4 | 5 | # ------------------------------------------------------------- 6 | def get 7 | url = URI.parse(params[:url]) 8 | hash = params[:hash] 9 | 10 | # TODO verify hash 11 | 12 | result = get_response_with_redirect(url) 13 | send_data result.body, 14 | type: result.content_type || 'text/plain', 15 | disposition: 'inline', 16 | status: result.code 17 | end 18 | 19 | 20 | private 21 | 22 | # ------------------------------------------------------------- 23 | def get_response_with_redirect(uri) 24 | r = Net::HTTP.get_response(uri) 25 | 26 | if r.code == "301" 27 | r = Net::HTTP.get_response(URI.parse(r.header['location'])) 28 | end 29 | 30 | r 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /app/controllers/scratchpad_repositories_controller.rb: -------------------------------------------------------------------------------- 1 | class ScratchpadRepositoriesController < ApplicationController 2 | 3 | before_filter :authenticate_user! 4 | load_and_authorize_resource :scratchpad_repository 5 | 6 | 7 | # ------------------------------------------------------------- 8 | def update 9 | respond_to do |format| 10 | if @scratchpad_repository.update_attributes(params[:scratchpad_repository]) 11 | format.html { redirect_to @scratchpad_repository, notice: 'Scratchpad repository was successfully updated.' } 12 | format.json { render json: @scratchpad_repository.as_json(include: [:environment]) } 13 | else 14 | format.html { render action: 'edit' } 15 | format.json { render json: @scratchpad_repository.errors, status: :unprocessable_entity } 16 | end 17 | end 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < Devise::SessionsController 2 | 3 | layout 'landing' 4 | 5 | before_filter :log_event, only: [:create, :destroy] 6 | 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/setup_controller.rb: -------------------------------------------------------------------------------- 1 | class SetupController < ApplicationController 2 | 3 | layout 'setup' 4 | 5 | # ------------------------------------------------------------- 6 | # GET /setup 7 | def index 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /app/controllers/typeaheads_controller.rb: -------------------------------------------------------------------------------- 1 | class TypeaheadsController < ApplicationController 2 | 3 | # ------------------------------------------------------------- 4 | def user 5 | @search = params[:search] 6 | @users = User.search(@search) 7 | 8 | respond_to do |format| 9 | format.json { render json: @users.as_json(only: [:first_name, :last_name, :email, :id]) } 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /app/helpers/activity_logs_helper.rb: -------------------------------------------------------------------------------- 1 | module ActivityLogsHelper 2 | 3 | # ------------------------------------------------------------- 4 | def filter_form 5 | form_tag({ controller: 'activity_logs', action: 'filter' }, 6 | remote: true, class: 'form-search well well-small') do 7 | yield 8 | end 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/helpers/assignment_offerings_helper.rb: -------------------------------------------------------------------------------- 1 | module AssignmentOfferingsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/assignments_helper.rb: -------------------------------------------------------------------------------- 1 | module AssignmentsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/authentications_helper.rb: -------------------------------------------------------------------------------- 1 | module AuthenticationsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/course_enrollments_helper.rb: -------------------------------------------------------------------------------- 1 | module CourseEnrollmentsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/course_offerings_helper.rb: -------------------------------------------------------------------------------- 1 | module CourseOfferingsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/courses_helper.rb: -------------------------------------------------------------------------------- 1 | module CoursesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/devise_helper.rb: -------------------------------------------------------------------------------- 1 | module DeviseHelper 2 | 3 | # ------------------------------------------------------------- 4 | def devise_error_messages! 5 | return "" if resource.nil? || resource.errors.empty? 6 | 7 | messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join 8 | sentence = I18n.t("errors.messages.not_saved", 9 | :count => resource.errors.count, 10 | :resource => resource_name) 11 | 12 | html = <<-HTML 13 |
14 |

#{sentence}

15 | 16 |
17 | HTML 18 | 19 | html.html_safe 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /app/helpers/environments_helper.rb: -------------------------------------------------------------------------------- 1 | module EnvironmentsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/errors_helper.rb: -------------------------------------------------------------------------------- 1 | module ErrorsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/example_repositories_helper.rb: -------------------------------------------------------------------------------- 1 | module ExampleRepositoriesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/global_roles_helper.rb: -------------------------------------------------------------------------------- 1 | module GlobalRolesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/landing_helper.rb: -------------------------------------------------------------------------------- 1 | module LandingHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/media_helper.rb: -------------------------------------------------------------------------------- 1 | module MediaHelper 2 | 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/organizations_helper.rb: -------------------------------------------------------------------------------- 1 | module OrganizationsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/setup_helper.rb: -------------------------------------------------------------------------------- 1 | module SetupHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/system_configurations_helper.rb: -------------------------------------------------------------------------------- 1 | module SystemConfigurationsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/terms_helper.rb: -------------------------------------------------------------------------------- 1 | module TermsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | 3 | # ------------------------------------------------------------- 4 | def full_name_or_none(user, options={}) 5 | if !user.first_name.blank? && !user.last_name.blank? 6 | options[:query] ? query_highlight(user.first_name + ' ' + user.last_name) : user.first_name + ' ' + user.last_name 7 | elsif options[:muted] 8 | raw(content_tag :span, '(none given)', class: 'muted') 9 | else 10 | '(none given)' 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/authentication.rb: -------------------------------------------------------------------------------- 1 | class Authentication < ActiveRecord::Base 2 | 3 | belongs_to :user 4 | 5 | attr_accessible :provider, :uid 6 | 7 | def apply_omniauth(user, omniauth) 8 | if user.email.blank? 9 | user.email = omniauth['info']['email'] 10 | end 11 | 12 | user.first_name ||= omniauth['info']['first_name'] 13 | user.last_name ||= omniauth['info']['last_name'] 14 | 15 | # Generate a dummy password for the user. 16 | user.password ||= Devise.friendly_token.first(20) 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /app/models/check_outcome.rb: -------------------------------------------------------------------------------- 1 | class CheckOutcome < ActiveRecord::Base 2 | 3 | belongs_to :assignment_check 4 | 5 | attr_accessible :name, :description, :category, :position, :score, 6 | :possible_score, :detail 7 | 8 | serialize :detail, Hash 9 | 10 | 11 | # ------------------------------------------------------------- 12 | def percentage_score 13 | 100 * score / possible_score 14 | end 15 | 16 | 17 | # ------------------------------------------------------------- 18 | def reason 19 | # Putting a $ at the end of the custom hint message for the 20 | # assertion will indicate that the rest of the message should 21 | # not be shown to the student. 22 | r = detail['reason'] 23 | if r && r =~ /(.*)\$:.*/ 24 | $1 25 | else 26 | r 27 | end 28 | end 29 | 30 | 31 | # ------------------------------------------------------------- 32 | def hint? 33 | reason && reason =~ /Hint:/i 34 | end 35 | 36 | 37 | # ------------------------------------------------------------- 38 | def hint 39 | if reason && reason =~ /Hint:\s*(.*)$/i 40 | $1 41 | end 42 | end 43 | 44 | end -------------------------------------------------------------------------------- /app/models/course_enrollment.rb: -------------------------------------------------------------------------------- 1 | class CourseEnrollment < ActiveRecord::Base 2 | 3 | belongs_to :user 4 | belongs_to :course_offering 5 | belongs_to :course_role 6 | 7 | paginates_per 100 8 | 9 | attr_accessible :user, :course_offering, :course_role, :user_id, 10 | :course_offering_id, :course_role_id 11 | 12 | end 13 | -------------------------------------------------------------------------------- /app/models/environment.rb: -------------------------------------------------------------------------------- 1 | class Environment < ActiveRecord::Base 2 | 3 | has_many :courses, foreign_key: 'default_environment_id' 4 | has_many :repositories 5 | 6 | attr_accessible :name, :preamble 7 | 8 | validates :name, presence: true 9 | 10 | end 11 | -------------------------------------------------------------------------------- /app/models/personal_repository.rb: -------------------------------------------------------------------------------- 1 | class PersonalRepository < Repository 2 | 3 | end 4 | -------------------------------------------------------------------------------- /app/models/resource_key_methods.rb: -------------------------------------------------------------------------------- 1 | require 'securerandom' 2 | 3 | module ResourceKeyMethods 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | before_save :generate_resource_key 8 | end 9 | 10 | 11 | # ------------------------------------------------------- 12 | def generate_resource_key 13 | unless self.resource_key 14 | key = nil 15 | 16 | loop do 17 | key = SecureRandom.urlsafe_base64(10) 18 | break unless self.class.where(resource_key: key).exists? 19 | end 20 | 21 | self.resource_key = key 22 | end 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /app/models/system_configuration.rb: -------------------------------------------------------------------------------- 1 | class SystemConfiguration < ActiveRecord::Base 2 | attr_accessible :storage_path, :work_path 3 | 4 | with_options presence: true, path: true do |sys_config| 5 | sys_config.validates :storage_path 6 | sys_config.validates :work_path 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /app/validators/changeable_validator.rb: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # Found at http://gist.github.com/3149393, modified for Pythy use 3 | class ChangeableValidator < ActiveModel::EachValidator 4 | # Enforce/prevent attribute change 5 | # 6 | # Example: Make attribute immutable once saved 7 | # validates :attribute, changeable: false, on: :update 8 | # 9 | # Example: Force attribute change on every save 10 | # validates :attribute, changeable: true 11 | 12 | def initialize(options) 13 | options[:changeable] = !(options[:with] === false) 14 | super 15 | end 16 | 17 | def validate_each(record, attribute, value) 18 | unless record.public_send(:"#{attribute}_changed?") == options[:changeable] 19 | record.errors.add(attribute, 20 | "#{options[:changeable] ? 'must' : 'cannot'} be modified") 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/validators/path_validator.rb: -------------------------------------------------------------------------------- 1 | # Validates that the path exists and is writable by the server. 2 | class PathValidator < ActiveModel::EachValidator 3 | def validate_each(record, attribute, value) 4 | dir = find_writable_path(value) 5 | 6 | if not File.exists?(dir) 7 | record.errors.add(attribute, "#{value} must exist.") 8 | end 9 | 10 | if not File.stat(dir).writable? 11 | record.errors.add(attribute, "#{value} must be writable by the server.") 12 | end 13 | 14 | end 15 | 16 | private 17 | # Checks each path in the hierarchy to see if the path can be 18 | # written to by the user who started the server 19 | def find_writable_path(dir_path) 20 | paths = File.split(dir_path) 21 | 22 | while(!File.exists?(dir_path) || 23 | !File.stat(dir_path).writable? && 24 | !paths[0].eql?(paths[1])) do 25 | dir_path = paths[0] 26 | paths = File.split(dir_path) 27 | end 28 | 29 | return dir_path 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/views/activity_logs/_activity_log.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td= link_to activity_log.user.email, activity_log.user 3 | %td= activity_log.action 4 | %td= l activity_log.created_at 5 | %td= activity_log.info -------------------------------------------------------------------------------- /app/views/activity_logs/_table.html.haml: -------------------------------------------------------------------------------- 1 | %table.table.table-striped.table-bordered.table-condensed 2 | %thead 3 | %tr 4 | %th.span3 User 5 | %th.span2 Action 6 | %th.span2 Time 7 | %th.span5 Extra Info 8 | %tbody#activity_logs 9 | = render @activity_logs 10 | 11 | #paginator 12 | = paginate @activity_logs, remote: true 13 | -------------------------------------------------------------------------------- /app/views/activity_logs/filter.js.coffee: -------------------------------------------------------------------------------- 1 | $('#log-entries').html('<%= escape_javascript(render partial: "table") %>') 2 | -------------------------------------------------------------------------------- /app/views/activity_logs/index.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = filter_form do 3 | %span.help-inline Filter: 4 | = typeahead_field_tag :email, nil, placeholder: 'E-mail', url: users_path 5 | = typeahead_field_tag :action_name, nil, placeholder: 'Action', url: activity_logs_path, submit: true 6 | = link_to 'Clear', '#', id: 'reset-filter', class: 'btn' 7 | = submit_tag 'Filter', class: 'btn btn-primary' 8 | 9 | #log-entries 10 | = render partial: 'table' 11 | -------------------------------------------------------------------------------- /app/views/activity_logs/index.js.coffee: -------------------------------------------------------------------------------- 1 | $('#activity_logs').html('<%= escape_javascript render(@activity_logs) %>') 2 | $('#paginator').html('<%= escape_javascript(paginate(@activity_logs, remote: true).to_s) %>') 3 | -------------------------------------------------------------------------------- /app/views/application/_flashbar.html.haml: -------------------------------------------------------------------------------- 1 | #flashbar 2 | - [:notice, :alert, :error].each do |level| 3 | - unless flash[level].blank? 4 | .flash{ class: flash_class(level) } 5 | .container 6 | .row 7 | = close_button_tag dismiss: 'flash' 8 | %span{ class: "tag-#{level.to_s}" } 9 | = flash[level] 10 | -------------------------------------------------------------------------------- /app/views/application/_form_errors.html.haml: -------------------------------------------------------------------------------- 1 | #form_errors 2 | - if model.errors.any? 3 | .alert.alert-block.alert-danger 4 | %p 5 | %strong 6 | The 7 | = model.class.name.humanize 8 | could not be saved due to the following errors: 9 | 10 | %ul 11 | - model.errors.full_messages.each do |msg| 12 | %li= msg 13 | -------------------------------------------------------------------------------- /app/views/application/_index_search.html.haml: -------------------------------------------------------------------------------- 1 | = form_tag url_for(action: 'index'), remote: true, method: :get, class: 'form-search breadcrumb-hang', autocomplete: 'off' do 2 | .input-append 3 | = search_field_tag :query, nil, class: 'input-medium input-small search-query', placeholder: 'Search...' 4 | = button_tag type: 'submit', class: 'btn' do 5 | = icon_tag('search') 6 | -------------------------------------------------------------------------------- /app/views/application/modal_form_errors.js.coffee: -------------------------------------------------------------------------------- 1 | $('#<%= controller_name %>_modal #flashbar-placeholder').html( 2 | '<%= escape_javascript(render partial: "form_errors", locals: { model: model }) %>') -------------------------------------------------------------------------------- /app/views/application/place_flash.js.coffee: -------------------------------------------------------------------------------- 1 | $('<%= where %>').html '<%= escape_javascript(render partial: "flashbar") %>' 2 | -------------------------------------------------------------------------------- /app/views/assignment_checks/_score_history_modal.html.haml: -------------------------------------------------------------------------------- 1 | #score_history_modal.modal.modal-wide.hide.fade 2 | .modal-header 3 | = close_button_tag dismiss: 'modal' 4 | %h3 5 | Score History for 6 | %strong= @assignment_repository.assignment_offering.assignment.short_name 7 | %small 8 | = surround '(', ')' do 9 | = @assignment_repository.assignment_offering.course_offering.course.number 10 | 11 | .modal-body 12 | #score-history-chart{ style: 'width: 770px; height: 350px;' } 13 | 14 | .modal-footer 15 | = button_tag 'Close', class: 'btn btn-primary', data: { dismiss: 'modal' } 16 | -------------------------------------------------------------------------------- /app/views/assignment_checks/show.js.coffee: -------------------------------------------------------------------------------- 1 | <% if @assignment_check.status == AssignmentCheck::COMPLETED %> 2 | # Append the modal to the page body. 3 | $('body').append( 4 | '<%= escape_javascript(render partial: "code/check_outcomes_modal", 5 | locals: { assignment_check: @assignment_check }) %>') 6 | 7 | # Ensure that the dialog gets removed from the DOM when it is closed. 8 | $('#check_outcomes_modal').on 'hidden', -> 9 | $('#check_outcomes_modal').remove() 10 | 11 | # Display the dialog. 12 | $('#check_outcomes_modal').modal('show') 13 | <% elsif @assignment_check.status == AssignmentCheck::FAILED %> 14 | pythy.alert 'An internal error occurred when executing your code.' 15 | <% elsif @assignment_check.status == AssignmentCheck::TIMEOUT %> 16 | pythy.alert 'Your code did not complete in the allowed amount of time.' 17 | <% end %> 18 | -------------------------------------------------------------------------------- /app/views/assignment_offerings/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @assignment_offering.course_offering, @assignment_offering do |f| 2 | 3 | = f.fields_for :assignment do |assignment| 4 | = assignment.text_field :short_name, class: 'input-large' do 5 | .help-block A brief name for the assignment, such as "Project 1". 6 | = assignment.text_field :long_name, class: 'input-xxlarge' do 7 | .help-block 8 | A more descriptive name for the assignment. The short name and long name will be combined as 9 | %strong "Short Name: Long Name" 10 | in many displays. 11 | = assignment.text_area :description, rows: 10, class: 'input-xxlarge' do 12 | .help-block 13 | The full write-up or specification of the assignment. This description will be processed as 14 | = link_to 'Markdown-formatted text', 'http://daringfireball.net/projects/markdown/syntax', target: '_blank' 15 | to display to students. 16 | 17 | = f.actions do 18 | = f.submit 19 | -------------------------------------------------------------------------------- /app/views/assignment_offerings/create.js.coffee: -------------------------------------------------------------------------------- 1 | $('#assignment_offerings').html( 2 | '<%= j render(@course_offering.assignment_offerings) %>') 3 | 4 | $('#assignment_offerings_modal').modal('hide') 5 | -------------------------------------------------------------------------------- /app/views/assignment_offerings/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/assignment_offerings/new.js.coffee: -------------------------------------------------------------------------------- 1 | # Append the modal to the page body. 2 | $('body').append('<%= j(render partial: "modal") %>') 3 | 4 | # Ensure that the dialog gets removed from the DOM when it is closed. 5 | $('#assignment_offerings_modal').on 'hidden', -> 6 | $('#assignment_offerings_modal').remove() 7 | 8 | # Display the dialog. 9 | $('#assignment_offerings_modal').modal('show') 10 | -------------------------------------------------------------------------------- /app/views/assignment_offerings/show.json.rabl: -------------------------------------------------------------------------------- 1 | object @assignment_offering 2 | 3 | attributes :id, :opens_at, :closes_at, :due_at 4 | node(:number_of_students) { |ao| ao.course_offering.students.count } 5 | node(:number_started) { |ao| ao.assignment_repositories_without_staff.count } 6 | 7 | child :assignment_repositories_without_staff, object_root: false do 8 | attributes :id 9 | node(:score) do |repo| 10 | check = repo.assignment_checks.most_recent 11 | check && check.overall_score || 0 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/views/assignment_repositories/show.html.haml: -------------------------------------------------------------------------------- 1 | %h2 2 | = @assignment.short_name 3 | %small 4 | = surround '(', ')' do 5 | = @assignment.long_name 6 | %h4 7 | %small 8 | = @assignment_repository.user.display_name 9 | 10 | .row 11 | .span6 12 | %h3 Activity Over Time 13 | #commit-time-chart.chart 14 | .span6 15 | -------------------------------------------------------------------------------- /app/views/assignments/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/assignments/index.html.erb: -------------------------------------------------------------------------------- 1 |

Listing assignments

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <% @assignments.each do |assignment| %> 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <% end %> 23 |
CreatorShort nameLong name
<%= assignment.creator_id %><%= assignment.short_name %><%= assignment.long_name %><%= link_to 'Show', assignment %><%= link_to 'Edit', edit_assignment_path(assignment) %><%= link_to 'Destroy', assignment, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
24 | 25 |
26 | 27 | <%= link_to 'New Assignment', new_assignment_path %> 28 | -------------------------------------------------------------------------------- /app/views/assignments/new.html.erb: -------------------------------------------------------------------------------- 1 |

New assignment

2 | 3 | <%= render 'form' %> 4 | 5 | <%= link_to 'Back', assignments_path %> 6 | -------------------------------------------------------------------------------- /app/views/assignments/template_picker.html.haml: -------------------------------------------------------------------------------- 1 | %table.table.table-bordered.table-striped.table-condensed 2 | %thead 3 | %tr 4 | %th.span4 Short Name 5 | %th.span4 Full Name 6 | %th.span3 Term 7 | %th.span1 8 | 9 | %tbody 10 | - if @assignments.empty? 11 | %td{ colspan: 4 } 12 | .alert There are no past assignments that you can access in this course. 13 | - else 14 | - @assignments.each do |assignment| 15 | %tr 16 | %td= assignment.short_name 17 | %td= assignment.long_name 18 | %td= assignment.term.display_name 19 | %td= link_to "Select", simple_course_path('pick_template', @course, @term, assignment.id.to_s) 20 | 21 | - unless @assignments.empty? 22 | #assignments_paginator 23 | = paginate @assignments 24 | -------------------------------------------------------------------------------- /app/views/authentications/index.html.haml: -------------------------------------------------------------------------------- 1 | - if @authentications 2 | - unless @authentications.empty? 3 | %p 4 | %strong You can sign in to this account using: 5 | .authentications 6 | - @authentications.each do |authentication| 7 | .authentication 8 | = image_tag "#{authentication.provider}_32.png", :size => "32x32" 9 | .provider 10 | = authentication.provider.titleize 11 | .uid 12 | = authentication.uid 13 | = link_to "X", authentication, :confirm => 'Are you sure you want to remove this authentication option?', :method => :delete, :class => "remove" 14 | .clear 15 | %p 16 | %strong Add another service to sign in with: 17 | - else 18 | %p 19 | %strong 20 | Sign in through one of these services: 21 | 22 | %a{:href => "/auth/facebook", :class => "auth_provider"} 23 | = image_tag "facebook_64.png", :size => "64x64", :alt => "Facebook" 24 | Facebook 25 | 26 | .clear 27 | -------------------------------------------------------------------------------- /app/views/code/_check_outcomes_modal.html.haml: -------------------------------------------------------------------------------- 1 | #check_outcomes_modal.modal.hide.fade 2 | .modal-header 3 | = close_button_tag dismiss: 'modal' 4 | %h3 Assignment Check Results 5 | 6 | .modal-body 7 | .outcomes-header.clearfix 8 | .pull-left 9 | The results of your 10 | %strong= assignment_check.number.ordinalize 11 | check at 12 | %strong 13 | = succeed ':' do 14 | = l assignment_check.created_at 15 | = score_tile_label(assignment_check) 16 | 17 | %table.table.table-condensed.table-bordered.table-striped 18 | %thead 19 | %tr 20 | %th Test 21 | %th Result 22 | %tbody 23 | - assignment_check.check_outcomes.each do |outcome| 24 | %tr 25 | %td 26 | = outcome.description || outcome.name 27 | - if outcome.reason 28 | %p.hint 29 | = outcome.reason 30 | %td{ valign: 'top' }= outcome_bar outcome 31 | 32 | .modal-footer 33 | = button_tag 'Close', class: 'btn btn-primary', data: { dismiss: 'modal' } 34 | -------------------------------------------------------------------------------- /app/views/code/_check_results.html.haml: -------------------------------------------------------------------------------- 1 | %table.table.table-bare 2 | %tbody 3 | - repository.assignment_checks.most_recent(10).each do |check| 4 | %tr 5 | %td 6 | = link_to l(check.updated_at), code_check_results_link(check), method: :post, remote: true 7 | %td{ style: 'width: 50%' } 8 | - if check.status == AssignmentCheck::COMPLETED 9 | = score_bar check.overall_score 10 | - elsif check.status == AssignmentCheck::TIMEOUT 11 | %small.text-error Timed out 12 | - elsif check.status == AssignmentCheck::FAILED 13 | %small.text-error Internal error 14 | -------------------------------------------------------------------------------- /app/views/code/_choose_environment_modal.html.haml: -------------------------------------------------------------------------------- 1 | #choose_environment_modal.modal.hide{ data: { backdrop: 'static' } } 2 | .modal-header 3 | %h3 Choose Python Environment 4 | 5 | .modal-body.has-typeahead 6 | %p 7 | Select the Python environment that you want to use for this 8 | scratchpad. You can change it at any time later on if you wish. 9 | 10 | = select_tag :environment, options_from_collection_for_select(Environment.all, :id, :name, environment_id), prompt: '(none)', data: { width: '100%' } 11 | 12 | .modal-footer 13 | = button_tag 'Set Environment', id: 'set-environment', class: 'btn btn-primary', data: { dismiss: 'modal' } 14 | -------------------------------------------------------------------------------- /app/views/code/_commit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | - check = commit[:assignment_check] 3 | - if check 4 | %tr 5 | %td{colspan: 2} 6 | - if check.status == AssignmentCheck::COMPLETED 7 | = link_to code_check_results_link(check), method: :post, remote: true do 8 | = score_badge check.overall_score 9 | - elsif check.status == AssignmentCheck::TIMEOUT 10 | %small.text-error Timed out 11 | - elsif check.status == AssignmentCheck::FAILED 12 | %small.text-error Internal error 13 | 14 | %tr{ data: { date: commit[:date].to_i * 1000 } } 15 | %td 16 | %a{ href: "\##{commit[:sha]}" }= l commit[:date] 17 | %td 18 | = commit[:message] 19 | -------------------------------------------------------------------------------- /app/views/code/_file_list.html.haml: -------------------------------------------------------------------------------- 1 | %ul.unstyled 2 | - @repository.ls(path).each do |e| 3 | - unless File.directory?(File.join(@repository.git_path, path, e)) 4 | - relative_path = File.join(path, e) 5 | %li 6 | = icon_tag('file') 7 | - if relative_path == @filename 8 | %strong= e 9 | - else 10 | %a{ href: code_path(@repository, relative_path) }= e 11 | -------------------------------------------------------------------------------- /app/views/code/_picture_tool.haml: -------------------------------------------------------------------------------- 1 | #mediacomp-colorpicker.modal.hide#Sk-canvasModal 2 | .modal-header 3 | = close_button_tag dismiss: 'modal' 4 | %h3 5 | Picture Tool 6 | 7 | .modal-body 8 | 9 | .modal-footer 10 | %form.form-inline.pull-left 11 | = label_tag 'canvas-x', 'X:' 12 | %input.input-mini#canvas-x{type: 'number'} 13 | = label_tag 'canvas-y', 'Y:' 14 | %input.input-mini#canvas-y{type: 'number'} 15 | %table.pull-right 16 | %tbody 17 | %tr 18 | %td.canvas-color-label R: 19 | %td#canvas-red 0 20 | %td.canvas-color-label G: 21 | %td#canvas-green 0 22 | %td.canvas-color-label B: 23 | %td#canvas-blue 0 24 | %td#canvas-color-swatch 25 | -------------------------------------------------------------------------------- /app/views/code/_user_list.html.haml: -------------------------------------------------------------------------------- 1 | - users.each do |user| 2 | %li{ class: user_list_item_class(@repository, user) }>< 3 | = gravatar_tag(user, 30, rel: 'tooltip', title: user_list_item_title(@repository, user)) 4 | -------------------------------------------------------------------------------- /app/views/code/add_history.js.coffee: -------------------------------------------------------------------------------- 1 | <% if commits.length > 0 %> 2 | $('#history-table tbody').append '<%= j render partial: "commit", collection: commits %>' 3 | <% else %> 4 | $('#history-table .next-page').hide() 5 | <% end %> 6 | window.codeController.nextHistoryPageLoaded() 7 | -------------------------------------------------------------------------------- /app/views/code/check.js.coffee: -------------------------------------------------------------------------------- 1 | <% if @closed && !@is_instructor %> 2 | pythy.alert 'You cannot perform anymore checks because the 3 | assignment has closed.', title: 'Assignment Closed' 4 | <% elsif !@was_changed && !@is_instructor %> 5 | $('#check').button('reset') 6 | pythy.alert 'Your code hasn\'t changed since your last check, 7 | so it will not be rechecked. Please look at your previous 8 | results.', title: 'Code Unchanged' 9 | <% elsif !@was_changed && !@last_check %> 10 | pythy.alert 'There is nothing to check! Is anything saved?', title: 'Nothing to check' 11 | <% end %> 12 | -------------------------------------------------------------------------------- /app/views/code/check_results.js.coffee: -------------------------------------------------------------------------------- 1 | $('#check').button('reset') 2 | 3 | <% if assignment_check.status == AssignmentCheck::COMPLETED %> 4 | # Append the modal to the page body. 5 | $('body').append( 6 | '<%= j render partial: "code/check_outcomes_modal", locals: { assignment_check: assignment_check } %>') 7 | 8 | # Ensure that the dialog gets removed from the DOM when it is closed. 9 | $('#check_outcomes_modal').on 'hidden', -> 10 | $('#check_outcomes_modal').remove() 11 | 12 | # Display the dialog. 13 | $('#check_outcomes_modal').modal('show') 14 | 15 | <% elsif assignment_check.status == AssignmentCheck::FAILED %> 16 | pythy.alert 'An internal error occurred when executing your code.' 17 | 18 | <% elsif assignment_check.status == AssignmentCheck::TIMEOUT %> 19 | pythy.alert 'Your code did not complete in the allowed amount of time.' 20 | 21 | <% end %> 22 | -------------------------------------------------------------------------------- /app/views/code/related_files/_assignment_reference_repository.html.haml: -------------------------------------------------------------------------------- 1 | 2 | .section-header 3 | Starter Files 4 | .container-fluid 5 | = render partial: 'file_list', locals: { path: 'starter' } 6 | 7 | .section-header 8 | Reference Tests 9 | .container-fluid 10 | = render partial: 'file_list', locals: { path: 'test' } 11 | 12 | .section-header 13 | Solution 14 | .container-fluid 15 | = render partial: 'file_list', locals: { path: 'solution' } 16 | 17 | .section-header 18 | Student View 19 | .container-fluid 20 | %ul.unstyled 21 | - @repository.assignment.assignment_offerings.each do |ao| 22 | %li 23 | = ao.course_offering.short_label + ": " + ao.course_offering.long_label 24 | %li 25 | = icon_tag('file') 26 | = link_to "main.py", code_path(ao) 27 | %br -------------------------------------------------------------------------------- /app/views/code/summary/_assignment_reference_repository.html.haml: -------------------------------------------------------------------------------- 1 | .section-header.big 2 | = succeed ": " do 3 | = @repository.assignment.short_name 4 | = @repository.assignment.long_name 5 | %span.label.label-important Reference repository 6 | 7 | .container-fluid 8 | This example was created by 9 | = @repository.user.display_name 10 | at 11 | = succeed '.' do 12 | %strong= l @repository.created_at 13 | -------------------------------------------------------------------------------- /app/views/code/summary/_assignment_repository.html.haml: -------------------------------------------------------------------------------- 1 | .section-header.big 2 | = succeed ": " do 3 | = @repository.assignment_offering.assignment.short_name 4 | = @repository.assignment_offering.assignment.long_name 5 | %small 6 | - if @repository.user != current_user 7 | %span.label.label-warning= @repository.user.display_name 8 | 9 | .container-fluid 10 | .row-fluid 11 | = raw @repository.assignment_offering.assignment.brief_summary_html 12 | 13 | .section-header 14 | Important Dates 15 | 16 | .container-fluid 17 | .row-fluid 18 | - if @repository.assignment_offering.due_at 19 | This assignment is due at 20 | = succeed '.' do 21 | = live_date_tag @repository.assignment_offering.due_at 22 | - if @repository.assignment_offering.closes_at 23 | This assignment may not be checked after 24 | = succeed '.' do 25 | = l @repository.assignment_offering.closes_at 26 | -------------------------------------------------------------------------------- /app/views/code/summary/_example_repository.html.haml: -------------------------------------------------------------------------------- 1 | .section-header.big 2 | Example: 3 | = @repository.name 4 | %small 5 | = surround '(', ')' do 6 | = @repository.course_offering.course.number 7 | 8 | .container-fluid 9 | .row-fluid 10 | This example was created by 11 | = @repository.user.display_name 12 | at 13 | = succeed '.' do 14 | %strong= l @repository.created_at 15 | 16 | - if can? :manage, @repository 17 | .section-header 18 | Users watching: 19 | 20 | .container-fluid 21 | %ul#accessing-users.gravatar-list 22 | -------------------------------------------------------------------------------- /app/views/code/summary/_scratchpad_repository.html.haml: -------------------------------------------------------------------------------- 1 | .section-header.big 2 | Scratchpad 3 | 4 | .container-fluid 5 | You can use this coding area to do “scratch work” that 6 | isn't part of any of your courses or assignments. The code that you 7 | write here is automatically saved. 8 | 9 | 10 | /= link_to icon_tag('code-fork') + ' Save as personal repository...', 'javascript:void(0)', id: 'save-as-personal', class: 'btn btn-block' 11 | -------------------------------------------------------------------------------- /app/views/code/update.js.coffee: -------------------------------------------------------------------------------- 1 | <% if @committed %> 2 | new_history_row = '<%= j render partial: "commit", locals: { commit: @commit_hash } %>' 3 | window.codeController.updateHistory(new_history_row, <%= @committed[:amend] %>) 4 | $('#save-state-icon').html('') 5 | $('#save-state-message').html('saved') 6 | <% end %> 7 | <% if @repository.is_a?(AssignmentRepository) && !@repository.assignment_offering.closed? %> 8 | $('#check').removeAttr('disabled') 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/code/update_code.js.coffee: -------------------------------------------------------------------------------- 1 | <% if local_assigns[:environment] %> 2 | window.codeController.setPreamble '<%= j raw environment.preamble || "" %>' 3 | <% end %> 4 | <% if local_assigns[:commit] %> 5 | new_history_row = '<%= j render partial: "commit", locals: { commit: commit } %>' 6 | amend = <%= amend %> 7 | <% else %> 8 | new_history_row = null 9 | amend = false 10 | <% end %> 11 | window.codeController.updateCode '<%= j raw code %>', <%= force %>, new_history_row, amend, <%= initial %> 12 | -------------------------------------------------------------------------------- /app/views/code/update_user_list.js.coffee: -------------------------------------------------------------------------------- 1 | $('#summary ul#accessing-users').html( 2 | "<%= j render partial: 'user_list', locals: { users: users } %>") 3 | #$('#accessing-user-count').text "(<%= users.count %>)" 4 | $('[rel=tooltip]').tooltip container: 'body' 5 | -------------------------------------------------------------------------------- /app/views/course_enrollments/_course_enrollment.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td.has_gravatar 3 | = gravatar_tag(course_enrollment.user, 28) 4 | = link_to course_enrollment.user.display_name, course_enrollment.user 5 | %td= course_enrollment.user.email 6 | %td= course_enrollment.course_role.name 7 | %td= link_to_destroy course_enrollment, remote: true 8 | -------------------------------------------------------------------------------- /app/views/course_enrollments/_modal.html.haml: -------------------------------------------------------------------------------- 1 | #course_enrollment_modal.modal.hide.fade 2 | = pythy_form_for @course_enrollment.course_offering, @course_enrollment, 3 | remote: true, html: { class: 'modal-form' } do |f| 4 | .modal-header 5 | = close_button_tag dismiss: 'modal' 6 | %h3 Add User Manually 7 | 8 | .modal-body.has-typeahead 9 | #flashbar-placeholder 10 | = f.label :user_id, 'User' do 11 | = f.hidden_field :user_id 12 | = text_field_tag :user_name, nil, class: 'input-xlarge typeahead-user', autocomplete: 'off' 13 | 14 | = f.collection_select( :course_role_id, 'Role', CourseRole.all, :id, :name, {:selected => CourseRole::STUDENT_ID}) 15 | = hidden_field_tag :type, @type 16 | 17 | .modal-footer 18 | = f.submit 19 | -------------------------------------------------------------------------------- /app/views/course_enrollments/_table.html.haml: -------------------------------------------------------------------------------- 1 | %table.table.table-bordered.table-striped.table-condensed 2 | %thead 3 | %tr 4 | %th.span4 Full Name 5 | %th.span4 E-mail 6 | %th.span3 Role 7 | %th.span1= icon_tag('cog') 8 | %tbody#course_enrollments 9 | - if enrollments.empty? 10 | %td{ colspan: 4 } 11 | .alert There are no users currently enrolled in this course offering. 12 | - else 13 | = render enrollments 14 | 15 | - unless enrollments.empty? 16 | #course_enrollments_paginator 17 | = paginate enrollments, :param_name => "enrollments_" + enrollments.first.course_offering_id.to_s + "_page" 18 | -------------------------------------------------------------------------------- /app/views/course_enrollments/create.js.coffee: -------------------------------------------------------------------------------- 1 | $('#course_enrollments').html( 2 | '<%= escape_javascript render(@course_offering.course_enrollments) %>') 3 | 4 | $('#course_enrollment_modal').modal('hide') 5 | -------------------------------------------------------------------------------- /app/views/course_enrollments/destroy.js.coffee: -------------------------------------------------------------------------------- 1 | $('#course_enrollments').html( 2 | '<%= escape_javascript render( 3 | @course_enrollment.course_offering.course_enrollments.page(params[:page])) %>') 4 | -------------------------------------------------------------------------------- /app/views/course_enrollments/index.js.coffee: -------------------------------------------------------------------------------- 1 | $('#course_enrollments').html( 2 | '<%= escape_javascript render(@course_enrollments) %>') 3 | $('#course_enrollments_paginator').html( 4 | '<%= escape_javascript(paginate(@course_enrollments, remote: true).to_s) %>') 5 | -------------------------------------------------------------------------------- /app/views/course_enrollments/new.js.coffee: -------------------------------------------------------------------------------- 1 | # Append the course enrollments dialog to the page body. 2 | $('body').append( 3 | '<%= escape_javascript(render partial: "course_enrollments/modal") %>') 4 | 5 | window.pythy.typeaheadUser() 6 | 7 | # Ensure that the dialog gets removed from the DOM when it is closed. 8 | $('#course_enrollment_modal').on 'hidden', -> 9 | $('#course_enrollment_modal').remove() 10 | 11 | $('#course_enrollment_modal').on 'shown', -> 12 | $('#course_enrollment_user_id').focus() 13 | 14 | $('#course_enrollment_modal #enroll-users-button').button('loading') 15 | 16 | # Display the dialog. 17 | $('#course_enrollment_modal').modal('show') 18 | -------------------------------------------------------------------------------- /app/views/course_offerings/_index.html.haml: -------------------------------------------------------------------------------- 1 | - if course_offerings.empty? 2 | %div.alert 3 | This course does not have any offerings. 4 | = link_to new_course_course_offering_path(@course) do 5 | Click here to create one. 6 | - else 7 | - course_offerings.each do |term, offerings| 8 | %h3 9 | = term.display_name 10 | Offerings 11 | %table.table.table-striped.table-bordered 12 | %thead 13 | %tr 14 | %th.span3 CRN 15 | %th.span7 Label 16 | %th.span1= icon_tag('cog') 17 | %tbody 18 | - offerings.each do |course_offering| 19 | %tr 20 | %td= link_to course_offering.short_label, course_offering 21 | %td= course_offering.long_label 22 | %td 23 | = link_to icon_tag('pencil'), edit_course_offering_path(course_offering) 24 | = link_to_destroy course_offering 25 | -------------------------------------------------------------------------------- /app/views/course_offerings/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/course_offerings/index.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('plus') + ' New...', new_course_course_offering_path(@course), class: 'btn' 3 | 4 | = render partial: 'course_offerings/index', locals: { course_offerings: @grouped_offerings } 5 | -------------------------------------------------------------------------------- /app/views/course_offerings/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/course_offerings/show.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('pencil') + ' Edit', edit_course_offering_path(@course_offering), class: 'btn' 3 | 4 | %h2.page-header 5 | = @course_offering.course.number 6 | = surround '(', ')' do 7 | = @course_offering.short_label 8 | %small 9 | - if @course_offering.long_label.blank? 10 | = @course_offering.term.display_name 11 | - else 12 | = succeed ',' do 13 | = @course_offering.term.display_name 14 | = @course_offering.long_label 15 | 16 | .clearfix 17 | %h3.pull-left Course Staff and Enrolled Students 18 | .pull-right.dropdown 19 | = dropdown_button_tag 'Add Users', class: 'btn btn-primary' 20 | %ul.dropdown-menu 21 | %li 22 | = link_to 'Manually...', new_course_offering_course_enrollment_path(@course_offering), remote: true 23 | %li 24 | = link_to 'Upload a roster...', upload_roster_path(@course_offering), remote: true 25 | 26 | #staff-enrollments-table 27 | = render partial: 'course_enrollments/table', locals: { enrollments: @course_enrollments } 28 | -------------------------------------------------------------------------------- /app/views/course_offerings/show.js.coffee: -------------------------------------------------------------------------------- 1 | $('#course_enrollments').html( 2 | '<%= escape_javascript render(@course_enrollments) %>') 3 | $('#course_enrollments_paginator').html( 4 | '<%= escape_javascript(paginate(@course_enrollments, remote: true).to_s) %>') 5 | -------------------------------------------------------------------------------- /app/views/course_roles/_form.html.haml: -------------------------------------------------------------------------------- 1 | = twitter_bootstrap_form_for @course_role, html: { class: 'form-horizontal' } do |f| 2 | = f.text_field :name, 'Name' 3 | 4 | = f.label 'Permissions' do |controls| 5 | = controls.check_box :can_manage_course, 'Modify course', disabled: @course_role.builtin? 6 | = controls.check_box :can_manage_assignments, 'Create and edit assignments', disabled: @course_role.builtin? 7 | = controls.check_box :can_grade_submissions, 'Grade other students\' work', disabled: @course_role.builtin? 8 | = controls.check_box :can_view_other_submissions, 'View other students\' work', disabled: @course_role.builtin? 9 | - if @course_role.builtin? 10 | %span.help-block 11 | Permissions cannot be changed for built-in roles. 12 | 13 | = f.actions do 14 | = f.submit 15 | -------------------------------------------------------------------------------- /app/views/course_roles/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/course_roles/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/course_roles/show.html.haml: -------------------------------------------------------------------------------- 1 | = render :partial => "flashbar" 2 | = link_to 'Edit', edit_course_role_path(@course_role) 3 | = link_to 'Back', course_roles_path -------------------------------------------------------------------------------- /app/views/courses/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @course.organization, @course do |f| 2 | 3 | = f.collection_select :organization_id, 'Organization', Organization.all, :id, :display_name, prompt: '(none)' 4 | 5 | = f.text_field :number do 6 | %span.help-block 7 | The course number prefixed by its departmental abbreviation (for example, "CS 110"). There are no restrictions on the format of this string so different types of organizations can use it differently (for example, "Sandbox" or "Playground" might be appropriate). 8 | 9 | = f.text_field :name, class: 'span6' do 10 | %span.help-block 11 | The full course title (for example, "Introduction to Python"). 12 | 13 | = f.collection_select :default_environment_id, 'Default environment', Environment.all, :id, :name, prompt: '(none)' 14 | 15 | = f.actions do 16 | = f.submit 17 | -------------------------------------------------------------------------------- /app/views/courses/_index.html.haml: -------------------------------------------------------------------------------- 1 | - if courses.empty? 2 | %div.alert 3 | This organization does not have any courses. 4 | = link_to new_organization_course_path(@organization) do 5 | Click here to create one. 6 | - else 7 | %table.table.table-striped.table-bordered 8 | %thead 9 | %tr 10 | %th.span3 Course Number 11 | %th.span8 Title 12 | %th.span1= icon_tag('cog') 13 | %tbody 14 | - courses.each do |course| 15 | %tr 16 | %td= link_to course.number, course 17 | %td= course.name 18 | %td 19 | = link_to icon_tag('pencil'), edit_course_path(course) 20 | = link_to_destroy course 21 | -------------------------------------------------------------------------------- /app/views/courses/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/courses/index.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('plus') + ' New...', new_organization_course_path(@organization), class: 'btn' 3 | 4 | = render partial: 'courses/index', locals: { courses: @courses } 5 | -------------------------------------------------------------------------------- /app/views/courses/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/courses/show.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('pencil') + ' Edit', edit_course_path(@course), class: 'btn' 3 | 4 | %h2.page-header 5 | = @course.number 6 | %small 7 | = @course.name 8 | 9 | %p 10 | = link_to 'Create a new offering of this course...', new_course_course_offering_path(@course) 11 | 12 | = render partial: 'course_offerings/index', locals: { course_offerings: @grouped_offerings } 13 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Resend confirmation instructions" %>
10 | <% end %> 11 | 12 | <%= render "links" %> -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @resource.email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>

6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

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

4 | 5 |

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

6 | 7 |

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

8 |

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

9 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

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

8 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
<%= f.label :password, "New password" %>
8 | <%= f.password_field :password %>
9 | 10 |
<%= f.label :password_confirmation, "Confirm new password" %>
11 | <%= f.password_field :password_confirmation %>
12 | 13 |
<%= f.submit "Change my password" %>
14 | <% end %> 15 | 16 | <%= render "links" %> -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

Forgot your password?

2 | 3 | <%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Send me reset password instructions" %>
10 | <% end %> 11 | 12 | <%= render "links" %> 13 | -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.label :password %> (leave blank if you don't want to change it)
10 | <%= f.password_field :password, :autocomplete => "off" %>
11 | 12 |
<%= f.label :password_confirmation %>
13 | <%= f.password_field :password_confirmation %>
14 | 15 |
<%= f.label :current_password %> (we need your current password to confirm your changes)
16 | <%= f.password_field :current_password %>
17 | 18 |
<%= f.submit "Update" %>
19 | <% end %> 20 | 21 |

Cancel my account

22 | 23 |

Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.

24 | 25 | <%= link_to "Back", :back %> 26 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Sign up 2 | 3 | = twitter_bootstrap_form_for resource, :as => resource_name, :url => registration_path(resource_name) do |f| 4 | = devise_error_messages! 5 | 6 | = f.email_field :email 7 | = f.password_field :password 8 | = f.password_field :password_confirmation 9 | = f.text_field :first_name 10 | = f.text_field :last_name 11 | 12 | = f.actions do 13 | = f.submit 'Sign up' 14 | 15 | = render 'links' 16 | -------------------------------------------------------------------------------- /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 |
<%= f.label :email %>
7 | <%= f.email_field :email %>
8 | 9 |
<%= f.submit "Resend unlock instructions" %>
10 | <% end %> 11 | 12 | <%= render "links" %> -------------------------------------------------------------------------------- /app/views/environments/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @environment do |f| 2 | 3 | = f.text_field :name do 4 | %span.help-block 5 | The human-readable name of the environment. 6 | 7 | = f.text_area :preamble, rows: 5, class: 'input-xxlarge' do 8 | .help-block 9 | Any code required to execute before student code (for example, 10 | additional import statements). 11 | 12 | = f.actions do 13 | = f.submit 14 | -------------------------------------------------------------------------------- /app/views/environments/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/environments/index.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('plus') + ' New...', new_environment_path, class: 'btn' 3 | 4 | %table.table.table-striped.table-bordered 5 | %thead 6 | %tr 7 | %th Name 8 | %th= icon_tag('cog') 9 | %tbody 10 | - @environments.each do |environment| 11 | %tr 12 | %td= link_to environment.name, environment 13 | %td 14 | = link_to icon_tag('pencil'), edit_environment_path(environment) 15 | = link_to_destroy environment 16 | -------------------------------------------------------------------------------- /app/views/environments/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/environments/show.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('pencil') + ' Edit', edit_environment_path(@environment), class: 'btn' 3 | 4 | %h2 5 | = @environment.name 6 | 7 | %h3 Preamble 8 | %p 9 | The Python statements below are inserted at the top of any code executed 10 | by Pythy. 11 | %pre= @environment.preamble 12 | -------------------------------------------------------------------------------- /app/views/errors/_modal.html.haml: -------------------------------------------------------------------------------- 1 | #error_modal.modal.modal-wide.hide 2 | .modal-body 3 | = render template: "errors/error_#{status}" 4 | .modal-footer 5 | = button_tag 'Close', class: 'btn btn-primary', data: { dismiss: 'modal' } 6 | -------------------------------------------------------------------------------- /app/views/errors/error_403.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Are You Supposed to Be Here? 2 | %div 3 | %p 4 | Unfortunately, you've ended up on a page that you are not 5 | authorized to view. If you think this 6 | is in error, please let the developers know! 7 | 8 | %p Sorry! 9 | 10 | = link_to 'Home', root_url, class: 'btn btn-primary' 11 | -------------------------------------------------------------------------------- /app/views/errors/error_404.html.haml: -------------------------------------------------------------------------------- 1 | - bc = ActiveSupport::BacktraceCleaner.new 2 | - bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } 3 | - bc.add_silencer { |line| line =~ /\/gems\// } 4 | - backtrace = $! && bc.clean($!.backtrace) 5 | 6 | %h2 Resource Not Found 7 | %div 8 | %p 9 | The resource that you requested could not be found. 10 | 11 | - if current_user && current_user.global_role.can_edit_system_configuration 12 | %hr 13 | - if $! 14 | %h4 Exception 15 | %pre.alert.alert-danger 16 | = $!.message 17 | %h4 Backtrace 18 | %pre{ style: 'overflow: auto; white-space: nowrap' } 19 | - backtrace.each do |frame| 20 | = frame 21 | %br 22 | -------------------------------------------------------------------------------- /app/views/errors/error_500.html.haml: -------------------------------------------------------------------------------- 1 | - bc = ActiveSupport::BacktraceCleaner.new 2 | - bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } 3 | - bc.add_silencer { |line| line =~ /\/gems\// } 4 | - backtrace = bc.clean($!.backtrace) 5 | 6 | %h2 We've Got a Problem ... 7 | %p 8 | Unfortunately, something went wrong. 9 | There are a lot of possible reasons why this might have happened, 10 | but we've logged the problem so the developers can look at it. 11 | 12 | %p Sorry! 13 | 14 | = link_to 'Home', root_url, class: 'btn btn-primary' 15 | 16 | - if current_user && current_user.global_role.can_edit_system_configuration 17 | %hr 18 | %h4 Exception 19 | %pre.alert.alert-danger 20 | = $!.message 21 | %h4 Backtrace 22 | %pre{ style: 'overflow: auto; white-space: nowrap' } 23 | - backtrace.each do |frame| 24 | = frame 25 | %br 26 | -------------------------------------------------------------------------------- /app/views/errors/error_modal.js.coffee: -------------------------------------------------------------------------------- 1 | # Append the course enrollments dialog to the page body. 2 | $('body').append( 3 | '<%= escape_javascript(render partial: "errors/modal", locals: { status: status }) %>') 4 | 5 | # Ensure that the dialog gets removed from the DOM when it is closed. 6 | $('#error_modal').on 'hidden', -> 7 | $('#error_modal').remove() 8 | 9 | # Display the dialog. 10 | $('#error_modal').modal('show') 11 | -------------------------------------------------------------------------------- /app/views/example_repositories/_empty.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td{ colspan: 2 } 3 | %div.alert.alert-info 4 | The course instructor hasn't made any examples available yet. 5 | -------------------------------------------------------------------------------- /app/views/example_repositories/_example_repository.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td= link_to example_repository.name, code_path(example_repository) 3 | %td 4 | = l example_repository.created_at, format: :date_only 5 |   6 | - if can?(:destroy, example_repository) 7 | = link_to_destroy example_repository 8 | -------------------------------------------------------------------------------- /app/views/example_repositories/_modal.html.haml: -------------------------------------------------------------------------------- 1 | #example_repositories_modal.modal.hide.fade 2 | = pythy_form_for @example_repository.course_offering, @example_repository, 3 | remote: true, html: { class: 'modal-form' } do |f| 4 | .modal-header 5 | = close_button_tag dismiss: 'modal' 6 | %h3 New Example 7 | 8 | .modal-body 9 | #flashbar-placeholder 10 | 11 | = f.text_field :name, class: 'input-xlarge' 12 | = f.text_area :description, rows: 5, class: 'input-xlarge' 13 | 14 | .control-group 15 | .controls 16 | %label.checkbox 17 | = check_box_tag :add_to_all_offerings 18 | Share across all offerings in the term 19 | 20 | .modal-footer 21 | = f.submit 22 | -------------------------------------------------------------------------------- /app/views/example_repositories/create.js.coffee: -------------------------------------------------------------------------------- 1 | $('#example_repositories').html( 2 | '<%= j render(@course_offering.example_repositories.sort! { |a, b| b.created_at <=> a.created_at }) %>') 3 | 4 | $('#example_repositories_modal').modal('hide') 5 | -------------------------------------------------------------------------------- /app/views/example_repositories/destroy.js.coffee: -------------------------------------------------------------------------------- 1 |

Examples#destroy

2 |

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

3 | -------------------------------------------------------------------------------- /app/views/example_repositories/index.js.coffee: -------------------------------------------------------------------------------- 1 | $('#example_repositories').html( 2 | '<%= escape_javascript render(@example_repositories) %>') 3 | $('#example_repositories_paginator').html( 4 | '<%= escape_javascript(paginate(@example_repositories, remote: true).to_s) %>') 5 | -------------------------------------------------------------------------------- /app/views/example_repositories/new.js.coffee: -------------------------------------------------------------------------------- 1 | # Append the modal to the page body. 2 | $('body').append( 3 | '<%= escape_javascript(render partial: "modal") %>') 4 | 5 | # Ensure that the dialog gets removed from the DOM when it is closed. 6 | $('#example_repositories_modal').on 'hidden', -> 7 | $('#example_repositories_modal').remove() 8 | 9 | # Display the dialog. 10 | $('#example_repositories_modal').modal('show') 11 | -------------------------------------------------------------------------------- /app/views/global_roles/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @global_role do |f| 2 | 3 | = f.text_field :name, 'Name' 4 | 5 | = f.label 'Permissions' do |controls| 6 | = controls.check_box :can_create_courses, 'Create courses', disabled: @global_role.builtin? 7 | = controls.check_box :can_manage_all_courses, 'Manage all courses', disabled: @global_role.builtin? 8 | = controls.check_box :can_edit_system_configuration, 'Edit system configuration', disabled: @global_role.builtin? 9 | - if @global_role.builtin? 10 | %span.help-block 11 | Permissions cannot be changed for built-in roles. 12 | 13 | = f.actions do 14 | = f.submit 15 | -------------------------------------------------------------------------------- /app/views/global_roles/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/global_roles/index.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('plus') + ' New...', new_global_role_path, class: 'btn' 3 | 4 | %h2 Global Roles 5 | %p 6 | Global roles are used to control the permissions that users have 7 | system-wide. 8 | 9 | %table.table.table-striped.table-bordered 10 | %thead 11 | %tr 12 | %th.span5 Name 13 | %th.span2 Can edit system configuration? 14 | %th.span2 Can manage all courses? 15 | %th.span2 Can create courses? 16 | %th.span1= icon_tag('cog') 17 | %tbody 18 | - @global_roles.each do |role| 19 | %tr 20 | %td 21 | = link_to role.name, role 22 | - if role.builtin? 23 | %span.muted (built-in) 24 | %td= checkmark_if role.can_edit_system_configuration? 25 | %td= checkmark_if role.can_manage_all_courses? 26 | %td= checkmark_if role.can_create_courses? 27 | %td 28 | = link_to icon_tag('pencil'), edit_global_role_path(role) 29 | - unless role.builtin? 30 | = link_to_destroy role 31 | -------------------------------------------------------------------------------- /app/views/global_roles/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/global_roles/show.html.haml: -------------------------------------------------------------------------------- 1 | = render :partial => "flashbar" 2 | = link_to 'Edit', edit_global_role_path(@global_role) 3 | = link_to 'Back', global_roles_path -------------------------------------------------------------------------------- /app/views/home/_course_staff_assignments.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .row 3 | .span7 4 | .btn-header-container.clearfix 5 | %h3.pull-left.slim.btn-line-height Assignments 6 | - if @offerings.any? { |o| o.role_for_user(current_user).can_manage_assignments? } 7 | .pull-right 8 | = link_to icon_tag('plus') + ' New...', new_course_offering_assignment_offering_path(@offerings.first), class: 'btn', remote: true 9 | 10 | #assignments 11 | = render(partial: 'home/instructor_assignment', collection: @assignments) || render(partial: 'home/no_assignments') 12 | 13 | .span5 14 | .btn-header-container.clearfix 15 | %h3.pull-left.slim.btn-line-height Instructor Examples 16 | - if @offerings.any? { |o| o.role_for_user(current_user).can_manage_assignments? } 17 | .pull-right 18 | = link_to icon_tag('plus') + ' New...', new_course_offering_example_repository_path(@offerings.first), class: 'btn', remote: true 19 | 20 | %table.examples 21 | %tbody#example_repositories 22 | = render(@examples) || render(partial: 'example_repositories/empty') 23 | -------------------------------------------------------------------------------- /app/views/home/_course_staff_grades.html.haml: -------------------------------------------------------------------------------- 1 | 2 | .container 3 | - @course_scores.each do |scores| 4 | %h4 5 | = scores.course_offering.full_label 6 | 7 | %table#grades-table.table.table-condensed.table-striped{'style' => 'border-collapse: inherit;'} 8 | %thead 9 | %tr 10 | %th Name 11 | - scores.assignment_offerings.each do |offering| 12 | %th= offering.assignment.short_name 13 | 14 | %tbody 15 | - scores.users.each do |user| 16 | %tr 17 | %td.has_gravatar 18 | = gravatar_tag(user, 28) 19 | = user.display_name 20 | - scores.assignment_offerings.each do |offering| 21 | %td 22 | - info = scores.info_for_user_and_assignment_offering(user, offering) 23 | - if info && info[:score] 24 | = link_to percentage(info[:score]), "/code/#{info[:repository_id]}" 25 | - else 26 | – 27 | -------------------------------------------------------------------------------- /app/views/home/_course_staff_settings.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | = pythy_form_for @offerings.first.course.organization, @offerings.first.course do |f| 3 | 4 | = f.collection_select :organization_id, 'Organization', Organization.all, :id, :display_name, prompt: '(none)' 5 | 6 | = f.text_field :number do 7 | %span.help-block 8 | The course number prefixed by its departmental abbreviation (for example, "CS 110"). There are no restrictions on the format of this string so different types of organizations can use it differently (for example, "Sandbox" or "Playground" might be appropriate). 9 | 10 | = f.text_field :name, class: 'span6' do 11 | %span.help-block 12 | The full course title (for example, "Introduction to Python"). 13 | 14 | = f.collection_select :default_environment_id, 'Default environment', Environment.all, :id, :name, prompt: '(none)' 15 | 16 | = hidden_field_tag :term, @term.id.to_s 17 | 18 | = f.actions do 19 | = f.submit 20 | -------------------------------------------------------------------------------- /app/views/home/_course_staff_users.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | %h3 Course Staff and Enrolled Students 3 | 4 | - @offerings.each do |offering| 5 | .btn-header-container.clearfix 6 | %h4.slim.btn-line-height.pull-left 7 | = offering.full_label 8 | 9 | .pull-right.dropdown 10 | = dropdown_button_tag icon_tag('plus') + ' Add users', class: 'btn' 11 | %ul.dropdown-menu 12 | %li 13 | = link_to 'Manually...', new_course_offering_course_enrollment_path(offering), remote: true 14 | %li 15 | = link_to 'Upload a roster...', upload_roster_path(offering), remote: true 16 | 17 | #course-enrollments-table 18 | = render partial: 'course_enrollments/table', locals: { enrollments: Kaminari.paginate_array(offering.course_enrollments).page(params["enrollments_" + offering.id.to_s + "_page"]) } 19 | -------------------------------------------------------------------------------- /app/views/home/_instructor_assignment.html.haml: -------------------------------------------------------------------------------- 1 | .assignment-tile-wrapper 2 | .assignment-tile.front{ href: assignment_url(instructor_assignment) } 3 | .assignment-short-name><= instructor_assignment.short_name 4 | .assignment-long-name><= instructor_assignment.long_name 5 | .assignment-tile.back 6 | = link_to assignment_path(instructor_assignment, anchor: 'code') do 7 | = icon_tag('laptop') 8 | Code 9 | = link_to instructor_assignment do 10 | = icon_tag('list-ol') 11 | Description 12 | = link_to assignment_path(instructor_assignment, anchor: 'grades') do 13 | = icon_tag('tasks') 14 | Grades 15 | = link_to assignment_path(instructor_assignment, anchor: 'edit') do 16 | = icon_tag('pencil') 17 | Edit 18 | -------------------------------------------------------------------------------- /app/views/home/_no_assignments.html.haml: -------------------------------------------------------------------------------- 1 | %div.alert.alert-info 2 | The course instructor hasn't made any new assignments available yet. 3 | -------------------------------------------------------------------------------- /app/views/home/_no_started_assignments.html.haml: -------------------------------------------------------------------------------- 1 | %div.alert.alert-info 2 | You haven't started on any work yet in this course. As you work on 3 | assignments, they will be listed here with the most recent work shown 4 | at the top. 5 | -------------------------------------------------------------------------------- /app/views/home/_not_found.html.haml: -------------------------------------------------------------------------------- 1 | .row-fluid 2 | .span3.offset1 3 | = image_tag 'snake-cap-300h.png' 4 | .span7.alert.alert-danger 5 | The course you are looking for does not exist. 6 | -------------------------------------------------------------------------------- /app/views/home/_not_started_assignment.html.haml: -------------------------------------------------------------------------------- 1 | .assignment-tile-wrapper 2 | .assignment-tile.front{ href: assignment_url(not_started_assignment.assignment) } 3 | = assignment_date_tag(not_started_assignment) 4 | .assignment-short-name><= not_started_assignment.assignment.short_name 5 | .assignment-long-name><= not_started_assignment.assignment.long_name 6 | .assignment-tile.back 7 | = link_to code_path(not_started_assignment) do 8 | = icon_tag('laptop') 9 | Code 10 | = link_to not_started_assignment.assignment do 11 | = icon_tag('list-ol') 12 | Description 13 | -------------------------------------------------------------------------------- /app/views/home/_started_assignment.html.haml: -------------------------------------------------------------------------------- 1 | - repository = started_assignment.repository_for_user(current_user) 2 | - most_recent_check = repository.assignment_checks.most_recent 3 | .assignment-tile-wrapper.student 4 | .assignment-tile.front{ href: assignment_url(started_assignment.assignment) } 5 | = assignment_date_tag(started_assignment) 6 | .assignment-short-name><= started_assignment.assignment.short_name 7 | .assignment-long-name><= started_assignment.assignment.long_name 8 | .assignment-tile.back 9 | = link_to code_path(started_assignment) do 10 | = icon_tag('laptop') 11 | Code 12 | = link_to started_assignment.assignment do 13 | = icon_tag('list-ol') 14 | Description 15 | = score_tile(most_recent_check) -------------------------------------------------------------------------------- /app/views/home/course.js.coffee: -------------------------------------------------------------------------------- 1 | $('#course_enrollments').html( 2 | '<%= j render(@course_enrollments) %>') 3 | $('#course_enrollments_paginator').html( 4 | '<%= j paginate(@course_enrollments, remote: true).to_s %>') 5 | -------------------------------------------------------------------------------- /app/views/home/course_staff.html.haml: -------------------------------------------------------------------------------- 1 | .jumbo-title 2 | .container 3 | .row 4 | .span8 5 | %h1.slim 6 | = @course.number 7 | %p.lead 8 | = @course.name 9 | 10 | = render partial: 'flashbar' 11 | 12 | .container 13 | %ul.nav.nav-tabs 14 | = tab_tag 'assignments-tab', icon_tag('laptop') + ' Assignments', active: true 15 | = tab_tag 'users-tab', icon_tag('group') + ' Users' 16 | = tab_tag 'grades-tab', icon_tag('tasks') + ' Grades' 17 | - @offerings.each do |offering| 18 | - if can? :edit, offering 19 | = tab_tag 'settings-tab', icon_tag('cog') + ' Settings' 20 | - break 21 | 22 | .tab-content 23 | #assignments-tab.tab-pane.active 24 | = render partial: 'course_staff_assignments' 25 | #users-tab.tab-pane 26 | = render partial: 'course_staff_users' 27 | #grades-tab.tab-pane 28 | = render partial: 'course_staff_grades' 29 | #settings-tab.tab-pane 30 | = render partial: 'course_staff_settings' 31 | - @offerings.each do |offering| 32 | - if can? :edit, offering 33 | = render partial: 'course_staff_offering_settings', locals: { offering: offering } 34 | 35 | -------------------------------------------------------------------------------- /app/views/home/load_grades.js.coffee: -------------------------------------------------------------------------------- 1 | $('#grades-tab').html '<%= j render partial: "home/course_staff_grades" %>' 2 | -------------------------------------------------------------------------------- /app/views/home/refresh_examples.js.coffee: -------------------------------------------------------------------------------- 1 | $('#example_repositories').html( 2 | "<%= j render(@examples) || render(partial: 'example_repositories/empty') %>") 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_first_page.html.haml: -------------------------------------------------------------------------------- 1 | %li{ class: ('active' if current_page.first?) } 2 | - if current_page.first? 3 | %span= raw(t 'views.pagination.first') 4 | - else 5 | = link_to raw(t 'views.pagination.first'), url, remote: remote 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | %span= raw(t 'views.pagination.truncate') 3 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.haml: -------------------------------------------------------------------------------- 1 | %li{ class: ('active' if current_page.last?) } 2 | - if current_page.last? 3 | %span= raw(t 'views.pagination.last') 4 | - else 5 | = link_to raw(t 'views.pagination.last'), url, remote: remote 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.haml: -------------------------------------------------------------------------------- 1 | %li{ class: ('active' if current_page.last?) } 2 | - if current_page.last? 3 | %span= raw(t 'views.pagination.next') 4 | - else 5 | = link_to raw(t 'views.pagination.next'), url, rel: 'next', remote: remote 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_page.html.haml: -------------------------------------------------------------------------------- 1 | %li{ class: ('active' if page.current?) } 2 | - if page.current? 3 | %span= page 4 | - else 5 | = link_to page, url, remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil 6 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.haml: -------------------------------------------------------------------------------- 1 | = paginator.render do 2 | .pagination 3 | %ul 4 | = first_page_tag 5 | = prev_page_tag 6 | - each_page do |page| 7 | - if page.left_outer? || page.right_outer? || page.inside_window? 8 | = page_tag page 9 | - elsif !page.was_truncated? 10 | = gap_tag 11 | = next_page_tag 12 | = last_page_tag 13 | -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.haml: -------------------------------------------------------------------------------- 1 | %li{ class: ('active' if current_page.first?) } 2 | - if current_page.first? 3 | %span= raw(t 'views.pagination.previous') 4 | - else 5 | = link_to raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote 6 | -------------------------------------------------------------------------------- /app/views/landing/index.html.haml: -------------------------------------------------------------------------------- 1 | = render template: 'sessions/new' 2 | -------------------------------------------------------------------------------- /app/views/layouts/code.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title!= content_for?(:title) ? 'Pythy – ' + content_for(:title) : 'Pythy' 5 | %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' } 6 | = stylesheet_link_tag 'application', media: 'all' 7 | = controller_stylesheet_link_tag 8 | = javascript_include_tag 'application' 9 | = google_api_javascript_include_tag 10 | = juggernaut_javascript_include_tag 11 | = controller_javascript_include_tag 12 | = skulpt_javascript_include_tags 13 | = csrf_meta_tags 14 | = favicon_link_tag '/favicon.png', type: 'image/png' 15 | = body_tag do 16 | = render partial: 'navbar' 17 | = render partial: 'flashbar' 18 | = yield 19 | -------------------------------------------------------------------------------- /app/views/layouts/error.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title!= 'Pythy – Error' 5 | = stylesheet_link_tag 'application', media: 'all' 6 | = stylesheet_link_tag 'errors', media: 'all' 7 | = javascript_include_tag 'application' 8 | = javascript_include_tag 'errors' 9 | = csrf_meta_tags 10 | = favicon_link_tag '/favicon.png', type: 'image/png' 11 | %body 12 | = render partial: 'navbar' 13 | .container 14 | #main-container-inner 15 | .row 16 | .span10.offset1 17 | .well 18 | = yield 19 | -------------------------------------------------------------------------------- /app/views/layouts/landing.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title!= 'Welcome to Pythy' 5 | %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' } 6 | = stylesheet_link_tag 'application', media: 'all' 7 | = controller_stylesheet_link_tag 8 | = javascript_include_tag 'application' 9 | = controller_javascript_include_tag 10 | = csrf_meta_tags 11 | = favicon_link_tag '/favicon.png', type: 'image/png' 12 | %body.landing 13 | .jumbo-title 14 | .container 15 | %h1 16 | Pythy 17 | %small the online Python learning environment 18 | = render partial: 'flashbar' 19 | = yield 20 | -------------------------------------------------------------------------------- /app/views/layouts/logged_in.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title!= content_for?(:title) ? 'Pythy – ' + content_for(:title) : 'Pythy' 5 | %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' } 6 | = stylesheet_link_tag 'application', media: 'all' 7 | = controller_stylesheet_link_tag 8 | = javascript_include_tag 'application' 9 | = google_api_javascript_include_tag 10 | = juggernaut_javascript_include_tag 11 | = controller_javascript_include_tag 12 | = csrf_meta_tags 13 | = favicon_link_tag '/favicon.png', type: 'image/png' 14 | = body_tag do 15 | = render partial: 'navbar' 16 | = render partial: 'flashbar' 17 | .container 18 | = yield 19 | -------------------------------------------------------------------------------- /app/views/layouts/no_container.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title!= content_for?(:title) ? 'Pythy – ' + content_for(:title) : 'Pythy' 5 | %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' } 6 | = stylesheet_link_tag 'application', media: 'all' 7 | = controller_stylesheet_link_tag 8 | = javascript_include_tag 'application' 9 | = google_api_javascript_include_tag 10 | = juggernaut_javascript_include_tag 11 | = controller_javascript_include_tag 12 | = csrf_meta_tags 13 | = favicon_link_tag '/favicon.png', type: 'image/png' 14 | = body_tag do 15 | = render partial: 'navbar' 16 | = yield 17 | -------------------------------------------------------------------------------- /app/views/layouts/not_logged_in.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title!= 'Pythy – Sign In' 5 | = stylesheet_link_tag 'application', media: 'all' 6 | = controller_stylesheet_link_tag 7 | = javascript_include_tag 'application' 8 | = controller_javascript_include_tag 9 | = csrf_meta_tags 10 | = favicon_link_tag '/favicon.png', type: 'image/png' 11 | %body.landing 12 | = render partial: 'flashbar' 13 | .container 14 | = yield 15 | -------------------------------------------------------------------------------- /app/views/layouts/setup.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %title!= 'Pythy – Initial Setup' 5 | = stylesheet_link_tag 'application', media: 'all' 6 | = controller_stylesheet_link_tag 7 | = javascript_include_tag 'application' 8 | = controller_javascript_include_tag 9 | = csrf_meta_tags 10 | = favicon_link_tag '/favicon.png', type: 'image/png' 11 | %body.setup 12 | .container 13 | = yield 14 | -------------------------------------------------------------------------------- /app/views/media/_filter_item.html.haml: -------------------------------------------------------------------------------- 1 | %li 2 | %a{ href: '#', data: { type: type } } 3 | %i{ class: "fa fa-#{icon}" } 4 | = icon_tag('chevron-right') 5 | = title 6 | -------------------------------------------------------------------------------- /app/views/media/_index.html.haml: -------------------------------------------------------------------------------- 1 | #media-drop-zone 2 | .container-fluid 3 | .row-fluid 4 | .span3 5 | %ul.filter-list.nav.nav-list 6 | = render partial: 'filter_item', locals: { icon: 'cloud', title: 'All Media', type: '*/*' } 7 | = render partial: 'filter_item', locals: { icon: 'picture', title: 'Pictures', type: 'image/*' } 8 | = render partial: 'filter_item', locals: { icon: 'music', title: 'Sounds', type: 'audio/*' } 9 | = render partial: 'filter_item', locals: { icon: 'file-alt', title: 'Text', type: 'text/*' } 10 | %p.clearfix 11 | %span.btn.btn-block.btn-success.fileinput-button 12 | = icon_tag('plus') 13 | %span Add files... 14 | %input{ type: 'file', id: 'media-upload', name: 'files[]', multiple: 'multiple', data: { url: @upload_url } } 15 | #media-progress.progress.progress-striped 16 | .bar 17 | .span9 18 | #media-area.media-area 19 | -------------------------------------------------------------------------------- /app/views/media/_modal.html.haml: -------------------------------------------------------------------------------- 1 | #media_library_modal.modal.modal-wide.hide 2 | .modal-header 3 | = close_button_tag dismiss: 'modal' 4 | %h3 5 | Media Library 6 | 7 | .modal-body 8 | = render partial: 'index' 9 | 10 | .modal-footer 11 | = button_tag 'Close', class: 'btn btn-primary', data: { dismiss: 'modal' } 12 | -------------------------------------------------------------------------------- /app/views/media/destroy.js.coffee: -------------------------------------------------------------------------------- 1 | window.pythy.mediaLibrary.refreshList() 2 | -------------------------------------------------------------------------------- /app/views/media/index.html.haml: -------------------------------------------------------------------------------- 1 | = render partial: 'index' 2 | -------------------------------------------------------------------------------- /app/views/media/index.js.coffee: -------------------------------------------------------------------------------- 1 | # Append the modal to the page body. 2 | $('body').append '<%= j render partial: "media/modal" %>' 3 | 4 | # Ensure that the dialog gets removed from the DOM when it is closed. 5 | $('#media_library_modal').on 'hidden', -> 6 | $('#media_library_modal').remove() 7 | 8 | # Display the dialog. 9 | $('#media_library_modal').modal('show') 10 | -------------------------------------------------------------------------------- /app/views/organizations/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @organization do |f| 2 | 3 | = f.text_field :display_name do 4 | %span.help-block 5 | The full displayable name of the organization. 6 | 7 | = f.text_field :abbreviation do 8 | %span.help-block 9 | A short abbreviation that will be used in URLs. 10 | 11 | .controls 12 | .alert.alert-info#url_part 13 | 14 | = f.text_field :domain do 15 | %span.help-inline (Optional) 16 | %span.help-block 17 | The e-mail domain used by members of this organization. 18 | If present, it can be used to restrict courses in an 19 | organization only to users within that domain. 20 | 21 | = f.actions do 22 | = f.submit 23 | -------------------------------------------------------------------------------- /app/views/organizations/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/organizations/index.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('plus') + ' New...', new_organization_path, class: 'btn' 3 | 4 | %table.table.table-striped.table-bordered 5 | %thead 6 | %tr 7 | %th Display Name 8 | %th Domain 9 | %th= icon_tag('cog') 10 | %tbody 11 | - @organizations.each do |organization| 12 | %tr 13 | %td 14 | = link_to organization.display_name, organization 15 | = "(#{organization.abbreviation})" 16 | %td= organization.domain 17 | %td 18 | = link_to icon_tag('pencil'), edit_organization_path(organization) 19 | = link_to_destroy organization 20 | -------------------------------------------------------------------------------- /app/views/organizations/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/organizations/show.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('pencil') + ' Edit', edit_organization_path(@organization), class: 'btn' 3 | 4 | %h2 5 | = @organization.display_name 6 | %small= "(#{@organization.domain})" 7 | 8 | -# Table listing the courses belonging to the organization (and allowing 9 | -# them to be modified 10 | %h3 11 | = link_to 'Courses', organization_courses_path(@organization) 12 | 13 | = render partial: 'courses/index', locals: { courses: @organization.courses } 14 | .pull-right 15 | = link_to 'Create a new course...', new_organization_course_path(@organization), class: 'btn btn-success' 16 | -------------------------------------------------------------------------------- /app/views/registrations/_new.html.haml: -------------------------------------------------------------------------------- 1 | = twitter_bootstrap_form_for(resource, :as => resource_name, :url => registration_path(resource_name), html: { class: 'form-horizontal' }) do |f| 2 | 3 | = f.email_field :email, class: 'input-medium' 4 | = f.password_field :password, class: 'input-medium' 5 | = f.password_field :password_confirmation, class: 'input-medium' 6 | = f.text_field :first_name, class: 'input-medium' 7 | = f.text_field :last_name, class: 'input-medium' 8 | 9 | = f.actions do 10 | = f.submit 'Sign up' 11 | -------------------------------------------------------------------------------- /app/views/self_enrollment/_course_list.html.haml: -------------------------------------------------------------------------------- 1 | - if courses.any? 2 | - courses.each do |course, offerings| 3 | %h3.slim 4 | = course.number 5 | %small 6 | = course.name 7 | %table.table.table-condensed 8 | %tbody 9 | - offerings.each do |offering| 10 | %tr 11 | %td= link_to_if link_to_offerings, offering.short_label, home_path(offering) 12 | %td= offering.long_label 13 | %td.text-right 14 | - if offering.user_enrolled?(current_user) 15 | %span.muted 16 | - enrollment = offering.course_enrollments.where(user_id: current_user.id).first 17 | = enrollment.course_role.name 18 | - else 19 | = form_tag '/self_enroll/enroll', class: 'form-basic form-inline' do 20 | = hidden_field_tag :course_offering, offering.id 21 | = submit_tag 'Enroll', class: 'btn btn-mini' 22 | 23 | - else 24 | This organization does not have any courses that allow student 25 | self-enrollment at the moment. 26 | Please contact your instructor to be added to your course. -------------------------------------------------------------------------------- /app/views/self_enrollment/_select_organization.html.haml: -------------------------------------------------------------------------------- 1 | .well.well-small 2 | %p Which school or organization is hosting the course? 3 | - if organizations.any? 4 | = select_tag :organization, options_for_select(organizations), { id: 'self-enroll-organization', prompt: '(select an organization)', data: { width: '100%' } } 5 | - else 6 | %div.alert 7 | There are no registered organizations at the moment. 8 | - if can? :create, Organization 9 | = link_to new_organization_path do 10 | Add a new organization. 11 | 12 | #select-term 13 | 14 | #enroll-course-list 15 | -------------------------------------------------------------------------------- /app/views/self_enrollment/_select_term.html.haml: -------------------------------------------------------------------------------- 1 | %p What term is the course offered? 2 | - if terms.any? 3 | = select_tag :term, options_for_select(terms), { id: 'self-enroll-term', prompt: '(select a term)', data: { width: '100%' } } 4 | - else 5 | %div.alert 6 | There are no terms to choose from at the moment. 7 | - if can? :create, Term 8 | = link_to new_term_path do 9 | Add a new term. -------------------------------------------------------------------------------- /app/views/self_enrollment/select_organization.js.coffee: -------------------------------------------------------------------------------- 1 | <% if @organization %> 2 | $('#select-term').html( 3 | "<%= j render partial: 'self_enrollment/select_term', locals: { terms: @term_list } %>") 4 | $('select').selectpicker() 5 | $('#enroll-course-list').html('') 6 | <% else %> 7 | $('#select-term').html('') 8 | $('#enroll-course-list').html('') 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/self_enrollment/select_term.js.coffee: -------------------------------------------------------------------------------- 1 | <% if @courses %> 2 | $('#enroll-course-list').html( 3 | "<%= j render partial: 'self_enrollment/course_list', locals: { courses: @courses, link_to_offerings: false } %>") 4 | <% else %> 5 | $('#enroll-course-list').html('') 6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/sessions/_sign_in.html.haml: -------------------------------------------------------------------------------- 1 | = twitter_bootstrap_form_for(resource, as: resource_name, url: session_path(resource), html: { class: 'form-horizontal' }) do |f| 2 | %fieldset 3 | = f.email_field :email, class: 'input-medium' 4 | = f.password_field :password, class: 'input-medium' do 5 | %span.help-block 6 | Forgot your password? 7 | = link_to 'Click here to reset it.', new_password_path(resource_name) 8 | = f.label nil do |controls| 9 | = controls.check_box :remember_me, 'Remember me?' 10 | = f.actions do 11 | = f.submit "Sign In" 12 | -------------------------------------------------------------------------------- /app/views/sessions/new.html.haml: -------------------------------------------------------------------------------- 1 | .container 2 | .row 3 | .span6 4 | %h2.page-header Already have an account? 5 | = render partial: 'sessions/sign_in' 6 | .span6 7 | %h2.page-header New user? 8 | = render partial: 'registrations/new' 9 | -------------------------------------------------------------------------------- /app/views/sessions/new_initial.html.haml: -------------------------------------------------------------------------------- 1 | %h2 Welcome to Pythy! 2 | %p 3 | This is the first time the application is being started, 4 | so please log-in and your account will be used as the application's 5 | administrator user. 6 | 7 | = twitter_bootstrap_form_for resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => 'form-horizontal' } do |f| 8 | = devise_error_messages! 9 | 10 | = f.text_field :email 11 | = f.password_field :password 12 | 13 | = f.actions do 14 | = f.submit 'Sign in', :class => 'btn btn-primary' 15 | 16 | = link_to "Sign up", new_registration_path(resource_name) 17 | -------------------------------------------------------------------------------- /app/views/setup/index.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .span8.offset2 3 | .well 4 | %h2 Welcome to Pythy! 5 | %p 6 | This is the first time the application is being started, 7 | so please log-in and your account will be used as the application's 8 | administrator user. 9 | 10 | = twitter_bootstrap_form_for resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => 'form-horizontal' } do |f| 11 | = devise_error_messages! 12 | 13 | = f.email_field :email 14 | = f.password_field :password 15 | = f.password_field :password_confirmation 16 | = f.text_field :first_name 17 | = f.text_field :last_name 18 | 19 | = f.actions do 20 | = f.submit 'Sign in' 21 | -------------------------------------------------------------------------------- /app/views/system_configurations/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @system_configuration, url: system_configuration_path do |f| 2 | 3 | = f.text_field :storage_path, class: 'input-xxlarge' do 4 | .help-block 5 | The path on the server where user data files (such as repositories for 6 | saved code) are stored. 7 | %span.text-error 8 | Be careful if you change this after the system has already been in 9 | use. 10 | You will need to move the old storage location to the new one manually. 11 | 12 | = f.text_field :work_path, class: 'input-xxlarge' do 13 | .help-block 14 | The path on the server where user code is copied when it is executed 15 | to check it against the instructor's reference tests. Putting this in 16 | a separate, small partition can help prevent run-away code from 17 | adversely affecting the performance of the rest of the system. 18 | 19 | = f.actions do 20 | = f.submit 21 | -------------------------------------------------------------------------------- /app/views/system_configurations/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/system_configurations/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/system_configurations/show.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('pencil') + ' Edit', edit_system_configuration_path(@system_configuration), class: 'btn' 3 | 4 | %table.table.table-bordered.table-striped 5 | %tr 6 | %th Storage path 7 | %td 8 | %tt= @system_configuration.storage_path 9 | %tr 10 | %th Working area 11 | %td 12 | %tt= @system_configuration.work_path 13 | -------------------------------------------------------------------------------- /app/views/terms/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @term do |f| 2 | 3 | = f.label 'Season and Year' do |controls| 4 | = controls.select :season, Term::SEASONS, {}, class: 'span2' 5 | = controls.number_field :year, delimiter: '', class: 'span2' 6 | 7 | = f.date_field :starts_on, class: 'span2' 8 | 9 | = f.date_field :ends_on, class: 'span2' 10 | 11 | = f.actions do 12 | = f.submit 13 | -------------------------------------------------------------------------------- /app/views/terms/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/terms/index.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('plus') + ' New...', new_term_path, class: 'btn' 3 | 4 | - if @terms.empty? 5 | %div.alert 6 | You have not yet created any terms. 7 | = link_to new_term_path do 8 | Click here to create one. 9 | - else 10 | %table.table.table-striped.table-bordered 11 | %thead 12 | %tr 13 | %th.span6 Season and Year 14 | %th.span5 Time Period 15 | %th.span1= icon_tag('cog') 16 | %tbody 17 | - @terms.each do |term| 18 | %tr 19 | %td 20 | - if term.contains_now? 21 | %strong 22 | = link_to term.display_name, term 23 | (current) 24 | - else 25 | = link_to term.display_name, term 26 | %td 27 | = l term.starts_on 28 | to 29 | = l term.ends_on 30 | %td 31 | = link_to icon_tag('pencil'), edit_term_path(term) 32 | = link_to_destroy term 33 | -------------------------------------------------------------------------------- /app/views/terms/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/terms/show.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('pencil') + ' Edit', edit_term_path(@term), class: 'btn' 3 | 4 | %h2 5 | = @term.display_name 6 | %small= "(#{l @term.starts_on} to #{l @term.ends_on})" 7 | -------------------------------------------------------------------------------- /app/views/upload_roster/_table.html.haml: -------------------------------------------------------------------------------- 1 | - if @uploaded_roster.valid? 2 | %table#roster-preview.table.table-bordered.table-striped.table-condensed 3 | %thead 4 | %tr 5 | - (0...@uploaded_roster.column_count).each do |index| 6 | %th 7 | = select_tag "columns[#{index}]", options_for_select(UploadedRoster::COLUMNS, @uploaded_roster.column_mapping[index]) 8 | %tbody 9 | - @uploaded_roster.preview_rows.each do |row| 10 | %tr 11 | - row.each do |value| 12 | %td= value 13 | -------------------------------------------------------------------------------- /app/views/upload_roster/index.js.coffee: -------------------------------------------------------------------------------- 1 | # Append the course enrollments dialog to the page body. 2 | $('body').append( 3 | '<%= j render partial: "upload_roster/modal" %>') 4 | 5 | # Ensure that the dialog gets removed from the DOM when it is closed. 6 | $('#upload_roster_modal').on 'hidden', -> 7 | $('#upload_roster_modal').remove() 8 | 9 | $('#upload_roster_modal').on 'shown', -> 10 | $('#upload_roster_modal select').selectpicker() 11 | 12 | $('#upload_roster_modal #roster').change -> 13 | $('#upload_roster_modal #has_csv').val(0) 14 | $(this).closest('form').submit() 15 | 16 | # Display the dialog. 17 | $('#upload_roster_modal').modal 'show' 18 | -------------------------------------------------------------------------------- /app/views/upload_roster/upload.js.coffee: -------------------------------------------------------------------------------- 1 | <%= remotipart_response do %> 2 | <% if params[:commit] %> 3 | # This submit came from clicking the submit button, so dismiss 4 | # the modal. 5 | $('#upload_roster_modal').modal 'hide' 6 | <% else %> 7 | # This submit came from the file upload control, so update and 8 | # display the preview table. 9 | $('#upload_roster_modal #roster-preview-wrapper').html( 10 | '<%= j render partial: "table" %>') 11 | $('#upload_roster_modal #roster-section2').slideDown() 12 | $('#upload_roster_modal select').selectpicker() 13 | <% end %> 14 | <% end %> 15 | -------------------------------------------------------------------------------- /app/views/users/_form.html.haml: -------------------------------------------------------------------------------- 1 | = pythy_form_for @user do |f| 2 | 3 | = f.text_field :email 4 | = f.text_field :first_name 5 | = f.text_field :last_name 6 | = f.collection_select :global_role_id , 'Global Role', GlobalRole.all, :id, :name , :prompt => true 7 | 8 | = f.actions do 9 | = f.submit 10 | -------------------------------------------------------------------------------- /app/views/users/_user.html.haml: -------------------------------------------------------------------------------- 1 | %tr 2 | %td.has_gravatar 3 | = gravatar_tag(user, 28) 4 | = full_name_or_none(user, muted: true, query: true) 5 | %td 6 | = link_to user do 7 | = query_highlight(user.email) 8 | %td= user.global_role.name 9 | %td 10 | %span.dropdown 11 | = link_to icon_tag('cog'), '#', data: { toggle: 'dropdown' } 12 | %ul.dropdown-menu.pull-right 13 | %li= link_to icon_tag('pencil') + ' Edit...', edit_user_path(user) 14 | %li= link_to_destroy user, text: true 15 | %li= link_to icon_tag('user') + ' Impersonate...', impersonate_user_path(user) 16 | -------------------------------------------------------------------------------- /app/views/users/edit.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/users/index.html.haml: -------------------------------------------------------------------------------- 1 | .row 2 | .pull-right 3 | = index_search_form 4 | 5 | %table.table.table-striped.table-bordered.table-condensed 6 | %thead 7 | %tr 8 | %th.span4 Full Name 9 | %th.span4 E-mail 10 | %th.span3 Role 11 | %th.span1= icon_tag('cog') 12 | %tbody#users 13 | - if @users.empty? 14 | %td{ colspan: 3 } 15 | .alert There are no users. 16 | - else 17 | = render @users 18 | 19 | - unless @users.empty? 20 | #users_paginator 21 | = paginate @users, remote: true 22 | -------------------------------------------------------------------------------- /app/views/users/index.js.coffee: -------------------------------------------------------------------------------- 1 | $('#users').html( 2 | '<%= escape_javascript render(@users) %>') 3 | $('#users_paginator').html( 4 | '<%= escape_javascript(paginate(@users, remote: true).to_s) %>') 5 | -------------------------------------------------------------------------------- /app/views/users/new.html.haml: -------------------------------------------------------------------------------- 1 | 2 | = render 'form' 3 | -------------------------------------------------------------------------------- /app/views/users/show.html.haml: -------------------------------------------------------------------------------- 1 | .pull-right 2 | = link_to icon_tag('pencil') + ' Edit', edit_user_path(@user), class: 'btn' 3 | 4 | .row 5 | .span3 6 | = gravatar_tag(@user, 210) 7 | 8 | .span9 9 | %h2= full_name_or_none(@user) 10 | %table.table 11 | %tbody 12 | %tr 13 | %th E-mail 14 | %td= mail_to @user.email 15 | %tr 16 | %th Global Role 17 | %td= @user.global_role.name 18 | 19 | %h3 Course Enrollments 20 | %table.table 21 | %tbody 22 | - @user.course_enrollments.each do |ce| 23 | %tr 24 | %td 25 | = succeed ':' do 26 | = link_to_if_can :show, ce.course_offering.course do 27 | = ce.course_offering.course.number 28 | = ce.course_offering.course.name 29 | = link_to_if_can :show, ce.course_offering do 30 | = surround '(', ')' do 31 | = ce.course_offering.short_label 32 | %td= ce.course_offering.term.display_name 33 | %td= ce.course_role.name -------------------------------------------------------------------------------- /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 Pythy::Application 5 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | 3 | # Set up gems listed in the Gemfile. 4 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 5 | 6 | require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) 7 | -------------------------------------------------------------------------------- /config/deploy/production.rb: -------------------------------------------------------------------------------- 1 | default_run_options[:pty] = true 2 | 3 | set :user, 'pythy' 4 | set :db_user, 'pythy' 5 | 6 | set :branch, 'stable' 7 | set :rails_env, 'production' 8 | set :deploy_to, "/home/#{user}/rails" 9 | 10 | after 'uploads:symlink', 'deploy:symlink_db' 11 | 12 | namespace :deploy do 13 | desc "Symlinks the database.yml" 14 | task :symlink_db, :roles => :app do 15 | run "ln -nfs #{deploy_to}/shared/config/database.yml #{release_path}/config/database.yml" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config/deploy/staging.rb: -------------------------------------------------------------------------------- 1 | default_run_options[:pty] = true 2 | 3 | set :user, 'pythystaging' 4 | set :db_user, 'pythystaging' 5 | 6 | set :branch, 'master' 7 | set :rails_env, 'staging' 8 | set :deploy_to, "/home/#{user}/rails" 9 | 10 | after 'uploads:symlink', 'deploy:symlink_db' 11 | 12 | namespace :deploy do 13 | desc "Symlinks the database.yml" 14 | task :symlink_db, :roles => :app do 15 | run "ln -nfs #{deploy_to}/shared/config/database.yml #{release_path}/config/database.yml" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the rails application 2 | require File.expand_path('../application', __FILE__) 3 | require 'rails_extensions' 4 | 5 | # Generate non-pretty-printed HTML even in development (it's faster, and 6 | # with tools like Firebug and the Webkit inspector, we don't need to look 7 | # directly at the source) 8 | Haml::Template.options[:ugly] = true 9 | 10 | # Initialize the rails application 11 | Pythy::Application.initialize! 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/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # We use the word "staff" to refer to users who are teachers or 4 | # graders on a course, so we add the inflection here to make sure 5 | # that singular/plural automated conversions do the right thing 6 | # (we don't want to have to write "staffs"). 7 | ActiveSupport::Inflector.inflections do |inflect| 8 | inflect.irregular 'staff', 'staff' 9 | end 10 | -------------------------------------------------------------------------------- /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 | # Mime::Type.register_alias "text/html", :iphone 6 | -------------------------------------------------------------------------------- /config/initializers/omniauth.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.use OmniAuth::Builder do 2 | provider :facebook, '204740312977274', 'c5315ec75271f0030534cdd27cfaa4a4' 3 | end 4 | -------------------------------------------------------------------------------- /config/initializers/rabl.rb: -------------------------------------------------------------------------------- 1 | Rabl.configure do |config| 2 | config.include_json_root = false 3 | end 4 | -------------------------------------------------------------------------------- /config/initializers/redis.rb: -------------------------------------------------------------------------------- 1 | # TODO We may want to allow this to be configured through VERY advanced system 2 | # configuration options. 3 | $raw_redis = Redis.new(host: 'localhost', port: 6379) 4 | 5 | # We want any Redis key access to be properly namespaced by the application 6 | # name, in case we're running multiple apps on the server that access the 7 | # same Redis instance. In addition to that, we include the name of the 8 | # current environment as part of the key prefix, so that doing work under 9 | # development/staging instances does not pollute the production keyspace. 10 | $redis_namespace = "pythy:#{Rails.env}" 11 | $redis = Redis::Namespace.new($redis_namespace, redis: $raw_redis) 12 | 13 | # Configure Sidekiq's background job processor to use the same namespace. 14 | # This must match what is in config/sidekiq.yml. 15 | Sidekiq.configure_server do |config| 16 | config.redis = { namespace: "#{$redis_namespace}:sidekiq" } 17 | end 18 | 19 | Sidekiq.configure_client do |config| 20 | config.redis = { namespace: "#{$redis_namespace}:sidekiq" } 21 | end 22 | -------------------------------------------------------------------------------- /config/initializers/secret_token.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | # Make sure the secret is at least 30 characters and all random, 6 | # no regular words or you'll be exposed to dictionary attacks. 7 | Pythy::Application.config.secret_token = 'a0e3c651211d9a2e88b4e51cb20bd2002f75c7e2234d41b44ff1bc8c5382d5958fdaa28b0ecfc622941cc9f3a037904c992bb173637ae08a493019854405be9e' 8 | Pythy::Application.config.secret_key_base = 'xxa0e3c651211d9a2e88b4e51cb20bd2002f75c7e2234d41b44ff1bc8c5382d5958fdaa28b0ecfc622941cc9f3a037904c992bb173637ae08a493019854405be9e' 9 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Pythy::Application.config.session_store :cookie_store, :key => '_pythy_session' 4 | 5 | # Use the database for sessions instead of the cookie-based default, 6 | # which shouldn't be used to store highly confidential information 7 | # (create the session table with "rails generate session_migration") 8 | # Pythy::Application.config.session_store :active_record_store 9 | -------------------------------------------------------------------------------- /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] 9 | end 10 | 11 | # Disable root element in JSON by default. 12 | ActiveSupport.on_load(:active_record) do 13 | self.include_root_in_json = false 14 | end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Localizations for US English. 2 | 3 | en: 4 | date: 5 | input: 6 | formats: 7 | - :default 8 | formats: 9 | default: "%m/%d/%Y" 10 | time: 11 | input: 12 | formats: 13 | - :default 14 | formats: 15 | default: "%m/%d/%Y %H:%M" 16 | date_only: "%m/%d/%Y" 17 | time_only: "%H:%M" 18 | am: "am" 19 | pm: "pm" 20 | 21 | activerecord: 22 | attributes: 23 | course_offering: 24 | url: "URL" 25 | 26 | user: 27 | email: 'E-mail address' 28 | password_confirmation: 'Confirm password' -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | :verbose: false 2 | :pidfile: ./tmp/pids/sidekiq.pid 3 | :namespace: pythy:<%= ENV['RAILS_ENV'] %>:sidekiq 4 | :concurrency: 25 5 | :queues: 6 | - [default, 5] 7 | -------------------------------------------------------------------------------- /db/migrate/20120924132009_create_institutions.rb: -------------------------------------------------------------------------------- 1 | class CreateInstitutions < ActiveRecord::Migration 2 | 3 | def change 4 | create_table :institutions do |t| 5 | t.string :domain 6 | t.string :display_name 7 | t.string :abbreviation 8 | t.string :url_part 9 | t.timestamps 10 | end 11 | 12 | add_index :institutions, :abbreviation, unique: true 13 | add_index :institutions, :domain, unique: true 14 | add_index :institutions, :url_part, unique: true 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20120924132121_create_departments.rb: -------------------------------------------------------------------------------- 1 | class CreateDepartments < ActiveRecord::Migration 2 | def change 3 | create_table :departments do |t| 4 | t.integer :institution_id 5 | t.string :name 6 | t.string :abbreviation 7 | t.string :url_part 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20120924132144_create_terms.rb: -------------------------------------------------------------------------------- 1 | class CreateTerms < ActiveRecord::Migration 2 | def change 3 | create_table :terms do |t| 4 | t.integer :year 5 | t.integer :season 6 | t.date :starts_on 7 | t.date :ends_on 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20120924132208_create_courses.rb: -------------------------------------------------------------------------------- 1 | class CreateCourses < ActiveRecord::Migration 2 | def change 3 | create_table :courses do |t| 4 | t.string :name 5 | t.string :number 6 | t.integer :department_id 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20120924132630_create_course_offerings.rb: -------------------------------------------------------------------------------- 1 | class CreateCourseOfferings < ActiveRecord::Migration 2 | def change 3 | create_table :course_offerings do |t| 4 | t.integer :course_id 5 | t.integer :term_id 6 | t.integer :crn 7 | t.string :label 8 | t.string :url 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20120924132808_create_assignments.rb: -------------------------------------------------------------------------------- 1 | class CreateAssignments < ActiveRecord::Migration 2 | def change 3 | create_table :assignments do |t| 4 | t.integer :creator_id 5 | t.integer :course_id 6 | t.string :short_name 7 | t.string :long_name 8 | t.text :brief_summary 9 | t.text :description 10 | t.string :url_part 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20120924133015_create_assignment_offerings.rb: -------------------------------------------------------------------------------- 1 | class CreateAssignmentOfferings < ActiveRecord::Migration 2 | def change 3 | create_table :assignment_offerings do |t| 4 | t.integer :assignment_id 5 | t.integer :course_offering_id 6 | t.datetime :opens_at 7 | t.datetime :closes_at 8 | t.datetime :due_at 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20120924141027_add_initial_columns_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddInitialColumnsToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :institution_id, :integer 4 | add_column :users, :first_name, :string 5 | add_column :users, :last_name, :string 6 | add_column :users, :global_role_id, :integer 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20120927011009_create_rails_admin_histories_table.rb: -------------------------------------------------------------------------------- 1 | class CreateRailsAdminHistoriesTable < ActiveRecord::Migration 2 | def self.up 3 | create_table :rails_admin_histories do |t| 4 | t.text :message # title, name, or object_id 5 | t.string :username 6 | t.integer :item 7 | t.string :table 8 | t.integer :month, :limit => 2 9 | t.integer :year, :limit => 5 10 | t.timestamps 11 | end 12 | add_index(:rails_admin_histories, [:item, :table, :month, :year], :name => 'index_rails_admin_histories' ) 13 | end 14 | 15 | def self.down 16 | drop_table :rails_admin_histories 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20121114021257_create_global_roles.rb: -------------------------------------------------------------------------------- 1 | class CreateGlobalRoles < ActiveRecord::Migration 2 | def change 3 | create_table :global_roles do |t| 4 | t.string :name, :unique => true, :null => false 5 | t.boolean :can_create_courses, :null => false, :default => false 6 | t.boolean :can_manage_all_courses, :null => false, :default => false 7 | t.boolean :can_edit_system_configuration, :null => false, :default => false 8 | t.boolean :builtin, :null => false, :default => false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121205005020_create_authentications.rb: -------------------------------------------------------------------------------- 1 | class CreateAuthentications < ActiveRecord::Migration 2 | def change 3 | create_table :authentications do |t| 4 | t.integer :user_id 5 | t.string :provider 6 | t.string :uid 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20121206145720_create_repositories.rb: -------------------------------------------------------------------------------- 1 | class CreateRepositories < ActiveRecord::Migration 2 | def change 3 | create_table :repositories do |t| 4 | t.string :type 5 | t.integer :user_id 6 | t.integer :term_id 7 | t.timestamps 8 | 9 | # when type == 'AssignmentRepository' 10 | t.integer :assignment_offering_id 11 | 12 | # when type == 'ExampleRepository' 13 | t.integer :course_offering_id 14 | t.text :description 15 | t.integer :source_repository_id 16 | 17 | # when type == 'PersonalRepository' or 'ExampleRepository' 18 | t.string :name 19 | 20 | # when type == 'AssignmentReferenceRepository' 21 | t.integer :assignment_id 22 | 23 | # when type == 'ScratchpadRepository', no extra columns yet 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /db/migrate/20121212151228_create_course_roles.rb: -------------------------------------------------------------------------------- 1 | class CreateCourseRoles < ActiveRecord::Migration 2 | def change 3 | create_table :course_roles do |t| 4 | t.string :name, :unique => true, :null => false 5 | t.boolean :can_manage_course, :null => false, :default => false 6 | t.boolean :can_manage_assignments, :null => false, :default => false 7 | t.boolean :can_grade_submissions, :null => false, :default => false 8 | t.boolean :can_view_other_submissions, :null => false, :default => false 9 | t.boolean :builtin, :null => false, :default => false 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20121212161741_create_course_enrollments.rb: -------------------------------------------------------------------------------- 1 | class CreateCourseEnrollments < ActiveRecord::Migration 2 | def change 3 | create_table :course_enrollments do |t| 4 | t.references :user 5 | t.references :course_offering 6 | t.references :course_role 7 | end 8 | add_index :course_enrollments, :user_id 9 | add_index :course_enrollments, :course_offering_id 10 | add_index :course_enrollments, :course_role_id 11 | 12 | # disallow duplicate enrollments 13 | add_index :course_enrollments, [:user_id, :course_offering_id], :unique => true 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20121224211905_create_activity_logs.rb: -------------------------------------------------------------------------------- 1 | class CreateActivityLogs < ActiveRecord::Migration 2 | 3 | def change 4 | create_table :activity_logs do |t| 5 | t.integer :user_id 6 | t.string :action 7 | t.text :info 8 | t.timestamps 9 | end 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20130103133018_create_system_configurations.rb: -------------------------------------------------------------------------------- 1 | class CreateSystemConfigurations < ActiveRecord::Migration 2 | 3 | def change 4 | create_table :system_configurations do |t| 5 | t.string :storage_path 6 | t.string :work_path 7 | t.timestamps 8 | end 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130109175139_create_assignment_checks.rb: -------------------------------------------------------------------------------- 1 | class CreateAssignmentChecks < ActiveRecord::Migration 2 | 3 | def change 4 | create_table :assignment_checks do |t| 5 | t.integer :assignment_repository_id 6 | t.integer :number 7 | t.integer :status 8 | t.decimal :overall_score, precision: 16, scale: 8 9 | t.text :extra 10 | t.timestamps 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130111190807_create_check_outcomes.rb: -------------------------------------------------------------------------------- 1 | class CreateCheckOutcomes < ActiveRecord::Migration 2 | 3 | def change 4 | create_table :check_outcomes do |t| 5 | t.integer :assignment_check_id 6 | t.string :name 7 | t.string :description 8 | t.string :category 9 | t.integer :position 10 | t.decimal :score, precision: 16, scale: 8 11 | t.decimal :possible_score, precision: 16, scale: 8 12 | t.text :detail 13 | t.timestamps 14 | end 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20130518031137_create_media_items.rb: -------------------------------------------------------------------------------- 1 | class CreateMediaItems < ActiveRecord::Migration 2 | 3 | def change 4 | create_table :media_items do |t| 5 | t.integer :user_id 6 | t.integer :assignment_id 7 | t.string :file 8 | t.string :content_type 9 | t.integer :file_size 10 | t.text :info 11 | 12 | t.timestamps 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20130529205501_add_resource_key_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddResourceKeyToUsers < ActiveRecord::Migration 2 | 3 | def change 4 | add_column :users, :resource_key, :string 5 | add_index :users, :resource_key 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20130529205532_add_resource_key_to_assignments.rb: -------------------------------------------------------------------------------- 1 | class AddResourceKeyToAssignments < ActiveRecord::Migration 2 | 3 | def change 4 | add_column :assignments, :resource_key, :string 5 | add_index :assignments, :resource_key 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20130615112518_add_commit_sha_to_assignment_checks.rb: -------------------------------------------------------------------------------- 1 | class AddCommitShaToAssignmentChecks < ActiveRecord::Migration 2 | 3 | def change 4 | add_column :assignment_checks, :commit_sha, :string 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130617144912_add_self_enrollment_allowed_to_course_offerings.rb: -------------------------------------------------------------------------------- 1 | class AddSelfEnrollmentAllowedToCourseOfferings < ActiveRecord::Migration 2 | 3 | def change 4 | add_column :course_offerings, :self_enrollment_allowed, :boolean 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130617145517_drop_departments_table.rb: -------------------------------------------------------------------------------- 1 | class DropDepartmentsTable < ActiveRecord::Migration 2 | 3 | def up 4 | drop_table :departments 5 | remove_column :courses, :department_id 6 | end 7 | 8 | def down 9 | raise ActiveRecord::IrreversibleMigration 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20130617145542_add_institution_id_to_courses.rb: -------------------------------------------------------------------------------- 1 | class AddInstitutionIdToCourses < ActiveRecord::Migration 2 | 3 | def change 4 | add_column :courses, :institution_id, :integer 5 | add_column :courses, :url_part, :string 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20130617204903_remove_institution_id_from_users.rb: -------------------------------------------------------------------------------- 1 | class RemoveInstitutionIdFromUsers < ActiveRecord::Migration 2 | 3 | def up 4 | remove_column :users, :institution_id 5 | end 6 | 7 | def down 8 | raise ActiveRecord::IrreversibleMigration 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130617205241_rename_institutions_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class RenameInstitutionsToOrganizations < ActiveRecord::Migration 2 | 3 | def change 4 | rename_table :institutions, :organizations 5 | rename_column :courses, :institution_id, :organization_id 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20130619172521_merge_first_last_name_into_full_name.rb: -------------------------------------------------------------------------------- 1 | class MergeFirstLastNameIntoFullName < ActiveRecord::Migration 2 | 3 | def up 4 | add_column :users, :full_name, :string 5 | 6 | # Before removing the old columns, concatenate the users' first and last 7 | # names into the single full_name column. 8 | execute 'update users set full_name = concat_ws(\' \', first_name, last_name)' 9 | 10 | remove_column :users, :first_name 11 | remove_column :users, :last_name 12 | end 13 | 14 | def down 15 | raise ActiveRecord::IrreversibleMigration 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20130623191637_rename_course_offering_crn_and_label.rb: -------------------------------------------------------------------------------- 1 | class RenameCourseOfferingCrnAndLabel < ActiveRecord::Migration 2 | 3 | def up 4 | rename_column :course_offerings, :crn, :short_label 5 | change_column :course_offerings, :short_label, :string 6 | 7 | rename_column :course_offerings, :label, :long_label 8 | end 9 | 10 | def down 11 | raise ActiveRecord::IrreversibleMigration 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20130625201013_create_environments.rb: -------------------------------------------------------------------------------- 1 | class CreateEnvironments < ActiveRecord::Migration 2 | 3 | def change 4 | create_table :environments do |t| 5 | t.string :name 6 | t.text :preamble 7 | t.timestamps 8 | end 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20130625202204_add_default_environment_id_to_courses.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultEnvironmentIdToCourses < ActiveRecord::Migration 2 | 3 | def change 4 | add_column :courses, :default_environment_id, :integer 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20130625202241_add_environment_id_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddEnvironmentIdToRepositories < ActiveRecord::Migration 2 | 3 | def change 4 | add_column :repositories, :environment_id, :integer 5 | end 6 | 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20131014014924_add_term_id_to_assignments.rb: -------------------------------------------------------------------------------- 1 | class AddTermIdToAssignments < ActiveRecord::Migration 2 | def change 3 | add_column :assignments, :term_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131119081535_split_full_name_into_first_and_last_name.rb: -------------------------------------------------------------------------------- 1 | class SplitFullNameIntoFirstAndLastName < ActiveRecord::Migration 2 | 3 | def up 4 | add_column :users, :first_name, :string 5 | add_column :users, :last_name, :string 6 | 7 | execute 'update users set last_name = substring_index(full_name, \' \', -1)' 8 | execute 'update users set first_name = left(full_name, length(full_name) - locate(\' \', reverse(full_name)))' 9 | 10 | remove_column :users, :full_name 11 | end 12 | 13 | def down 14 | add_column :users, :full_name, :string 15 | 16 | # Before removing the old columns, concatenate the users' first and last 17 | # names into the single full_name column. 18 | execute 'update users set full_name = concat_ws(\' \', first_name, last_name)' 19 | 20 | remove_column :users, :first_name 21 | remove_column :users, :last_name 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /db/migrate/20140116001823_add_current_course_and_current_term_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddCurrentCourseAndCurrentTermToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :current_course_id, :integer 4 | add_column :users, :current_term_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /doc/other_features.md: -------------------------------------------------------------------------------- 1 | Other Features 2 | -------------- 3 | 4 | 1. Lack of repl and functions like help. 5 | 2. turtle graphics support. 6 | -------------------------------------------------------------------------------- /erd.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/erd.pdf -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/custom_failure.rb: -------------------------------------------------------------------------------- 1 | class CustomFailure < Devise::FailureApp 2 | 3 | def redirect_url 4 | #return super unless [:worker, :employer, :user].include?(scope) 5 | #make it specific to a scope 6 | needs_initial_setup? ? root_url : super 7 | end 8 | 9 | # You need to override respond to eliminate recall 10 | def respond 11 | http_auth? ? http_auth : redirect 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | 2 | To change the file structure of the reference repositories to the current version: 3 | 4 | - Create an "assignments" folder inside each term. 5 | 6 | - For each Assignment: 7 | - Assign a term_id to the assignment. 8 | - Move the reference repo folder (has the url_part as its name) to inside the corresponding "assignments" folder in the term folder. 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Pythy", 3 | "version": "0.1.0", 4 | "description": "An online IDE for novice Python environment", 5 | "dependencies": { 6 | "juggernaut": "2.1.1" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The page you were looking for doesn't exist.

23 |

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

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

The change you wanted was rejected.

23 |

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

24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
22 |

We're sorry, but something went wrong.

23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/public/favicon.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.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 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- 1 | This folder contains Python support code that Pythy uses during 2 | server-side checks. 3 | -------------------------------------------------------------------------------- /python/image/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pythy server-side image module. 3 | """ 4 | -------------------------------------------------------------------------------- /python/image/fonts/Comic_Sans_MS.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Comic_Sans_MS.ttf -------------------------------------------------------------------------------- /python/image/fonts/Comic_Sans_MS_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Comic_Sans_MS_Bold.ttf -------------------------------------------------------------------------------- /python/image/fonts/Comic_Sans_MS_Bold_Italic.ttf: -------------------------------------------------------------------------------- 1 | Comic_Sans_MS_Bold.ttf -------------------------------------------------------------------------------- /python/image/fonts/Comic_Sans_MS_Italic.ttf: -------------------------------------------------------------------------------- 1 | Comic_Sans_MS.ttf -------------------------------------------------------------------------------- /python/image/fonts/LiberationMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/LiberationMono.ttf -------------------------------------------------------------------------------- /python/image/fonts/LiberationMono_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/LiberationMono_Bold.ttf -------------------------------------------------------------------------------- /python/image/fonts/LiberationMono_Bold_Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/LiberationMono_Bold_Italic.ttf -------------------------------------------------------------------------------- /python/image/fonts/LiberationMono_Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/LiberationMono_Italic.ttf -------------------------------------------------------------------------------- /python/image/fonts/Times_New_Roman.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Times_New_Roman.ttf -------------------------------------------------------------------------------- /python/image/fonts/Times_New_Roman_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Times_New_Roman_Bold.ttf -------------------------------------------------------------------------------- /python/image/fonts/Times_New_Roman_Bold_Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Times_New_Roman_Bold_Italic.ttf -------------------------------------------------------------------------------- /python/image/fonts/Times_New_Roman_Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Times_New_Roman_Italic.ttf -------------------------------------------------------------------------------- /python/image/fonts/Verdana.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Verdana.ttf -------------------------------------------------------------------------------- /python/image/fonts/Verdana_Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Verdana_Bold.ttf -------------------------------------------------------------------------------- /python/image/fonts/Verdana_Bold_Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Verdana_Bold_Italic.ttf -------------------------------------------------------------------------------- /python/image/fonts/Verdana_Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/image/fonts/Verdana_Italic.ttf -------------------------------------------------------------------------------- /python/media/__init__.py: -------------------------------------------------------------------------------- 1 | # Order is important here 2 | 3 | from image.style import * 4 | from image.color import * 5 | from image.pixel import * 6 | from image.picture import * 7 | from image import * 8 | from sound import * 9 | from sound.sample import * 10 | from sound.sound import * 11 | 12 | _picked_files = [] 13 | 14 | def setPickedFile(filename): _picked_files.append(filename) 15 | 16 | def pickAFile(): 17 | if(len(_picked_files)): 18 | return _picked_files.pop(0) 19 | else: 20 | raise IndexError('Please set atleast one picked file using ' + 21 | 'setPickedFile(filename) before calling this method') 22 | 23 | def setMediaPath(path): pass 24 | 25 | def getMediaPath(filename=''): return filename 26 | -------------------------------------------------------------------------------- /python/pythy/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pythy server-side support module. 3 | 4 | This module contains core classes used by Pythy when student code is 5 | executed on the server (such as when reference tests are being checked). 6 | """ 7 | 8 | 9 | __all__ = ['TestCase', 'TestRunner', 'runAllTests'] 10 | 11 | __pythy = True 12 | 13 | from .test import TestCase, TestRunner, runAllTests 14 | -------------------------------------------------------------------------------- /python/sound/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pythy server-side sound module. 3 | """ 4 | -------------------------------------------------------------------------------- /python/sound/sample.py: -------------------------------------------------------------------------------- 1 | def getSound(sample): 2 | return sample.sound 3 | 4 | def getSampleValue(sample): 5 | return sample.sound.getLeftSample(sample.index) 6 | 7 | def setSampleValue(sample, value): 8 | sample.sound.setLeftSample(sample.index, value) 9 | 10 | class Sample: 11 | def __init__(self, sound, index): 12 | self.sound = sound 13 | self.index = index 14 | 15 | def __str__(self): 16 | return 'Sample at {} with value {}'.format(str(self.index), str(self.getSampleValue())) 17 | 18 | Sample.getSound = getSound 19 | Sample.getSampleValue = getSampleValue 20 | Sample.setSampleValue = setSampleValue 21 | -------------------------------------------------------------------------------- /python/tests/image/README.md: -------------------------------------------------------------------------------- 1 | Instructions 2 | ------------ 3 | 4 | 1. Start a SimpleHTTPServer `python -m SimpleHTTPServer 9000` 5 | 2. Run the tests using ./run.sh 6 | 7 | An http server is need to avoid cross-origin tainting of the canvas/sound when testing. 8 | The source code is referenced through symlinks so that they can be accessed by the http server. 9 | -------------------------------------------------------------------------------- /python/tests/image/imgs/addArcFilled_10_10_100_50_60_30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addArcFilled_10_10_100_50_60_30.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addArcFilled_10_10_100_50_60_30_magenta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addArcFilled_10_10_100_50_60_30_magenta.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addArc_10_10_100_50_60_30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addArc_10_10_100_50_60_30.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addArc_10_10_100_50_60_30_magenta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addArc_10_10_100_50_60_30_magenta.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addLine_10_10_20_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addLine_10_10_20_20.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addLine_10_10_20_20_pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addLine_10_10_20_20_pink.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addOvalFilled_10_10_70_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addOvalFilled_10_10_70_50.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addOvalFilled_10_10_70_50_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addOvalFilled_10_10_70_50_yellow.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addOval_10_10_70_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addOval_10_10_70_50.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addOval_10_10_70_50_orange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addOval_10_10_70_50_orange.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addRectFilled_10_10_70_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addRectFilled_10_10_70_50.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addRectFilled_10_10_70_50_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addRectFilled_10_10_70_50_red.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addRect_10_10_70_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addRect_10_10_70_50.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addRect_10_10_70_50_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addRect_10_10_70_50_red.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addTextWithStyle_10_10_Hello_sansSerif_BOLDITALIC_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addTextWithStyle_10_10_Hello_sansSerif_BOLDITALIC_10.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addTextWithStyle_10_10_Hello_sansSerif_BOLDITALIC_10_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addTextWithStyle_10_10_Hello_sansSerif_BOLDITALIC_10_green.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addText_10_10_Hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addText_10_10_Hello.png -------------------------------------------------------------------------------- /python/tests/image/imgs/addText_10_10_Hello_cyan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/addText_10_10_Hello_cyan.png -------------------------------------------------------------------------------- /python/tests/image/imgs/copyInto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/copyInto.png -------------------------------------------------------------------------------- /python/tests/image/imgs/orangePic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/orangePic.png -------------------------------------------------------------------------------- /python/tests/image/imgs/test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/image/imgs/test.jpg -------------------------------------------------------------------------------- /python/tests/image/run.sh: -------------------------------------------------------------------------------- 1 | export PYTHONPATH=../.. 2 | python3 -m unittest discover . '*_test.py' 3 | -------------------------------------------------------------------------------- /python/tests/media/README.md: -------------------------------------------------------------------------------- 1 | Instructions 2 | ------------ 3 | 4 | 1. Run the tests using ./run.sh 5 | -------------------------------------------------------------------------------- /python/tests/media/init_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from media import * 3 | 4 | class ImageTest(unittest.TestCase): 5 | def test_pickAFile(self): 6 | with self.assertRaises(IndexError): fileURL = pickAFile() 7 | setPickedFile('http://localhost:9000/imgs/test.jpg') 8 | fileURL = pickAFile() 9 | self.assertEqual('http://localhost:9000/imgs/test.jpg', fileURL) 10 | 11 | def test_setMediaPath(self): 12 | setMediaPath('abc') 13 | 14 | def test_getMediaPath(self): 15 | self.assertEqual(getMediaPath(), '') 16 | self.assertEqual(getMediaPath('filename'), 'filename') 17 | 18 | -------------------------------------------------------------------------------- /python/tests/media/run.sh: -------------------------------------------------------------------------------- 1 | export PYTHONPATH=../.. 2 | python3 -m unittest discover . '*_test.py' 3 | -------------------------------------------------------------------------------- /python/tests/sound/README.md: -------------------------------------------------------------------------------- 1 | Instructions 2 | ------------ 3 | 4 | 1. Start a SimpleHTTPServer `python -m SimpleHTTPServer 9000` 5 | 2. Run the tests using ./run.sh 6 | 7 | An http server is need to avoid cross-origin tainting of the canvas/sound when testing. 8 | The source code is referenced through symlinks so that they can be accessed by the http server. 9 | -------------------------------------------------------------------------------- /python/tests/sound/run.sh: -------------------------------------------------------------------------------- 1 | export PYTHONPATH=../.. 2 | python3 -m unittest discover . '*_test.py' 3 | -------------------------------------------------------------------------------- /python/tests/sound/sounds/test_mono.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/sound/sounds/test_mono.wav -------------------------------------------------------------------------------- /python/tests/sound/sounds/test_stereo.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/python/tests/sound/sounds/test_stereo.wav -------------------------------------------------------------------------------- /python/unittest/__main__.py: -------------------------------------------------------------------------------- 1 | """Main entry point""" 2 | 3 | import sys 4 | if sys.argv[0].endswith("__main__.py"): 5 | sys.argv[0] = "python -m unittest" 6 | 7 | __unittest = True 8 | 9 | from .main import main, TestProgram, USAGE_AS_MAIN 10 | TestProgram.USAGE = USAGE_AS_MAIN 11 | 12 | main(module=None) 13 | -------------------------------------------------------------------------------- /python/unittest/test/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | 6 | here = os.path.dirname(__file__) 7 | loader = unittest.defaultTestLoader 8 | 9 | def suite(): 10 | suite = unittest.TestSuite() 11 | for fn in os.listdir(here): 12 | if fn.startswith("test") and fn.endswith(".py"): 13 | modname = "unittest.test." + fn[:-3] 14 | __import__(modname) 15 | module = sys.modules[modname] 16 | suite.addTest(loader.loadTestsFromModule(module)) 17 | suite.addTest(loader.loadTestsFromName('unittest.test.testmock')) 18 | return suite 19 | 20 | 21 | if __name__ == "__main__": 22 | unittest.main(defaultTest="suite") 23 | -------------------------------------------------------------------------------- /python/unittest/test/dummy.py: -------------------------------------------------------------------------------- 1 | # Empty module for testing the loading of modules 2 | -------------------------------------------------------------------------------- /python/unittest/test/testmock/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | 5 | 6 | here = os.path.dirname(__file__) 7 | loader = unittest.defaultTestLoader 8 | 9 | def load_tests(*args): 10 | suite = unittest.TestSuite() 11 | for fn in os.listdir(here): 12 | if fn.startswith("test") and fn.endswith(".py"): 13 | modname = "unittest.test.testmock." + fn[:-3] 14 | __import__(modname) 15 | module = sys.modules[modname] 16 | suite.addTest(loader.loadTestsFromModule(module)) 17 | return suite 18 | -------------------------------------------------------------------------------- /python/unittest/test/testmock/support.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def is_instance(obj, klass): 4 | """Version of is_instance that doesn't access __class__""" 5 | return issubclass(type(obj), klass) 6 | 7 | 8 | class SomeClass(object): 9 | class_attribute = None 10 | 11 | def wibble(self): 12 | pass 13 | 14 | 15 | class X(object): 16 | pass 17 | 18 | 19 | def examine_warnings(func): 20 | def wrapper(): 21 | with catch_warnings(record=True) as ws: 22 | func(ws) 23 | return wrapper 24 | -------------------------------------------------------------------------------- /python/unittest/test/testmock/testsentinel.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import sentinel, DEFAULT 3 | 4 | 5 | class SentinelTest(unittest.TestCase): 6 | 7 | def testSentinels(self): 8 | self.assertEqual(sentinel.whatever, sentinel.whatever, 9 | 'sentinel not stored') 10 | self.assertNotEqual(sentinel.whatever, sentinel.whateverelse, 11 | 'sentinel should be unique') 12 | 13 | 14 | def testSentinelName(self): 15 | self.assertEqual(str(sentinel.whatever), 'sentinel.whatever', 16 | 'sentinel name incorrect') 17 | 18 | 19 | def testDEFAULT(self): 20 | self.assertTrue(DEFAULT is sentinel.DEFAULT) 21 | 22 | def testBases(self): 23 | # If this doesn't raise an AttributeError then help(mock) is broken 24 | self.assertRaises(AttributeError, lambda: sentinel.__bases__) 25 | 26 | 27 | if __name__ == '__main__': 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /script/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application. 3 | 4 | APP_PATH = File.expand_path('../../config/application', __FILE__) 5 | require File.expand_path('../../config/boot', __FILE__) 6 | require 'rails/commands' 7 | -------------------------------------------------------------------------------- /spec/factories/activity_log_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :activity_log1, class: ActivityLog do 3 | user 4 | action "Test Log1" 5 | info location: "Factory", type: "Test" 6 | end 7 | factory :activity_log2, class: ActivityLog do 8 | user 9 | action "Test Log2" 10 | info location: "Factory", type: "Test" 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/factories/course_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :course, class: Course do 3 | name "Introduction to Media Computation" 4 | number "CS 1124" 5 | default_environment_id Environment.first.id 6 | organization 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/course_role_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :course_role, class: CourseRole do 3 | name "Teaching Assistant" 4 | builtin false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/environment_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :environment, class: Environment do 3 | name "Python v3.0" 4 | preamble "Sample code goes in here" 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/global_role_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :global_role, class: GlobalRole do 3 | name "Teaching Assistant" 4 | builtin false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/organization_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :organization, class: Organization do 3 | display_name "Virginia Tech" 4 | domain "vt.edu" 5 | abbreviation "VT" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/system_configuration_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :sys_config, class: SystemConfiguration do 3 | storage_path Rails.root.join('spec', 'test_storage_path').to_s 4 | work_path Rails.root.join('spec', 'test_work_path').to_s 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/factories/term_factory.rb: -------------------------------------------------------------------------------- 1 | FactoryGirl.define do 2 | factory :summer1_term, class: Term do 3 | starts_on Date.new(2012, 5, 28) 4 | ends_on Date.new(2012, 7, 1) 5 | season Term::SEASONS['Summer I'] 6 | year 2012 7 | end 8 | factory :spring_term, class: Term do 9 | starts_on Date.new(2012, 1, 28) 10 | ends_on Date.new(2012, 5, 10) 11 | season Term::SEASONS['Spring'] 12 | year 2012 13 | end 14 | factory :summer2_term, class: Term do 15 | starts_on Date.new(2012, 7, 1) 16 | ends_on Date.new(2012, 8, 10) 17 | season Term::SEASONS['Summer II'] 18 | year 2012 19 | end 20 | factory :fall_term, class: Term do 21 | starts_on Date.new(2012, 8, 23) 22 | ends_on Date.new(2012, 12, 10) 23 | season Term::SEASONS['Fall'] 24 | year 2012 25 | end 26 | factory :winter_term, class: Term do 27 | starts_on Date.new(2012, 12, 15) 28 | ends_on Date.new(2013, 1, 20) 29 | season Term::SEASONS['Winter'] 30 | year 2012 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/javascripts/README.md: -------------------------------------------------------------------------------- 1 | Instructions 2 | ------------ 3 | 4 | 1. Start a SimpleHTTPServer `python -m SimpleHTTPServer 9000` 5 | 2. Visit the url `http://127.0.0.1:9000/` 6 | 7 | An http server is need to avoid cross-origin tainting of the canvas/sound when testing. 8 | The source code is referenced through symlinks so that they can be accessed by the http server. 9 | -------------------------------------------------------------------------------- /spec/javascripts/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pythy Sound API tests 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /spec/javascripts/sounds/test_mono.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/javascripts/sounds/test_mono.wav -------------------------------------------------------------------------------- /spec/javascripts/sounds/test_stereo.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/javascripts/sounds/test_stereo.wav -------------------------------------------------------------------------------- /spec/javascripts/src/sound.js.coffee: -------------------------------------------------------------------------------- 1 | ../../../app/assets/javascripts/sound.js.coffee -------------------------------------------------------------------------------- /spec/javascripts/stubs/pythy_stubs.js: -------------------------------------------------------------------------------- 1 | window.pythy = { 2 | uploadFileFromBlob: function () {} 3 | }; 4 | window.mediaffi = { 5 | customizeMediaURL: function (url) { return url; } 6 | }; 7 | window.CodeController = { 8 | transformUrl : function (url) { return url; } 9 | }; 10 | -------------------------------------------------------------------------------- /spec/models/coverage/.last_run.json: -------------------------------------------------------------------------------- 1 | { 2 | "result": { 3 | "covered_percent": 0.0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /spec/models/coverage/.resultset.json: -------------------------------------------------------------------------------- 1 | { 2 | "RSpec": { 3 | "coverage": { 4 | }, 5 | "timestamp": 1395849858 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/blank.gif -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_close.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_loading.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_nav_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_nav_left.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_nav_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_nav_right.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_e.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_n.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_ne.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_nw.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_s.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_se.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_sw.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_shadow_w.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_title_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_title_left.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_title_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_title_main.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_title_over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_title_over.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancy_title_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancy_title_right.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancybox-x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancybox-x.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancybox-y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancybox-y.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/fancybox/fancybox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/fancybox/fancybox.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/favicon_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/favicon_green.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/favicon_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/favicon_red.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/favicon_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/favicon_yellow.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/loading.gif -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/magnify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/magnify.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/spec/models/coverage/assets/0.7.1/smoothness/images/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /spec/models/environment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Environment do 4 | let(:environment) { FactoryGirl.build(:environment) } 5 | 6 | subject { environment } 7 | 8 | # Attribute testing 9 | it { should respond_to(:name) } 10 | it { should respond_to(:preamble) } 11 | 12 | # Association testing 13 | it { should have_many(:courses).with_foreign_key('default_environment_id') } 14 | it { should have_many(:repositories) } 15 | 16 | # Validation testing 17 | it { should be_valid } 18 | 19 | describe "when name is not present" do 20 | before { environment.name = '' } 21 | 22 | it { should be_invalid } 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /spec/models/system_configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe SystemConfiguration do 4 | let(:sys_config) { FactoryGirl.build(:sys_config) } 5 | subject { sys_config } 6 | 7 | it { should be_valid } 8 | it { should respond_to(:storage_path) } 9 | it { should respond_to(:work_path) } 10 | 11 | describe "when storage_path is empty" do 12 | before { sys_config.storage_path = ''} 13 | it { should_not be_valid } 14 | end 15 | 16 | describe "when work_path is empty" do 17 | before { sys_config.work_path = ''} 18 | it { should_not be_valid } 19 | end 20 | 21 | describe "when the rails server doesn't have permission 22 | to write to the storage_path" do 23 | before { sys_config.storage_path = '/' } 24 | it { should_not be_valid } 25 | end 26 | 27 | describe "when the rails server doesn't have permission 28 | to write to the work_path" do 29 | before { sys_config.work_path = '/' } 30 | it { should_not be_valid } 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/vendor/assets/javascripts/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/web-cat/pythy/8dfcfd595e596f4d6f768ea5a86d2396dcbb1258/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/accordion.less: -------------------------------------------------------------------------------- 1 | // 2 | // Accordion 3 | // -------------------------------------------------- 4 | 5 | 6 | // Parent container 7 | .accordion { 8 | margin-bottom: @baseLineHeight; 9 | } 10 | 11 | // Group == heading + body 12 | .accordion-group { 13 | margin-bottom: 2px; 14 | border: 1px solid #e5e5e5; 15 | .border-radius(@baseBorderRadius); 16 | } 17 | .accordion-heading { 18 | border-bottom: 0; 19 | } 20 | .accordion-heading .accordion-toggle { 21 | display: block; 22 | padding: 8px 15px; 23 | } 24 | 25 | // General toggle styles 26 | .accordion-toggle { 27 | cursor: pointer; 28 | } 29 | 30 | // Inner needs the styles because you can't animate properly with any styles on the element 31 | .accordion-inner { 32 | padding: 9px 15px; 33 | border-top: 1px solid #e5e5e5; 34 | } 35 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: 8px 15px; 8 | margin: 0 0 @baseLineHeight; 9 | list-style: none; 10 | background-color: #f5f5f5; 11 | .border-radius(@baseBorderRadius); 12 | > li { 13 | display: inline-block; 14 | .ie7-inline-block(); 15 | text-shadow: 0 1px 0 @white; 16 | > .divider { 17 | padding: 0 5px; 18 | color: #ccc; 19 | } 20 | } 21 | > .active { 22 | color: @grayLight; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: 20px; 9 | font-weight: bold; 10 | line-height: @baseLineHeight; 11 | color: @black; 12 | text-shadow: 0 1px 0 rgba(255,255,255,1); 13 | .opacity(20); 14 | &:hover { 15 | color: @black; 16 | text-decoration: none; 17 | cursor: pointer; 18 | .opacity(40); 19 | } 20 | } 21 | 22 | // Additional properties for button version 23 | // iOS requires the button element instead of an anchor tag. 24 | // If you want the anchor version, it requires `href="#"`. 25 | button.close { 26 | padding: 0; 27 | cursor: pointer; 28 | background: transparent; 29 | border: 0; 30 | -webkit-appearance: none; 31 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | 6 | .fade { 7 | opacity: 0; 8 | .transition(opacity .15s linear); 9 | &.in { 10 | opacity: 1; 11 | } 12 | } 13 | 14 | .collapse { 15 | position: relative; 16 | height: 0; 17 | overflow: hidden; 18 | .transition(height .35s ease); 19 | &.in { 20 | height: auto; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Fixed (940px) 7 | #grid > .core(@gridColumnWidth, @gridGutterWidth); 8 | 9 | // Fluid (940px) 10 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); 11 | 12 | // Reset utility classes due to specificity 13 | [class*="span"].hide, 14 | .row-fluid [class*="span"].hide { 15 | display: none; 16 | } 17 | 18 | [class*="span"].pull-right, 19 | .row-fluid [class*="span"].pull-right { 20 | float: right; 21 | } 22 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/hero-unit.less: -------------------------------------------------------------------------------- 1 | // 2 | // Hero unit 3 | // -------------------------------------------------- 4 | 5 | 6 | .hero-unit { 7 | padding: 60px; 8 | margin-bottom: 30px; 9 | font-size: 18px; 10 | font-weight: 200; 11 | line-height: @baseLineHeight * 1.5; 12 | color: @heroUnitLeadColor; 13 | background-color: @heroUnitBackground; 14 | .border-radius(6px); 15 | h1 { 16 | margin-bottom: 0; 17 | font-size: 60px; 18 | line-height: 1; 19 | color: @heroUnitHeadingColor; 20 | letter-spacing: -1px; 21 | } 22 | li { 23 | line-height: @baseLineHeight * 1.5; // Reset since we specify in type.less 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/layouts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Layouts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container (centered, fixed-width layouts) 7 | .container { 8 | .container-fixed(); 9 | } 10 | 11 | // Fluid layouts (left aligned, with sidebar, min- & max-width content) 12 | .container-fluid { 13 | padding-right: @gridGutterWidth; 14 | padding-left: @gridGutterWidth; 15 | .clearfix(); 16 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/media.less: -------------------------------------------------------------------------------- 1 | // Media objects 2 | // Source: http://stubbornella.org/content/?p=497 3 | // -------------------------------------------------- 4 | 5 | 6 | // Common styles 7 | // ------------------------- 8 | 9 | // Clear the floats 10 | .media, 11 | .media-body { 12 | overflow: hidden; 13 | *overflow: visible; 14 | zoom: 1; 15 | } 16 | 17 | // Proper spacing between instances of .media 18 | .media, 19 | .media .media { 20 | margin-top: 15px; 21 | } 22 | .media:first-child { 23 | margin-top: 0; 24 | } 25 | 26 | // For images and videos, set to block 27 | .media-object { 28 | display: block; 29 | } 30 | 31 | // Reset margins on headings for tighter default spacing 32 | .media-heading { 33 | margin: 0 0 5px; 34 | } 35 | 36 | 37 | // Media image alignment 38 | // ------------------------- 39 | 40 | .media .pull-left { 41 | margin-right: 10px; 42 | } 43 | .media .pull-right { 44 | margin-left: 10px; 45 | } 46 | 47 | 48 | // Media list variation 49 | // ------------------------- 50 | 51 | // Undo default ul/ol styles 52 | .media-list { 53 | margin-left: 0; 54 | list-style: none; 55 | } 56 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | margin: @baseLineHeight 0; 8 | list-style: none; 9 | text-align: center; 10 | .clearfix(); 11 | } 12 | .pager li { 13 | display: inline; 14 | } 15 | .pager li > a, 16 | .pager li > span { 17 | display: inline-block; 18 | padding: 5px 14px; 19 | background-color: #fff; 20 | border: 1px solid #ddd; 21 | .border-radius(15px); 22 | } 23 | .pager li > a:hover { 24 | text-decoration: none; 25 | background-color: #f5f5f5; 26 | } 27 | .pager .next > a, 28 | .pager .next > span { 29 | float: right; 30 | } 31 | .pager .previous > a, 32 | .pager .previous > span { 33 | float: left; 34 | } 35 | .pager .disabled > a, 36 | .pager .disabled > a:hover, 37 | .pager .disabled > span { 38 | color: @grayLight; 39 | background-color: #fff; 40 | cursor: default; 41 | } -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/responsive-1200px-min.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Large desktop and up 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 1200px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200); 16 | 17 | // Thumbnails 18 | .thumbnails { 19 | margin-left: -@gridGutterWidth1200; 20 | } 21 | .thumbnails > li { 22 | margin-left: @gridGutterWidth1200; 23 | } 24 | .row-fluid .thumbnails { 25 | margin-left: 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/responsive-768px-979px.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Tablet to desktop 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 768px) and (max-width: 979px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth768, @gridGutterWidth768); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth768, @gridGutterWidth768); 16 | 17 | // No need to reset .thumbnails here since it's the same @gridGutterWidth 18 | 19 | } 20 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/scaffolding.less: -------------------------------------------------------------------------------- 1 | // 2 | // Scaffolding 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body reset 7 | // ------------------------- 8 | 9 | body { 10 | margin: 0; 11 | font-family: @baseFontFamily; 12 | font-size: @baseFontSize; 13 | line-height: @baseLineHeight; 14 | color: @textColor; 15 | background-color: @bodyBackground; 16 | } 17 | 18 | 19 | // Links 20 | // ------------------------- 21 | 22 | a { 23 | color: @linkColor; 24 | text-decoration: none; 25 | } 26 | a:hover { 27 | color: @linkColorHover; 28 | text-decoration: underline; 29 | } 30 | 31 | 32 | // Images 33 | // ------------------------- 34 | 35 | // Rounded corners 36 | .img-rounded { 37 | .border-radius(6px); 38 | } 39 | 40 | // Add polaroid-esque trim 41 | .img-polaroid { 42 | padding: 4px; 43 | background-color: #fff; 44 | border: 1px solid #ccc; 45 | border: 1px solid rgba(0,0,0,.2); 46 | .box-shadow(0 1px 3px rgba(0,0,0,.1)); 47 | } 48 | 49 | // Perfect circle 50 | .img-circle { 51 | .border-radius(500px); // crank the border-radius so it works with most reasonably sized images 52 | } 53 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Quick floats 7 | .pull-right { 8 | float: right; 9 | } 10 | .pull-left { 11 | float: left; 12 | } 13 | 14 | // Toggling content 15 | .hide { 16 | display: none; 17 | } 18 | .show { 19 | display: block; 20 | } 21 | 22 | // Visibility 23 | .invisible { 24 | visibility: hidden; 25 | } 26 | 27 | // For Affix plugin 28 | .affix { 29 | position: fixed; 30 | } 31 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/twitter/bootstrap/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @wellBackground; 12 | border: 1px solid darken(@wellBackground, 7%); 13 | .border-radius(@baseBorderRadius); 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-large { 23 | padding: 24px; 24 | .border-radius(@borderRadiusLarge); 25 | } 26 | .well-small { 27 | padding: 9px; 28 | .border-radius(@borderRadiusSmall); 29 | } 30 | --------------------------------------------------------------------------------