├── .browserslistrc ├── .codecov.yml ├── .dockerignore ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── template-bug.md │ ├── template-change.md │ ├── template-epic.md │ └── template-story.md └── workflows │ ├── docker.yml │ ├── migrations.yml │ └── tests.yml ├── .gitignore ├── .rspec ├── .rspec_parallel ├── .rubocop.yml ├── .ruby-gemset ├── .ruby-version ├── AUTHORS ├── COPYRIGHT ├── GNU-AGPL-3.0 ├── Gemfile ├── Gemfile.lock ├── Guardfile ├── LICENSING ├── README.md ├── Rakefile ├── action.yml ├── app ├── access_policies │ ├── allow_all_access_policy.rb │ ├── cc │ │ └── task_access_policy.rb │ ├── course_access_policy.rb │ ├── doorkeeper │ │ └── application_access_policy.rb │ ├── ecosystem_access_policy.rb │ ├── enrollment_change_access_policy.rb │ ├── exercise_access_policy.rb │ ├── grading_template_access_policy.rb │ ├── job_access_policy.rb │ ├── note_access_policy.rb │ ├── offering_access_policy.rb │ ├── period_access_policy.rb │ ├── practice_question_access_policy.rb │ ├── research_survey_access_policy.rb │ ├── role_access_policy.rb │ ├── student_access_policy.rb │ ├── suggestion_access_policy.rb │ ├── target_access_policy.rb │ ├── task_access_policy.rb │ ├── task_plan_access_policy.rb │ ├── tasked_access_policy.rb │ ├── tasking_plan_access_policy.rb │ ├── teacher_access_policy.rb │ ├── teacher_student_access_policy.rb │ ├── track_tutor_onboarding_event_policy.rb │ └── user_access_policy.rb ├── assets │ ├── fonts │ │ ├── lato-v11-latin-regular.svg │ │ ├── lato-v11-latin-regular.ttf │ │ ├── lato-v11-latin-regular.woff │ │ └── lato-v11-latin-regular.woff2 │ ├── html │ │ ├── 404.html.erb │ │ ├── 422.html.erb │ │ ├── 500.html.erb │ │ └── 503.html.erb │ ├── images │ │ ├── blue_green_bg.jpg │ │ ├── gates_red.jpg │ │ ├── ljaf_color_tagline_transp.gif │ │ ├── openstax-logo-white.png │ │ ├── openstax-logo-white.svg │ │ ├── openstax-tutor-beta-logo.svg │ │ ├── ost-hero.jpg │ │ ├── ost-horiz-tm-logo.svg │ │ ├── rice-logo.png │ │ ├── student-onboarding │ │ │ ├── key.svg │ │ │ ├── payment-info.svg │ │ │ └── student-id.svg │ │ └── tutor-background.svg │ ├── javascripts │ │ ├── admin.js │ │ ├── application.js │ │ ├── course_search.js │ │ ├── customer_service.js │ │ ├── home.js │ │ ├── home │ │ │ ├── html5shiv.js │ │ │ ├── jquery.localscroll-1.2.7-min.js │ │ │ ├── jquery.mixitup.min.js │ │ │ ├── jquery.scrollTo.js │ │ │ ├── main.js │ │ │ └── respond.js │ │ ├── manager.js │ │ ├── payments │ │ │ ├── api-stub.js │ │ │ └── stub.js │ │ ├── research.js │ │ └── ui.js.coffee │ └── stylesheets │ │ ├── _mixins.scss │ │ ├── admin.scss │ │ ├── admin_navbar.scss │ │ ├── alt_signup.scss │ │ ├── application.scss │ │ ├── browser_upgrade.scss │ │ ├── common_colors.scss │ │ ├── customer_service.scss │ │ ├── enroll.scss │ │ ├── error_card.scss │ │ ├── fonts.scss │ │ ├── global.scss │ │ ├── home.scss │ │ ├── launch.scss │ │ ├── manager.scss │ │ ├── minimal_error.scss │ │ ├── payments │ │ └── stub.css │ │ ├── research.scss │ │ ├── research │ │ └── survey-preview.scss │ │ ├── syntax_highlight.scss │ │ └── vendor │ │ └── picnic.css ├── controllers │ ├── admin │ │ ├── base_controller.rb │ │ ├── catalog_offerings_controller.rb │ │ ├── console_controller.rb │ │ ├── courses_controller.rb │ │ ├── demo_controller.rb │ │ ├── districts_controller.rb │ │ ├── ecosystems_controller.rb │ │ ├── jobs_controller.rb │ │ ├── notifications_controller.rb │ │ ├── payments_controller.rb │ │ ├── periods_controller.rb │ │ ├── research_data_controller.rb │ │ ├── salesforce_controller.rb │ │ ├── schools_controller.rb │ │ ├── students_controller.rb │ │ ├── tags_controller.rb │ │ ├── targeted_contracts_controller.rb │ │ ├── teachers_controller.rb │ │ ├── tests_controller.rb │ │ └── users_controller.rb │ ├── api │ │ └── v1 │ │ │ ├── api_controller.rb │ │ │ ├── course_exercises_controller.rb │ │ │ ├── courses_controller.rb │ │ │ ├── demo_controller.rb │ │ │ ├── ecosystems_controller.rb │ │ │ ├── enrollment_controller.rb │ │ │ ├── fake_purchase_actions.rb │ │ │ ├── grading_templates_controller.rb │ │ │ ├── guides_controller.rb │ │ │ ├── jobs_controller.rb │ │ │ ├── lms │ │ │ └── courses_controller.rb │ │ │ ├── log_controller.rb │ │ │ ├── notes_controller.rb │ │ │ ├── offerings_controller.rb │ │ │ ├── pages_controller.rb │ │ │ ├── payment_codes_controller.rb │ │ │ ├── performance_reports_controller.rb │ │ │ ├── periods_controller.rb │ │ │ ├── practice_questions_controller.rb │ │ │ ├── practices_controller.rb │ │ │ ├── purchases_controller.rb │ │ │ ├── research_surveys_controller.rb │ │ │ ├── storage_controller.rb │ │ │ ├── students_controller.rb │ │ │ ├── task_plans_controller.rb │ │ │ ├── task_steps_controller.rb │ │ │ ├── tasking_plans_controller.rb │ │ │ ├── tasks_controller.rb │ │ │ ├── teachers_controller.rb │ │ │ ├── terms_controller.rb │ │ │ ├── updates_controller.rb │ │ │ └── users_controller.rb │ ├── application_controller.rb │ ├── content_analyst │ │ ├── base_controller.rb │ │ ├── console_controller.rb │ │ ├── ecosystems_controller.rb │ │ └── jobs_controller.rb │ ├── courses_controller.rb │ ├── customer_service │ │ ├── base_controller.rb │ │ ├── console_controller.rb │ │ ├── courses_controller.rb │ │ ├── ecosystems_controller.rb │ │ ├── jobs_controller.rb │ │ ├── students_controller.rb │ │ ├── tags_controller.rb │ │ └── users_controller.rb │ ├── lms_controller.rb │ ├── manager │ │ ├── course_details.rb │ │ ├── ecosystems_actions.rb │ │ ├── job_actions.rb │ │ ├── search_users.rb │ │ └── student_actions.rb │ ├── pardot_controller.rb │ ├── purchases_controller.rb │ ├── research │ │ ├── base_controller.rb │ │ ├── cohorts_controller.rb │ │ ├── console_controller.rb │ │ ├── studies_controller.rb │ │ ├── study_courses_controller.rb │ │ └── survey_plans_controller.rb │ ├── short_codes_controller.rb │ ├── static_pages_controller.rb │ ├── terms_controller.rb │ └── webview_controller.rb ├── handlers │ ├── admin │ │ ├── catalog_offering_create.rb │ │ ├── catalog_offering_update.rb │ │ ├── courses_create.rb │ │ ├── courses_destroy.rb │ │ ├── courses_update.rb │ │ ├── districts_create.rb │ │ ├── districts_destroy.rb │ │ ├── districts_update.rb │ │ ├── schools_create.rb │ │ ├── schools_destroy.rb │ │ ├── schools_update.rb │ │ ├── targeted_contracts_create.rb │ │ ├── users_create.rb │ │ └── users_update.rb │ └── courses_teach.rb ├── helpers │ ├── application_helper.rb │ ├── salesforce_helper.rb │ └── webview_helper.rb ├── javascript │ ├── images │ │ └── homepage │ │ │ ├── OST_color.png │ │ │ ├── OST_dark.png │ │ │ ├── OST_light.png │ │ │ ├── add_edit.jpg │ │ │ ├── add_edit@2x.jpg │ │ │ ├── books │ │ │ ├── american-government-2e.png │ │ │ ├── american-government-3e.png │ │ │ ├── anatomy-physiology.png │ │ │ ├── biology-2e.png │ │ │ ├── biology-for-ap-courses.png │ │ │ ├── college-physics-for-ap-courses.png │ │ │ ├── college-physics.png │ │ │ ├── entrepreneurship.png │ │ │ ├── introduction-to-sociology-2e.png │ │ │ ├── introduction-to-sociology-3e.png │ │ │ ├── life-liberty-and-the-pursuit-of-happiness.png │ │ │ ├── psychology-2e.png │ │ │ └── us-history.png │ │ │ ├── new_subjects.jpg │ │ │ ├── rice_logo_dark.png │ │ │ └── rice_logo_light.png │ ├── packs │ │ ├── application.js │ │ ├── carousel.js │ │ └── homepage.js │ └── styles │ │ └── homepage.scss ├── jobs │ ├── application_job.rb │ └── lev_job_returning_job.rb ├── models │ ├── application_record.rb │ ├── demo │ │ └── mash.rb │ ├── entity │ │ └── role.rb │ ├── environment.rb │ ├── indestructible_record.rb │ ├── payment_code.rb │ ├── purchased_item.rb │ ├── ratings │ │ ├── exercise_group_book_part.rb │ │ ├── period_book_part.rb │ │ └── role_book_part.rb │ └── settings │ │ ├── db.rb │ │ └── redis.rb ├── representers │ └── api │ │ └── v1 │ │ ├── admin │ │ ├── user_representer.rb │ │ └── user_search_representer.rb │ │ ├── approve_enrollment_change_representer.rb │ │ ├── book_part_toc_representer.rb │ │ ├── book_toc_representer.rb │ │ ├── book_tocs_representer.rb │ │ ├── bootstrap_data_representer.rb │ │ ├── clue_representer.rb │ │ ├── course_clone_representer.rb │ │ ├── course_enrollments_representer.rb │ │ ├── course_guide_child_representer.rb │ │ ├── course_guide_period_representer.rb │ │ ├── course_representer.rb │ │ ├── courses │ │ └── dashboard_representer.rb │ │ ├── courses_representer.rb │ │ ├── demo │ │ ├── all_representer.rb │ │ ├── assign │ │ │ ├── course │ │ │ │ ├── representer.rb │ │ │ │ └── task_plan │ │ │ │ │ ├── assigned_to_representer.rb │ │ │ │ │ ├── dropped_question_representer.rb │ │ │ │ │ ├── exercise_representer.rb │ │ │ │ │ └── representer.rb │ │ │ └── representer.rb │ │ ├── base_representer.rb │ │ ├── catalog_offering_representer.rb │ │ ├── course │ │ │ ├── course_representer.rb │ │ │ ├── period_representer.rb │ │ │ ├── representer.rb │ │ │ └── user_representer.rb │ │ ├── course_representer.rb │ │ ├── import │ │ │ ├── book │ │ │ │ ├── reading_processing_instruction_representer.rb │ │ │ │ └── representer.rb │ │ │ ├── catalog_offering_representer.rb │ │ │ └── representer.rb │ │ ├── period_representer.rb │ │ ├── task_plan_representer.rb │ │ ├── user_representer.rb │ │ ├── users │ │ │ └── representer.rb │ │ └── work │ │ │ ├── course │ │ │ ├── representer.rb │ │ │ └── task_plan │ │ │ │ ├── representer.rb │ │ │ │ └── task_representer.rb │ │ │ └── representer.rb │ │ ├── ecosystem_book_representer.rb │ │ ├── ecosystem_representer.rb │ │ ├── ecosystems_representer.rb │ │ ├── enrollment │ │ └── period_with_course_representer.rb │ │ ├── enrollment_change_representer.rb │ │ ├── error_representer.rb │ │ ├── exercise_image_representer.rb │ │ ├── exercise_representer.rb │ │ ├── exercise_search_representer.rb │ │ ├── exercises_representer.rb │ │ ├── grading_template_representer.rb │ │ ├── grading_template_search_representer.rb │ │ ├── highlight_representer.rb │ │ ├── job_representer.rb │ │ ├── jobs_representer.rb │ │ ├── lms │ │ └── linking_representer.rb │ │ ├── log_entries_representer.rb │ │ ├── log_entry_representer.rb │ │ ├── new_enrollment_change_representer.rb │ │ ├── note_representer.rb │ │ ├── notes_representer.rb │ │ ├── notification_representer.rb │ │ ├── offering_representer.rb │ │ ├── offering_search_representer.rb │ │ ├── page_representer.rb │ │ ├── performance_report │ │ ├── export_representer.rb │ │ ├── exports_representer.rb │ │ ├── period_representer.rb │ │ ├── representer.rb │ │ └── student │ │ │ ├── data │ │ │ ├── heading_representer.rb │ │ │ ├── null_representer.rb │ │ │ └── representer.rb │ │ │ └── representer.rb │ │ ├── period_representer.rb │ │ ├── practice_question_representer.rb │ │ ├── practice_questions_representer.rb │ │ ├── practice_representer.rb │ │ ├── rails_collection_setter.rb │ │ ├── refresh_representer.rb │ │ ├── related_content_representer.rb │ │ ├── research_survey_representer.rb │ │ ├── role_representer.rb │ │ ├── roster_representer.rb │ │ ├── shared_role_representer.rb │ │ ├── snap_lab_representer.rb │ │ ├── student_representer.rb │ │ ├── student_self_update_representer.rb │ │ ├── student_teacher_update_representer.rb │ │ ├── tag_representer.rb │ │ ├── task_plan │ │ ├── dropped_question_representer.rb │ │ ├── extension_representer.rb │ │ ├── representer.rb │ │ ├── scores │ │ │ ├── question_heading_representer.rb │ │ │ ├── representer.rb │ │ │ ├── student_question_representer.rb │ │ │ ├── student_representer.rb │ │ │ └── tasking_plan_representer.rb │ │ ├── search_representer.rb │ │ ├── stats_representer.rb │ │ └── tasking_plan_representer.rb │ │ ├── task_representer.rb │ │ ├── task_step_representer.rb │ │ ├── tasked_representer_mapper.rb │ │ ├── tasks │ │ ├── stats_representer.rb │ │ ├── task_step_representer.rb │ │ ├── tasked_exercise_grading_representer.rb │ │ ├── tasked_exercise_representer.rb │ │ ├── tasked_external_url_representer.rb │ │ ├── tasked_interactive_representer.rb │ │ ├── tasked_placeholder_representer.rb │ │ ├── tasked_reading_representer.rb │ │ └── tasked_video_representer.rb │ │ ├── teacher_course_guide_representer.rb │ │ ├── teacher_representer.rb │ │ ├── teacher_student_representer.rb │ │ ├── teachers_representer.rb │ │ ├── term_year_representer.rb │ │ ├── ui_settings_representer.rb │ │ ├── updates_representer.rb │ │ ├── user_profile_representer.rb │ │ └── user_representer.rb ├── routines │ ├── add_ecosystem_to_course.rb │ ├── add_spy_info.rb │ ├── add_user_as_course_teacher.rb │ ├── add_user_as_period_student.rb │ ├── background_migrate.rb │ ├── build_teacher_exercise_content_hash.rb │ ├── calculate_clue.rb │ ├── calculate_task_plan_scores.rb │ ├── calculate_task_stats.rb │ ├── choose_course_role.rb │ ├── choose_exercises.rb │ ├── clone_course.rb │ ├── collect_course_info.rb │ ├── collect_jobs_data.rb │ ├── concerns │ │ ├── clue_merger.rb │ │ └── find_or_create_practice_task_routine.rb │ ├── copy_teacher_exercises.rb │ ├── create_course.rb │ ├── create_or_claim_course.rb │ ├── create_or_reset_teacher_student.rb │ ├── create_period.rb │ ├── create_teacher_exercise.rb │ ├── dashboard_routine_methods.rb │ ├── delete_teacher_exercise.rb │ ├── demo │ │ ├── all.rb │ │ ├── assign.rb │ │ ├── base.rb │ │ ├── course.rb │ │ ├── export.rb │ │ ├── import.rb │ │ ├── users.rb │ │ └── work.rb │ ├── distribute_tasks.rb │ ├── does_tasking_exist.rb │ ├── export_and_upload_research_data.rb │ ├── export_exercise_exclusions.rb │ ├── export_users_info_to_match_with_consent_forms.rb │ ├── fetch_and_import_book_and_create_ecosystem.rb │ ├── filter_excluded_exercises.rb │ ├── find_or_create_practice_saved_task.rb │ ├── find_or_create_practice_specific_topics_task.rb │ ├── find_or_create_practice_worst_topics_task.rb │ ├── generate_payment_code_report.rb │ ├── generate_payment_codes.rb │ ├── get_course_ecosystem.rb │ ├── get_course_ecosystems_map.rb │ ├── get_course_roster.rb │ ├── get_course_teachers.rb │ ├── get_dashboard.rb │ ├── get_exercises.rb │ ├── get_page_exercise_ids_by_pool_types.rb │ ├── get_practice_question_exercises.rb │ ├── get_salesforce_book_names.rb │ ├── get_student_guide.rb │ ├── get_task_core_page_ids.rb │ ├── get_teacher_guide.rb │ ├── get_user_course_roles.rb │ ├── get_user_courses.rb │ ├── get_user_terms_infos.rb │ ├── individualize_tasking_plans.rb │ ├── mark_task_step_completed.rb │ ├── move_student.rb │ ├── populate_preview_course_content.rb │ ├── preview │ │ ├── answer_exercise.rb │ │ └── work_task.rb │ ├── push_salesforce_course_stats.rb │ ├── ratings │ │ ├── calculate_g_and_e.rb │ │ ├── concerns │ │ │ └── rating_jobs.rb │ │ ├── update_glicko.rb │ │ ├── update_period_book_parts.rb │ │ └── update_role_book_parts.rb │ ├── reassign_published_period_task_plans.rb │ ├── redeem_payment_code.rb │ ├── refund_payment.rb │ ├── search_courses.rb │ ├── secure_random_token_generator.rb │ ├── task_exercise.rb │ ├── track_tutor_onboarding_event.rb │ ├── update_assigned_exercise_version.rb │ ├── update_course.rb │ ├── update_payment_status.rb │ ├── update_task_plan_ecosystem.rb │ ├── user_is_course_student.rb │ ├── user_is_course_teacher.rb │ └── work_preview_course_tasks.rb ├── subsystems │ ├── catalog │ │ ├── create_offering.rb │ │ ├── models │ │ │ └── offering.rb │ │ └── update_offering.rb │ ├── content │ │ ├── book.rb │ │ ├── book_part.rb │ │ ├── chapter.rb │ │ ├── delete_ecosystem.rb │ │ ├── import_book.rb │ │ ├── list_ecosystems.rb │ │ ├── manifest.rb │ │ ├── manifest │ │ │ └── book.rb │ │ ├── map.rb │ │ ├── map_invalid_error.rb │ │ ├── models │ │ │ ├── book.rb │ │ │ ├── ecosystem.rb │ │ │ ├── exercise.rb │ │ │ ├── exercise_tag.rb │ │ │ ├── lo_teks_tag.rb │ │ │ ├── map.rb │ │ │ ├── note.rb │ │ │ ├── page.rb │ │ │ ├── page_tag.rb │ │ │ └── tag.rb │ │ ├── page.rb │ │ ├── question.rb │ │ ├── routines │ │ │ ├── import_exercises.rb │ │ │ ├── import_page.rb │ │ │ ├── populate_exercise_pools.rb │ │ │ ├── remap_practice_question_exercises.rb │ │ │ ├── remap_teacher_exercises.rb │ │ │ ├── tag_resource.rb │ │ │ └── transform_and_cache_page_content.rb │ │ ├── search_tags.rb │ │ ├── unit.rb │ │ └── upload_ecosystem_manifest_to_validator.rb │ ├── course_content │ │ ├── add_ecosystem_to_course.rb │ │ ├── get_course_ecosystems.rb │ │ ├── models │ │ │ ├── course_ecosystem.rb │ │ │ └── excluded_exercise.rb │ │ └── update_exercise_exclusions.rb │ ├── course_membership │ │ ├── activate_student.rb │ │ ├── add_enrollment.rb │ │ ├── add_student.rb │ │ ├── add_teacher.rb │ │ ├── add_teacher_student.rb │ │ ├── archive_period.rb │ │ ├── create_enrollment_change.rb │ │ ├── create_period.rb │ │ ├── get_course_periods.rb │ │ ├── get_course_roles.rb │ │ ├── get_course_teacher_roles.rb │ │ ├── get_period.rb │ │ ├── get_period_student_roles.rb │ │ ├── get_role_courses.rb │ │ ├── get_teachers.rb │ │ ├── inactivate_student.rb │ │ ├── is_course_student.rb │ │ ├── is_course_teacher.rb │ │ ├── models │ │ │ ├── enrollment.rb │ │ │ ├── enrollment_change.rb │ │ │ ├── period.rb │ │ │ ├── student.rb │ │ │ ├── teacher.rb │ │ │ └── teacher_student.rb │ │ ├── process_enrollment_change.rb │ │ ├── unarchive_period.rb │ │ ├── update_period.rb │ │ └── validate_enrollment_parameters.rb │ ├── course_profile │ │ ├── build_preview_courses.rb │ │ ├── claim_preview_course.rb │ │ ├── create_course.rb │ │ ├── mark_course_enrolled.rb │ │ ├── models │ │ │ ├── cache.rb │ │ │ └── course.rb │ │ └── update_course.rb │ ├── legal │ │ ├── create_targeted_contract.rb │ │ ├── destroy_targeted_contract.rb │ │ ├── forget_about.rb │ │ ├── get_contract_names.rb │ │ ├── get_targeted_contracts.rb │ │ ├── models │ │ │ └── targeted_contract.rb │ │ ├── targeted_contract.rb │ │ └── utils.rb │ ├── lms │ │ ├── launch.rb │ │ ├── models │ │ │ ├── app.rb │ │ │ ├── context.rb │ │ │ ├── course_score_callback.rb │ │ │ ├── nonce.rb │ │ │ ├── stubbed_app.rb │ │ │ ├── tool_consumer.rb │ │ │ ├── trusted_launch_data.rb │ │ │ └── user.rb │ │ ├── outcome_response.rb │ │ ├── pair_launch_to_course.rb │ │ ├── queries.rb │ │ ├── remove_last_course_pairing.rb │ │ ├── send_course_scores.rb │ │ └── willo_labs.rb │ ├── research │ │ ├── activate_study.rb │ │ ├── add_course_to_study.rb │ │ ├── admit_students_to_studies.rb │ │ ├── assign_missing_surveys.rb │ │ ├── cohort_membership_manager.rb │ │ ├── complete_survey.rb │ │ ├── create_cohort.rb │ │ ├── deactivate_study.rb │ │ ├── export_and_upload_survey_data.rb │ │ ├── hide_survey_plan.rb │ │ ├── models │ │ │ ├── cohort.rb │ │ │ ├── cohort_member.rb │ │ │ ├── study.rb │ │ │ ├── study_course.rb │ │ │ ├── survey.rb │ │ │ └── survey_plan.rb │ │ ├── publish_survey_plan.rb │ │ ├── reassign_members.rb │ │ ├── remove_course_from_study.rb │ │ ├── unhide_survey_plan.rb │ │ └── update_study_activations.rb │ ├── role │ │ ├── create_user_role.rb │ │ └── get_default_user_role.rb │ ├── school_district │ │ ├── create_district.rb │ │ ├── create_school.rb │ │ ├── delete_district.rb │ │ ├── delete_school.rb │ │ ├── get_district.rb │ │ ├── get_school.rb │ │ ├── list_districts.rb │ │ ├── list_schools.rb │ │ ├── models │ │ │ ├── district.rb │ │ │ └── school.rb │ │ ├── update_district.rb │ │ └── update_school.rb │ ├── short_code │ │ ├── create.rb │ │ ├── find_short_code.rb │ │ ├── get_short_code_url.rb │ │ ├── models │ │ │ └── short_code.rb │ │ ├── short_code_redirect.rb │ │ └── url_for.rb │ ├── tasks │ │ ├── assign_coursewide_task_plans_to_new_period.rb │ │ ├── assistants │ │ │ ├── README.md │ │ │ ├── event_assistant.rb │ │ │ ├── external_assignment_assistant.rb │ │ │ ├── fragment_assistant.rb │ │ │ ├── generic_assistant.rb │ │ │ ├── homework_assistant.rb │ │ │ └── i_reading_assistant.rb │ │ ├── close_practice_task.rb │ │ ├── create_course_assistants.rb │ │ ├── create_tasking.rb │ │ ├── does_tasking_exist.rb │ │ ├── export_performance_report.rb │ │ ├── fetch_assignment_pes.rb │ │ ├── fetch_assignment_spes.rb │ │ ├── fetch_practice_worst_areas_exercises.rb │ │ ├── freeze_ended_course_teacher_performance_reports.rb │ │ ├── get_assigned_exercises.rb │ │ ├── get_assistant.rb │ │ ├── get_performance_report.rb │ │ ├── get_performance_report_exports.rb │ │ ├── get_practice_task.rb │ │ ├── get_redirect_url.rb │ │ ├── get_task_plans.rb │ │ ├── get_tasks.rb │ │ ├── models │ │ │ ├── assistant.rb │ │ │ ├── concept_coach_task.rb │ │ │ ├── course_assistant.rb │ │ │ ├── dropped_question.rb │ │ │ ├── extension.rb │ │ │ ├── grading_template.rb │ │ │ ├── performance_report_export.rb │ │ │ ├── practice_question.rb │ │ │ ├── previous_attempt.rb │ │ │ ├── task.rb │ │ │ ├── task_plan.rb │ │ │ ├── task_step.rb │ │ │ ├── tasked_exercise.rb │ │ │ ├── tasked_external_url.rb │ │ │ ├── tasked_interactive.rb │ │ │ ├── tasked_placeholder.rb │ │ │ ├── tasked_reading.rb │ │ │ ├── tasked_video.rb │ │ │ ├── tasking.rb │ │ │ └── tasking_plan.rb │ │ ├── performance_report │ │ │ ├── export_cc_xlsx.rb │ │ │ ├── export_pre_wrm_xlsx.rb │ │ │ └── export_xlsx.rb │ │ ├── populate_placeholder_steps.rb │ │ ├── update_task_caches.rb │ │ └── update_task_plan_caches.rb │ └── user │ │ ├── find_or_create_user.rb │ │ ├── make_administrator.rb │ │ ├── models │ │ ├── administrator.rb │ │ ├── anonymous_author_profile.rb │ │ ├── anonymous_profile.rb │ │ ├── content_analyst.rb │ │ ├── customer_service.rb │ │ ├── openstax_profile.rb │ │ ├── profile.rb │ │ ├── researcher.rb │ │ ├── suggestion.rb │ │ ├── tour.rb │ │ └── tour_view.rb │ │ ├── record_tour_view.rb │ │ ├── search_users.rb │ │ ├── set_administrator_state.rb │ │ ├── set_content_analyst_state.rb │ │ ├── set_customer_support_state.rb │ │ └── set_researcher_state.rb ├── uploaders │ └── export_uploader.rb └── views │ ├── admin │ ├── catalog_offerings │ │ ├── _display_row.html.erb │ │ ├── _edit_row.html.erb │ │ ├── edit.html.erb │ │ └── index.html.erb │ ├── console │ │ └── index.html.erb │ ├── courses │ │ ├── _form.html.erb │ │ ├── _periods.html.erb │ │ ├── _roster.html.erb │ │ ├── _salesforce.html.erb │ │ ├── _set_ecosystem.html.erb │ │ ├── _teachers.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ └── new.html.erb │ ├── demo │ │ ├── _catalog_offering.html.erb │ │ ├── _course.html.erb │ │ ├── _form_collection.html.erb │ │ ├── _form_collection_item.html.erb │ │ ├── _period.html.erb │ │ ├── _string.html.erb │ │ ├── _task_plan.html.erb │ │ ├── _user.html.erb │ │ ├── assign │ │ │ ├── _fields.html.erb │ │ │ └── task_plan │ │ │ │ ├── _assigned_to.html.erb │ │ │ │ └── _fields.html.erb │ │ ├── course │ │ │ ├── _fields.html.erb │ │ │ └── _period.html.erb │ │ ├── form.html.erb │ │ ├── import │ │ │ ├── _fields.html.erb │ │ │ └── _reading_processing_instruction.html.erb │ │ ├── index.html.erb │ │ ├── users │ │ │ ├── _fields.html.erb │ │ │ └── _user.html.erb │ │ └── work │ │ │ ├── _fields.html.erb │ │ │ └── task_plan │ │ │ ├── _fields.html.erb │ │ │ └── _task.html.erb │ ├── districts │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ └── new.html.erb │ ├── ecosystems │ │ ├── index.html.erb │ │ └── new.html.erb │ ├── notifications │ │ ├── _index.html.erb │ │ └── index.html.erb │ ├── payments │ │ └── index.html.erb │ ├── periods │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── research_data │ │ └── index.html.erb │ ├── salesforce │ │ ├── _course.html.erb │ │ └── failures.html.erb │ ├── schools │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ └── new.html.erb │ ├── tags │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ └── index.html.erb │ ├── targeted_contracts │ │ ├── _form.html.erb │ │ ├── index.html.erb │ │ └── new.html.erb │ ├── tests │ │ ├── launch_iframe.html.erb │ │ ├── minimal_error.html.erb │ │ └── minimal_error_iframe.html.erb │ └── users │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── info.html.erb │ │ └── new.html.erb │ ├── auth │ ├── logout.html.erb │ └── popup.html.erb │ ├── content_analyst │ ├── console │ │ └── index.html.erb │ └── ecosystems │ │ └── index.html.erb │ ├── customer_service │ ├── console │ │ └── index.html.erb │ ├── courses │ │ ├── index.html.erb │ │ └── show.html.erb │ ├── ecosystems │ │ └── index.html.erb │ ├── tags │ │ └── index.html.erb │ └── users │ │ ├── index.html.erb │ │ └── info.html.erb │ ├── layouts │ ├── _authentication.html.erb │ ├── _bootstrap_tab_activate_on_load.html.erb │ ├── _copyright_terms.html.erb │ ├── _error_card.html.erb │ ├── _flash_messages.html.erb │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _meta.html.erb │ ├── admin.html.erb │ ├── application.html.erb │ ├── application_api_docs.html.erb │ ├── content_analyst.html.erb │ ├── customer_service.html.erb │ ├── mailer.html.erb │ ├── minimal_error.html.erb │ ├── research.html.erb │ ├── static_error.html.erb │ └── webview.html.erb │ ├── lms │ ├── ci_launch.html.erb │ ├── configuration.xml.erb │ ├── fail_app_not_found.html.erb │ ├── fail_catchall.html.erb │ ├── fail_could_not_load_launch.html.erb │ ├── fail_course_ended.html.erb │ ├── fail_course_score_in_use.html.erb │ ├── fail_expired_timestamp.html.erb │ ├── fail_invalid_signature.html.erb │ ├── fail_invalid_timestamp.html.erb │ ├── fail_lms_disabled.html.erb │ ├── fail_missing_required_fields.html.erb │ ├── fail_nonce_already_used.html.erb │ ├── fail_unpaired.html.erb │ ├── fail_unsupported_role.html.erb │ ├── launch.html.erb │ └── pair.html.erb │ ├── manager │ ├── courses │ │ ├── _index.html.erb │ │ ├── _jobs.html.erb │ │ ├── _search.html.erb │ │ └── _tabs.html.erb │ ├── ecosystems │ │ ├── _import.html.erb │ │ ├── _index.html.erb │ │ ├── _jobs.html.erb │ │ └── _listing.html.erb │ ├── jobs │ │ ├── index.html.erb │ │ └── show.html.erb │ ├── periods │ │ └── _index.html.erb │ ├── students │ │ └── index.html.erb │ ├── tags │ │ └── _index.html.erb │ ├── teachers │ │ └── _index.html.erb │ └── users │ │ ├── _index.html.erb │ │ └── _info.html.erb │ ├── research │ ├── cohorts │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── members.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ ├── console │ │ ├── help.html.erb │ │ └── index.html.erb │ ├── studies │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ └── show.html.erb │ └── survey_plans │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── index.html.erb │ │ ├── new.html.erb │ │ ├── preview.html.erb │ │ └── show.html.erb │ ├── shared │ └── courses │ │ ├── _search.html.erb │ │ └── _search_help.html.erb │ ├── static_pages │ ├── browser_upgrade.html.erb │ ├── copyright.html.erb │ ├── generic_error.html.erb │ ├── signup.html.erb │ ├── stubbed_payments.html.erb │ └── terms.html.erb │ ├── terms │ ├── index.html.erb │ └── pose.html.erb │ ├── warning_mailer │ └── warning.html.erb │ └── webview │ ├── _google_analytics.html.erb │ ├── _pardot.html.erb │ ├── enroll.html.erb │ ├── home.html.erb │ └── index.html.erb ├── babel.config.js ├── bin ├── bundle ├── delayed_job_worker_pool ├── puma ├── pumactl ├── rails ├── rake ├── rspec ├── setup ├── spring ├── update ├── webpack ├── webpack-dev-server ├── whenever ├── wheneverize └── yarn ├── cd.jenkins ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── delayed_job_worker_pool.rb ├── demo │ ├── review │ │ ├── assign │ │ │ ├── _assign.yml.erb │ │ │ ├── bio.yml.erb │ │ │ ├── mini_1.yml.erb │ │ │ ├── mini_2.yml.erb │ │ │ ├── phys.yml.erb │ │ │ └── soc.yml.erb │ │ ├── course │ │ │ ├── _course.yml │ │ │ ├── bio.yml.erb │ │ │ ├── mini_1.yml │ │ │ ├── mini_2.yml │ │ │ ├── phys.yml.erb │ │ │ └── soc.yml.erb │ │ ├── import │ │ │ ├── bio.yml │ │ │ ├── mini_1.yml │ │ │ ├── mini_2.yml │ │ │ ├── phys.yml │ │ │ └── soc.yml │ │ ├── users │ │ │ ├── _users.yml │ │ │ ├── bio.yml.erb │ │ │ ├── mini_1.yml │ │ │ ├── mini_2.yml │ │ │ ├── phys.yml.erb │ │ │ └── soc.yml.erb │ │ └── work │ │ │ ├── _in_progress.yml │ │ │ ├── _in_progress_even.yml │ │ │ ├── _in_progress_odd.yml │ │ │ ├── _not_started.yml │ │ │ ├── _not_started_even.yml │ │ │ ├── _not_started_odd.yml │ │ │ ├── _review_work.yml.erb │ │ │ ├── bio.yml.erb │ │ │ ├── mini_1.yml.erb │ │ │ ├── mini_2.yml.erb │ │ │ ├── phys.yml.erb │ │ │ └── soc.yml.erb │ └── test │ │ ├── import │ │ └── ap_phys.yml │ │ └── users │ │ ├── staff │ │ ├── administrators.yml │ │ ├── content_analysts.yml │ │ ├── customer_support.yml │ │ └── researchers.yml │ │ ├── students.yml.erb │ │ └── teachers.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── homepage │ └── home.yml ├── initializers │ ├── 00-subsystems.rb │ ├── 01-openstax_exercises.rb │ ├── access_policies.rb │ ├── active_force.rb │ ├── active_job.rb │ ├── active_storage.rb │ ├── apipie.rb │ ├── application_controller_renderer.rb │ ├── ar_sequence.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── browser_support.rb │ ├── carrierwave.rb │ ├── content_security_policy.rb │ ├── controllers.rb │ ├── cookies_serializer.rb │ ├── delayed_job.rb │ ├── delayed_job_heartbeat.rb │ ├── doorkeeper.rb │ ├── filter_parameter_logging.rb │ ├── fine_print.rb │ ├── homepage_config.rb │ ├── inflections.rb │ ├── jobba.rb │ ├── lev.rb │ ├── mime_types.rb │ ├── openstax_accounts.rb │ ├── openstax_api.rb │ ├── openstax_content.rb │ ├── openstax_payments.rb │ ├── openstax_salesforce.rb │ ├── openstax_utilities.rb │ ├── openstax_validator.rb │ ├── pry.rb │ ├── rails_settings_ui.rb │ ├── rescue_from.rb │ ├── sentry.rb │ ├── session_store.rb │ ├── trace_activerecord_queries.rb │ ├── transaction_retry.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── reading_processing_instructions.yml ├── routes.rb ├── schedule.rb ├── scout_apm.yml ├── secrets.yml ├── spring.rb ├── storage.yml ├── webpack │ ├── development.js │ ├── environment.js │ ├── production.js │ └── test.js └── webpacker.yml ├── db ├── background_migrate │ ├── 20181112190157_set_core_and_personalized_placeholder_exercise_steps_counts.rb │ ├── 20190918184315_set_tasks_task_steps_is_core.rb │ ├── 20191205182525_generate_stats.rb │ ├── 20200330145448_remove_duplicate_content_from_taskeds.rb │ ├── 20200515140524_initialize_glicko_ratings.rb │ ├── 20200521193851_populate_existing_task_course_ids.rb │ ├── 20200617142820_cache_points_for_existing_tasks.rb │ ├── 20210915153151_set_value_for_null_attempt_numbers_in_background.rb │ └── 20211005155727_remove_serialized_nokogiri_nodes_in_background.rb ├── migrate │ ├── 00000_create_entity_roles.rb │ ├── 00001_create_entity_users.rb │ ├── 00003_create_entity_courses.rb │ ├── 00004_create_content_ecosystems.rb │ ├── 00005_create_content_books.rb │ ├── 00006_create_entity_tasks.rb │ ├── 00999_create_course_membership_periods.rb │ ├── 01000_create_course_membership_students.rb │ ├── 01001_create_course_membership_teachers.rb │ ├── 01100_create_role_role_users.rb │ ├── 01300_create_course_content_course_ecosystems.rb │ ├── 01400_create_tasks_taskings.rb │ ├── 01500_create_school_district_districts.rb │ ├── 01600_create_school_district_schools.rb │ ├── 01700_create_course_profile_profiles.rb │ ├── 20140724183731_create_doorkeeper_tables.rb │ ├── 20140724183932_install_fine_print.rb │ ├── 20140724184012_create_openstax_accounts_accounts.rb │ ├── 20140724184013_create_openstax_accounts_groups.openstax_accounts.rb │ ├── 20140724184014_create_openstax_accounts_group_members.openstax_accounts.rb │ ├── 20140724184015_create_openstax_accounts_group_owners.openstax_accounts.rb │ ├── 20140724184016_create_openstax_accounts_group_nestings.openstax_accounts.rb │ ├── 20140724184630_create_user_profile_profiles.rb │ ├── 20140724184800_create_user_profile_administrators.rb │ ├── 20140925185224_create_tasks_assistants.rb │ ├── 20140926165258_create_tasks_task_plans.rb │ ├── 20140926213212_create_tasks_tasks.rb │ ├── 20140927175504_create_tasks_tasked_readings.rb │ ├── 20141021205221_create_tasks_task_steps.rb │ ├── 20141103184649_create_tasks_tasking_plans.rb │ ├── 20141106215530_create_content_tags.rb │ ├── 20150204233802_create_content_chapters.rb │ ├── 20150204773636_create_content_pools.rb │ ├── 20150205180054_create_content_pages.rb │ ├── 20150212023636_create_content_exercises.rb │ ├── 20150212123636_create_tasks_tasked_exercises.rb │ ├── 20150212223636_create_tasks_course_assistants.rb │ ├── 20150218225356_create_content_page_tags.rb │ ├── 20150218225408_create_content_exercise_tags.rb │ ├── 20150319110230_create_tasks_tasked_videos.rb │ ├── 20150321164408_create_fake_stores.rb │ ├── 20150325170729_create_tasks_tasked_interactives.rb │ ├── 20150420184110_create_content_lo_teks.rb │ ├── 20150505184224_create_tasks_performance_report_exports.rb │ ├── 20150507224137_create_tasks_tasked_placeholder.rb │ ├── 20150708120910_create_tasks_tasked_external_urls.rb │ ├── 20150716164846_create_course_membership_enrollments.rb │ ├── 20150722052819_add_implicit_signatures.fine_print.rb │ ├── 20150804002154_create_legal_targeted_contracts.rb │ ├── 20150804002246_create_legal_targeted_contract_relationships.rb │ ├── 20150821001102_add_content_ecosystem_to_task_plans.rb │ ├── 20150826233318_add_task_plans_content_ecosystem_id_not_null_constraint.rb │ ├── 20150904230556_add_all_exercises_pool_to_chapters.rb │ ├── 20150904230607_add_all_exercises_pool_to_pages.rb │ ├── 20150921184614_create_content_analysts.rb │ ├── 20150921185020_rename_user_profile_administrators_profile_id_to_user_profile_profile_id.rb │ ├── 20150922200112_rename_user_profile_subsystem_to_user.rb │ ├── 20150923202225_add_task_spy_column.rb │ ├── 20150925164029_create_user_customer_services.rb │ ├── 20151008154744_add_enrollment_code_to_course_membership_periods.rb │ ├── 20151009223317_create_delayed_jobs.rb │ ├── 20151015214212_add_is_concept_coach_to_course_profile_profiles.rb │ ├── 20151016170440_create_catalog_offerings.rb │ ├── 20151020183551_create_course_membership_enrollment_changes.rb │ ├── 20151023175052_add_deleted_at_to_course_membership_periods.rb │ ├── 20151027161158_add_comments_to_content_ecosystems.rb │ ├── 20151028004919_create_salesforce_users.rb │ ├── 20151028163225_update_course_membership_periods_uniquness_index.rb │ ├── 20151029184944_add_correct_answer_id_to_tasks_tasked_exercises.rb │ ├── 20151029222530_create_tasks_concept_coach_tasks.rb │ ├── 20151030004409_add_default_course_name_to_catalog_offering.rb │ ├── 20151031032144_create_salesforce_attached_record.rb │ ├── 20151102161939_add_teacher_join_token_to_course_profile_profiles.rb │ ├── 20151103171830_add_student_identifier_to_course_membership_students.rb │ ├── 20151121171035_create_settings.rb │ ├── 20151124165753_add_role_to_tasks_concept_coach_tasks.rb │ ├── 20151201181256_add_deleted_at_to_course_membership_enrollments.rb │ ├── 20151201181307_add_deleted_at_to_course_membership_enrollment_changes.rb │ ├── 20151203205853_change_course_profile_catalog_offering_identifier_to_catalog_offering_id.rb │ ├── 20151203214237_rename_catalog_offerings_identifier_to_salesforce_book_name_and_add_appearance_code.rb │ ├── 20151203221809_add_appearance_code_to_course_profile_profiles.rb │ ├── 20151218012828_remove_unique_from_enrollment_changes_index_on_enrollment_id.rb │ ├── 20160112001557_drop_fake_stores.rb │ ├── 20160114232922_change_default_group_task_steps_to_recovery_group.rb │ ├── 20160122223539_rename_student_inactive_at_to_deleted_at.rb │ ├── 20160224233642_create_course_content_excluded_exercises.rb │ ├── 20160225203522_fix_content_exercise_answer_orders.rb │ ├── 20160225204521_fix_tasked_exercise_answer_orders.rb │ ├── 20160301235335_add_biglearn_excluded_pool_uuid_to_course_profiles_profiles.rb │ ├── 20160317184322_add_task_plan_feedback.rb │ ├── 20160331194837_add_reading_processing_instructions_to_content_books.rb │ ├── 20160411184043_rename_reading_try_another_pool_to_reading_context_pool.rb │ ├── 20160411214041_change_can_be_recovered_to_related_exercise_ids.rb │ ├── 20160416010537_add_multipart_fields_to_tasks_tasked_exercise.rb │ ├── 20160421021631_add_late_work_fields_to_tasks_tasks.rb │ ├── 20160421142200_add_default_times_to_courses_and_periods.rb │ ├── 20160427192800_create_short_code_short_codes.rb │ ├── 20160502205835_add_concept_coach_pool_to_content_pages.rb │ ├── 20160505010926_change_default_times_to_strings.rb │ ├── 20160505221319_add_defaults_for_null_counts.rb │ ├── 20160506181201_truncate_free_responses.rb │ ├── 20160512155201_add_explicit_late_counts_to_tasks_tasks.rb │ ├── 20160512214056_create_time_zones.rb │ ├── 20160512215252_change_timezone_to_time_zone_id.rb │ ├── 20160517202901_change_is_feedback_immediate_null_and_default.rb │ ├── 20160520002440_rename_teacher_join_token_teach_token.rb │ ├── 20160524141809_add_deleted_at_to_task_models.rb │ ├── 20160524142958_improve_deleted_at_indices.rb │ ├── 20160524152139_remove_entity_tasks.rb │ ├── 20160526202800_add_hidden_at_to_tasks_tasks.rb │ ├── 20160531190234_add_context_fields_and_flags_to_content_exercises.rb │ ├── 20160601225450_add_short_id_to_book_and_page.rb │ ├── 20160603151054_add_context_to_tasks_tasked_exercises.rb │ ├── 20160607195354_add_is_college_flag.rb │ ├── 20160609210637_create_content_maps.rb │ ├── 20160610210955_add_fragments_and_snap_labs_to_content_pages.rb │ ├── 20160614202715_drop_is_normally_college_from_catalog_offerings.rb │ ├── 20160623014118_add_first_and_last_published_at_to_task_plans.rb │ ├── 20160623015400_index_tasks_and_tasking_plans_on_opens_at_ntz.rb │ ├── 20160623231430_convert_serialized_fields_to_json.rb │ ├── 20160625040530_cache_content_page_fragments_and_snap_labs.rb │ ├── 20160702071446_fix_bad_foreign_keys_constraints.rb │ ├── 20160726045753_add_deleted_at_to_attached_records.rb │ ├── 20160802161104_add_unique_indices_to_school_district_models.rb │ ├── 20160802210108_add_missing_timestamps.rb │ ├── 20160808204103_add_profile_ui_settings.rb │ ├── 20160829164149_change_student_identifier_index.rb │ ├── 20160830235417_recalculate_task_step_counts.rb │ ├── 20160923180438_create_user_tours.rb │ ├── 20161011212156_add_faculty_status_to_accounts_accounts.openstax_accounts.rb │ ├── 20161017201959_rename_student_deidentifier_to_role_research_identifier.rb │ ├── 20161017233446_add_entity_teacher_student_role_id_to_periods.rb │ ├── 20161027183650_add_salesforce_contact_id_to_accounts_accounts.openstax_accounts.rb │ ├── 20161027220333_add_is_available_to_catalog_offerings.rb │ ├── 20161028232006_rename_course_profile_profiles_to_course_profile_courses.rb │ ├── 20161028232036_drop_entity_courses.rb │ ├── 20161028232045_add_start_at_and_end_at_to_courses.rb │ ├── 20161028232416_add_term_and_year_to_courses.rb │ ├── 20161031181743_add_cloned_from_id_to_courses_and_task_plans.rb │ ├── 20161101220241_add_is_demo_to_courses.rb │ ├── 20161104185157_change_accounts_openstax_uid_to_be_nullable.openstax_accounts.rb │ ├── 20161108152717_allow_duplicate_student_ids.rb │ ├── 20161129003301_add_conflicting_enrollment_id_to_enrollment_change.rb │ ├── 20161206181028_change_accounts_username_to_be_nullable.openstax_accounts.rb │ ├── 20161212211910_add_title_and_number_to_catalog_offerings.rb │ ├── 20170131204025_add_is_excluded_from_salesforce_to_courses.rb │ ├── 20170208182122_add_uuid_and_group_uuid_to_content_exercises.rb │ ├── 20170208193914_add_new_biglearn_fields.rb │ ├── 20170209195416_add_ecosystem_id_to_tasks_tasks.rb │ ├── 20170307230726_add_biglearn_algorithm_names_to_course_profile_courses.rb │ ├── 20170308183035_fix_content_ecosystem_titles.rb │ ├── 20170317165213_remove_personalized_placeholder_strategy_from_tasks_tasks.rb │ ├── 20170320194017_add_content_page_id_and_spy_and_completed_at_indices_and_remove_related_content_from_tasks_task_steps.rb │ ├── 20170321232019_add_spes_are_assigned_and_pes_are_assigned_to_tasks_tasks.rb │ ├── 20170323195331_course_trial_2_preview.rb │ ├── 20170412213150_add_is_test_to_course_profile_courses.rb │ ├── 20170422005136_create_lms_tables.rb │ ├── 20170425195217_add_task_plan_is_preview_flag.rb │ ├── 20170427231854_make_offering_sf_book_name_index_nonunique.rb │ ├── 20170428230057_add_payment_fields.rb │ ├── 20170504174702_add_estimated_students.rb │ ├── 20170509175648_install_openstax_salesforce.openstax_salesforce.rb │ ├── 20170509203010_switch_to_openstax_salesforce.rb │ ├── 20170516183727_add_uuid_and_role_to_accounts_accounts.openstax_accounts.rb │ ├── 20170518182025_preview_claimed.rb │ ├── 20170526133610_add_sequence_number_to_enrollments.rb │ ├── 20170609150609_set_payment_due_at_not_null.rb │ ├── 20170621215721_add_is_refund_pending_to_course_membership_students.rb │ ├── 20170714141120_add_is_preview_ready_to_course_profile_courses.rb │ ├── 20170715185155_set_default_uuids.rb │ ├── 20170719182041_student_refund_survey.rb │ ├── 20170724162437_allow_nil_is_college.rb │ ├── 20170731215454_cleanup_deleted_at_columns.rb │ ├── 20170822172715_create_lms_user.rb │ ├── 20170824032434_reorganize_lms_tables.rb │ ├── 20170826122030_create_lms_tables_2.rb │ ├── 20170905204817_change_fields_on_course_grade_callback.rb │ ├── 20170913171643_add_lms_enabled_fields_to_course.rb │ ├── 20170915024702_add_created_at_to_lms_trusted_launch_data.rb │ ├── 20170918164126_add_lti_id_course_index.rb │ ├── 20170926170721_add_course_is_access_switchable.rb │ ├── 20170927024042_rename_lms_course_grade_callbacks_to_use_score.rb │ ├── 20171002211007_add_last_lms_scores_push_job_id_to_course_profile_courses.rb │ ├── 20171005200431_add_lms_index_uniqueness.rb │ ├── 20171006150216_add_lms_index_uniqueness_2.rb │ ├── 20171009181536_add_index_to_lms_trusted_launch_data_created_at.rb │ ├── 20171009182057_add_missing_lms_timestamps.rb │ ├── 20171016185328_improve_tasks_tasks_task_type_index_for_research_export.rb │ ├── 20171025173835_add_resource_link_id_to_lms_course_score_callbacks.rb │ ├── 20171027141415_create_tasks_task_caches.rb │ ├── 20171027145335_remove_tasks_taskings_count_from_tasks_tasks.rb │ ├── 20171109210427_assign_missing_uuids_for_local_accounts.openstax_accounts.rb │ ├── 20171109215245_cache_existing_tasks.rb │ ├── 20171110155706_add_tasks_tasks_foreign_key_to_content_ecosystems.rb │ ├── 20171204162034_create_tasks_period_caches.rb │ ├── 20171212165309_prepend_r_to_research_identifiers.rb │ ├── 20171219012010_add_is_cached_for_period_to_tasks_task_caches.rb │ ├── 20171220165540_add_support_identifier_to_accounts_accounts.openstax_accounts.rb │ ├── 20171221002027_fix_existing_task_caches_again.rb │ ├── 20171221235805_add_is_test_to_accounts_accounts.openstax_accounts.rb │ ├── 20171222160223_add_fragment_index_to_tasks_task_steps.rb │ ├── 20171228164817_add_salesforce_fields_to_course.rb │ ├── 20171229173406_create_user_researchers.rb │ ├── 20171229201156_add_research_models.rb │ ├── 20180116223224_add_weights_to_course_profile_courses.rb │ ├── 20180123165516_fix_external_task_caches.rb │ ├── 20180222174206_reset_task_caches.rb │ ├── 20180223002900_add_question_index_to_tasks_tasked_exercises.rb │ ├── 20180223162905_fix_accepted_late_counts.rb │ ├── 20180228233811_add_school_type_to_accounts_accounts.openstax_accounts.rb │ ├── 20180309195712_add_period_id_to_students.rb │ ├── 20180430162014_remove_class_size.rb │ ├── 20180629000000_add_research_models_2.rb │ ├── 20180727154157_support_willo_labs.rb │ ├── 20180731170823_add_nickname_to_content_exercises.rb │ ├── 20180905223801_drop_salesforce_attached_records.rb │ ├── 20180912162358_add_research_cohorts_and_students.rb │ ├── 20180921183625_study_brains.rb │ ├── 20180928165828_add_owner_to_application.rb │ ├── 20180928165850_add_previous_refresh_token_to_access_tokens.rb │ ├── 20180928165907_add_confidential_to_applications.rb │ ├── 20180928165932_add_missing_doorkeeper_foreign_keys.rb │ ├── 20181018152101_research_manipulations.rb │ ├── 20181105162325_add_baked_flag_to_books.rb │ ├── 20181108003418_add_core_and_personalized_placeholder_exercise_steps_count_to_tasks.rb │ ├── 20181203172559_add_baked_book_location_columns.rb │ ├── 20190206210659_create_content_notes.rb │ ├── 20190215150450_lms_context_app_type.rb │ ├── 20190218222054_task_step_garbage_detect.rb │ ├── 20190328020455_rename_study_classes.rb │ ├── 20190331034130_rename_garbage_to_response.rb │ ├── 20190411181631_lti_course_multi_context.rb │ ├── 20190419202654_create_course_membership_teacher_students.rb │ ├── 20190426205807_drop_role_role_users.rb │ ├── 20190430164054_add_teacher_student_ids_to_tasks_task_caches.rb │ ├── 20190506190532_allow_null_biglearn_algorithm_names.rb │ ├── 20190515200717_update_settings.rb │ ├── 20190521225422_drop_legal_targeted_contract_relationships.rb │ ├── 20190618201206_add_uuid_and_book_location_indexes_to_content_pages.rb │ ├── 20190711001122_drop_cc_page_stats.rb │ ├── 20190918184534_add_is_core_to_tasks_task_steps.rb │ ├── 20190923160455_add_task_plan_and_withdrawn_at_to_task_caches.rb │ ├── 20191002212525_add_pe_and_spe_calculation_and_ecosystem_matrix_uuids_to_tasks_tasks.rb │ ├── 20191105182525_create_stats.rb │ ├── 20191115192003_drop_openstax_uid_and_username_uniqueness.openstax_accounts.rb │ ├── 20191115192004_drop_accounts_groups.openstax_accounts.rb │ ├── 20191210200351_create_tasks_grading_templates.rb │ ├── 20200123234902_add_cloned_from_id_to_grading_templates.rb │ ├── 20200127182429_add_closes_at_to_tasks.rb │ ├── 20200204192400_delete_teacher_students_for_deleted_teachers.rb │ ├── 20200204234650_denormalize_books.rb │ ├── 20200221163659_add_points_to_homework_task_plans.rb │ ├── 20200303163234_add_core_page_ids_to_tasks_tasks.rb │ ├── 20200306232806_add_student_history_at_to_tasks.rb │ ├── 20200309221635_add_is_preview_available_and_preview_only_message_to_catalog_offerings.rb │ ├── 20200316162341_add_book_indices_to_content_pages.rb │ ├── 20200320203024_remove_content_from_taskeds.rb │ ├── 20200324204502_create_tasks_extensions.rb │ ├── 20200330205829_create_tasks_dropped_questions.rb │ ├── 20200410002543_add_number_of_questions_to_content_exercises.rb │ ├── 20200415183442_add_grading_columns.rb │ ├── 20200416191842_add_unique_index_on_grading_templates_course_id_and_name.rb │ ├── 20200423163436_add_is_kip_to_openstax_accounts_accounts.openstax_accounts.rb │ ├── 20200428154625_add_parent_book_part_uuid_to_content_pages.rb │ ├── 20200428154643_create_ratings_period_book_parts.rb │ ├── 20200429165357_create_ratings_role_book_parts.rb │ ├── 20200429221115_null_answer_id.rb │ ├── 20200504180056_rename_student_history_at_to_core_steps_completed_at.rb │ ├── 20200504180508_add_course_id_to_tasks.rb │ ├── 20200504180509_denormalize_time_zones.rb │ ├── 20200511154623_create_ratings_exercise_group_book_parts.rb │ ├── 20200511155404_add_glicko_columns.rb │ ├── 20200513175819_cache_if_task_is_manually_gradable.rb │ ├── 20200529212256_add_school_location_to_openstax_accounts_accounts.openstax_accounts.rb │ ├── 20200529235243_add_past_due_unattempted_ungraded_wrq_are_zero_to_courses.rb │ ├── 20200605151734_add_wrq_count_gradable_step_count_and_ungraded_step_count.rb │ ├── 20200617142531_add_available_points_and_published_points_to_tasks.rb │ ├── 20200713140652_add_teacher_performance_report_to_courses.rb │ ├── 20200716155313_remove_task_and_period_caches.rb │ ├── 20200723162633_index_task_steps_on_page_id.rb │ ├── 20200727210436_remove_biglearn_columns.rb │ ├── 20200730181511_create_course_profile_caches.rb │ ├── 20200731211543_add_grant_tutor_access_to_openstax_accounts_accounts.openstax_accounts.rb │ ├── 20200804194756_create_environments.rb │ ├── 20200804195118_add_environment_to_courses.rb │ ├── 20200807211136_fix_existing_task_caches.rb │ ├── 20200811191556_add_deleted_at_to_catalog_offerings.rb │ ├── 20200818184342_uuid_launch_data.rb │ ├── 20201006191442_create_practice_questions.rb │ ├── 20201028182152_create_active_storage_tables.active_storage.rb │ ├── 20201105003758_add_author_to_content_exercises.rb │ ├── 20201117191750_update_content_exercise_fields.rb │ ├── 20201118213740_create_delayed_workers.rb │ ├── 20201120230022_add_copyable_and_anonymize_author_to_content_exercises.rb │ ├── 20201123223824_add_derived_from_to_content_exercises.rb │ ├── 20201204011000_add_coauthor_profile_ids_to_content_exercises.rb │ ├── 20201204222351_add_deleted_at_to_content_exercises.rb │ ├── 20210126223119_add_course_code_to_course_profile_courses.rb │ ├── 20210129203307_add_subject_to_catalog_offerings.rb │ ├── 20210208180911_add_os_book_id_to_catalog_offerings.rb │ ├── 20210208231233_create_user_suggestions.rb │ ├── 20210315204108_remove_teacher_exercises_from_page_pools.rb │ ├── 20210329195114_make_catalog_offering_urls_nullable.rb │ ├── 20210329203051_remove_catalog_offering_urls.rb │ ├── 20210331164251_preview_does_not_cost.rb │ ├── 20210419211921_remove_stats.rb │ ├── 20210520154733_add_archive_version_to_content_books.rb │ ├── 20210521230242_allow_null_content_page_versions.rb │ ├── 20210622152149_add_updated_by_instructor_at_to_tasks_task_plans.rb │ ├── 20210623225308_create_payment_codes.rb │ ├── 20210715135030_add_solutions_are_public_to_exercises.rb │ ├── 20210803201316_add_allow_auto_graded_multiple_attempts_to_tasks_grading_templates.rb │ ├── 20210803213532_create_tasks_previous_attempts.rb │ ├── 20210803214129_add_attempt_number_to_tasks_tasked_exercises.rb │ ├── 20210915153150_set_value_for_null_attempt_numbers.rb │ ├── 20211005155701_remove_serialized_nokogiri_nodes.rb │ ├── 20211103150618_add_shuffle_answer_choices_fields.rb │ └── 20211115122108_add_is_administrator_to_openstax_accounts_accounts.openstax_accounts.rb ├── schema.rb ├── seeds.rb └── views │ ├── cc_page_stats_v01.sql │ └── cc_page_stats_v02.sql ├── docker-compose.yml ├── docker-quickstart.md ├── docker ├── Dockerfile └── entrypoint.rb ├── lib ├── active_record │ └── indestructible_record.rb ├── acts_as_resource.rb ├── acts_as_tasked.rb ├── auto_uuid.rb ├── axlsx_modifications.rb ├── box.rb ├── configurable.rb ├── configurable │ ├── client_error.rb │ └── client_methods.rb ├── date_time_utilities.rb ├── demo.rb ├── env_utilities.rb ├── error_page_builder.rb ├── filename_sanitizer.rb ├── has_timezone.rb ├── i_am.rb ├── json_serialize.rb ├── lev │ └── delegator.rb ├── logout_redirect_chooser.rb ├── map_users_accounts.rb ├── markdown_wrapper.rb ├── openstax │ ├── exercises │ │ ├── exercises.rb │ │ ├── v1.rb │ │ └── v1 │ │ │ ├── configuration.rb │ │ │ ├── exercise.rb │ │ │ ├── fake_client.rb │ │ │ └── real_client.rb │ ├── payments │ │ ├── api.rb │ │ ├── api │ │ │ ├── configuration.rb │ │ │ ├── fake_client.rb │ │ │ ├── real_client.rb │ │ │ └── remote_error.rb │ │ ├── fake_purchased_item.rb │ │ └── payments.rb │ └── validator │ │ ├── v1.rb │ │ ├── v1 │ │ ├── configuration.rb │ │ ├── fake_client.rb │ │ └── real_client.rb │ │ └── validator.rb ├── rack-attack-settings.rb ├── scout_helper.rb ├── settings.rb ├── settings │ ├── course_appearance_codes.rb │ ├── exercises.rb │ ├── notifications.rb │ ├── pardot.rb │ ├── payments.rb │ ├── response_validation.rb │ └── salesforce.rb ├── shared_course_search_helper.rb ├── tagger.rb ├── tasks │ ├── aws │ │ └── update_cloudwatch_metrics.rake │ ├── cron │ │ ├── day.rake │ │ ├── minute.rake │ │ └── month.rake │ ├── demo.rake │ ├── dump_restore.rake │ ├── exercises.rake │ ├── export_users_info_to_match_with_consent_forms.rake │ ├── find_dupes.rake │ ├── import_notes.rake │ ├── jobs.rake │ ├── lint.rake │ └── log_to_stdout.rake ├── term_year.rb ├── tutor │ ├── subsystems.rb │ └── subsystems │ │ └── association_extensions.rb ├── type_verification.rb ├── unique_tokenable.rb ├── url_generator.rb ├── values_table.rb ├── verify_and_get_id_array.rb ├── xlsx_helper.rb └── xlsx_utils.rb ├── log └── .keep ├── modd.conf ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── logo-openstax-tutor-vert.png └── robots.txt ├── release.jenkins ├── spec ├── access_policies │ ├── cc │ │ └── task_access_policy_spec.rb │ ├── course_access_policy_spec.rb │ ├── doorkeeper │ │ └── application_access_policy_spec.rb │ ├── ecosystem_access_policy_spec.rb │ ├── enrollment_change_access_policy_spec.rb │ ├── exercise_access_policy_spec.rb │ ├── grading_template_access_policy_spec.rb │ ├── note_access_policy_spec.rb │ ├── offering_access_policy_spec.rb │ ├── period_access_policy_spec.rb │ ├── practice_question_access_policy_spec.rb │ ├── research_survey_access_policy_spec.rb │ ├── role_access_policy_spec.rb │ ├── student_access_policy_spec.rb │ ├── suggestion_policy_spec.rb │ ├── task_access_policy_spec.rb │ ├── task_plan_access_policy_spec.rb │ ├── tasked_access_policy_spec.rb │ ├── tasking_plan_access_policy_spec.rb │ ├── teacher_access_policy_spec.rb │ ├── teacher_student_access_policy_spec.rb │ ├── track_tutor_onboarding_event_policy_spec.rb │ └── user_access_policy_spec.rb ├── controllers │ └── application_controller_spec.rb ├── factories │ ├── catalog │ │ └── offerings.rb │ ├── content │ │ ├── books.rb │ │ ├── ecosystems.rb │ │ ├── exercise_tags.rb │ │ ├── exercises.rb │ │ ├── lo_teks_tags.rb │ │ ├── maps.rb │ │ ├── mini_ecosystem.rb │ │ ├── note.rb │ │ ├── page_tags.rb │ │ ├── pages.rb │ │ └── tags.rb │ ├── course_content │ │ ├── course_ecosystems.rb │ │ └── excluded_exercises.rb │ ├── course_membership │ │ ├── enrollments.rb │ │ ├── periods.rb │ │ ├── students.rb │ │ ├── teacher_students.rb │ │ └── teachers.rb │ ├── course_profile │ │ ├── caches.rb │ │ └── courses.rb │ ├── delayed │ │ └── job.rb │ ├── doorkeeper │ │ ├── access_tokens.rb │ │ └── applications.rb │ ├── entity │ │ └── roles.rb │ ├── environments.rb │ ├── lms │ │ ├── app.rb │ │ ├── context.rb │ │ ├── course_score_callback.rb │ │ ├── launch_request.rb │ │ └── tool_consumer.rb │ ├── payment_codes.rb │ ├── ratings │ │ ├── exercise_group_book_parts.rb │ │ ├── period_book_parts.rb │ │ └── role_book_parts.rb │ ├── research │ │ ├── cohort.rb │ │ ├── study.rb │ │ ├── study_course.rb │ │ ├── survey.rb │ │ ├── survey_plan.rb │ │ └── tasks.rb │ ├── school_district │ │ ├── districts.rb │ │ └── schools.rb │ ├── short_code │ │ └── short_codes.rb │ ├── tasks │ │ ├── assistants.rb │ │ ├── concept_coach_tasks.rb │ │ ├── course_assistants.rb │ │ ├── dropped_questions.rb │ │ ├── extensions.rb │ │ ├── grading_templates.rb │ │ ├── performance_report_exports.rb │ │ ├── practice_questions.rb │ │ ├── previous_attempts.rb │ │ ├── task_plans.rb │ │ ├── task_steps.rb │ │ ├── tasked_exercises.rb │ │ ├── tasked_external_urls.rb │ │ ├── tasked_interactives.rb │ │ ├── tasked_placeholder.rb │ │ ├── tasked_readings.rb │ │ ├── tasked_task_plan.rb │ │ ├── tasked_videos.rb │ │ ├── tasking_plans.rb │ │ ├── taskings.rb │ │ └── tasks.rb │ └── user │ │ ├── administrators.rb │ │ ├── content_analysts.rb │ │ ├── customer_services.rb │ │ ├── profiles.rb │ │ ├── researcher.rb │ │ ├── suggestion.rb │ │ └── tour.rb ├── feature_js_helper.rb ├── features │ ├── admin │ │ ├── assign_teachers_spec.rb │ │ ├── bulk_set_flag_spec.rb │ │ ├── create_blank_course_spec.rb │ │ ├── create_blank_district_spec.rb │ │ ├── create_user_accounts_spec.rb │ │ ├── district_terms_work_spec.rb │ │ ├── edit_course_salesforce_spec.rb │ │ ├── edit_course_spec.rb │ │ ├── manage_schools_spec.rb │ │ ├── queued_jobs_spec.rb │ │ ├── sign_in_as_spec.rb │ │ └── visit_admin_dashboard_spec.rb │ ├── browser_upgrade_spec.rb │ ├── content_analyst │ │ └── ecosystems_spec.rb │ ├── customer_service │ │ ├── ecosystems_spec.rb │ │ ├── queued_jobs_spec.rb │ │ └── visit_customer_service_dashboard_spec.rb │ ├── drop_timing_and_refunds.rb │ ├── homepage_spec.rb │ ├── lms_launch_errors_spec.rb │ ├── pardot_spec.rb │ ├── research │ │ ├── cohorts_spec.rb │ │ ├── studies_spec.rb │ │ ├── study_course_mgmt_spec.rb │ │ └── survey_plans_spec.rb │ ├── salesforce │ │ └── push_salesforce_course_stats_spec.rb │ ├── short_codes_spec.rb │ └── teacher_url_join_spec.rb ├── fixtures │ ├── box │ │ └── excluded_exercises_stats_by_course_20170925T213441Z.csv │ ├── content │ │ └── sample_manifest.yml │ ├── demo │ │ ├── assign │ │ │ └── review │ │ │ │ └── apush.yml │ │ ├── course │ │ │ └── review │ │ │ │ └── apush.yml │ │ ├── import │ │ │ └── review │ │ │ │ ├── apush-prototype.yml │ │ │ │ └── apush.yml │ │ ├── users │ │ │ ├── review │ │ │ │ └── apush.yml │ │ │ └── staff │ │ │ │ ├── administrators.yml │ │ │ │ ├── content_analysts.yml │ │ │ │ ├── customer_support.yml │ │ │ │ └── researchers.yml │ │ └── work │ │ │ └── review │ │ │ └── apush.yml │ ├── exports │ │ └── Sociology_with_Courseware_Review_Scores_20200807-163104.xlsx │ ├── images │ │ └── staxly.png │ ├── manifests │ │ └── Section Summary Context Exercise.yml │ └── roster │ │ ├── test_courses_post_roster_1.csv │ │ ├── test_courses_post_roster_2.csv │ │ ├── test_courses_post_roster_blank_lines.csv │ │ └── test_courses_post_roster_incomplete.csv ├── handlers │ ├── admin │ │ ├── courses_create_spec.rb │ │ └── courses_destroy_spec.rb │ └── courses_teach_spec.rb ├── lib │ ├── acts_as_tasked_spec.rb │ ├── box_spec.rb │ ├── date_time_utilities_spec.rb │ ├── delayed │ │ └── worker_spec.rb │ ├── filename_sanitizer_spec.rb │ ├── has_timezone_spec.rb │ ├── json_serialize_spec.rb │ ├── logout_redirect_chooser_spec.rb │ ├── map_users_accounts_spec.rb │ ├── openstax │ │ ├── accounts │ │ │ └── configuration_spec.rb │ │ ├── exercises │ │ │ ├── v1 │ │ │ │ ├── exercise_spec.rb │ │ │ │ ├── fake_client_spec.rb │ │ │ │ └── real_client_spec.rb │ │ │ └── v1_spec.rb │ │ ├── payments │ │ │ └── api │ │ │ │ └── real_client_spec.rb │ │ └── validator │ │ │ ├── v1 │ │ │ ├── fake_client_spec.rb │ │ │ └── real_client_spec.rb │ │ │ └── v1_spec.rb │ ├── settings │ │ ├── exercises_spec.rb │ │ ├── notifications_spec.rb │ │ ├── pardot_spec.rb │ │ └── payments_spec.rb │ ├── tagger_spec.rb │ ├── tasks │ │ ├── cron │ │ │ ├── day.rake_spec.rb │ │ │ ├── minute.rake_spec.rb │ │ │ └── month.rake_spec.rb │ │ ├── demo.rake_spec.rb │ │ ├── exercises.rake_spec.rb │ │ └── import_notes.rake_spec.rb │ ├── term_year_spec.rb │ ├── transaction_retry_spec.rb │ ├── unique_tokenable_spec.rb │ ├── values_table_spec.rb │ ├── xlsx_helper_spec.rb │ └── xlsx_utils_spec.rb ├── mocks │ └── assistants │ │ └── dummy_assistant.rb ├── models │ ├── entity │ │ └── role_spec.rb │ ├── environment_spec.rb │ ├── payment_code_spec.rb │ └── ratings │ │ ├── exercise_group_book_part_spec.rb │ │ ├── period_book_part_spec.rb │ │ └── role_book_part_spec.rb ├── rails_helper.rb ├── representers │ └── api │ │ └── v1 │ │ ├── approve_enrollment_change_representer_spec.rb │ │ ├── book_part_toc_representer_spec.rb │ │ ├── book_toc_representer_spec.rb │ │ ├── book_tocs_representer_spec.rb │ │ ├── bootstrap_data_representer_spec.rb │ │ ├── course_clone_representer_spec.rb │ │ ├── course_enrollments_representer_spec.rb │ │ ├── course_representer_shared_examples.rb │ │ ├── course_representer_spec.rb │ │ ├── courses │ │ └── dashboard_representer_spec.rb │ │ ├── ecosystem_representer_spec.rb │ │ ├── enrollment_change_representer_spec.rb │ │ ├── error_representer_spec.rb │ │ ├── exercise_representer_spec.rb │ │ ├── exercises_representer_spec.rb │ │ ├── grading_template_representer_spec.rb │ │ ├── job_representer_spec.rb │ │ ├── lms │ │ └── linking_representer_spec.rb │ │ ├── new_enrollment_change_representer_spec.rb │ │ ├── offering_representer_spec.rb │ │ ├── performance_report │ │ ├── export_representer_spec.rb │ │ └── representer_spec.rb │ │ ├── period_representer_spec.rb │ │ ├── practice_question_representer_spec.rb │ │ ├── rails_collection_setter.rb │ │ ├── snap_lab_representer_spec.rb │ │ ├── student_representer_spec.rb │ │ ├── student_self_update_representer_spec.rb │ │ ├── tag_representer_spec.rb │ │ ├── task_plan │ │ ├── dropped_question_representer_spec.rb │ │ ├── extension_representer_spec.rb │ │ ├── representer_spec.rb │ │ ├── scores │ │ │ └── representer_spec.rb │ │ ├── stats_representer_spec.rb │ │ └── tasking_plan_representer_spec.rb │ │ ├── task_representer_spec.rb │ │ ├── task_step_representer_spec.rb │ │ ├── tasked_representer_mapper_spec.rb │ │ ├── tasks │ │ ├── stats_representer_spec.rb │ │ ├── tasked_exercise_grading_representer_spec.rb │ │ ├── tasked_exercise_representer_shared_examples.rb │ │ ├── tasked_exercise_representer_spec.rb │ │ ├── tasked_external_url_representer_spec.rb │ │ ├── tasked_placeholder_representer_spec.rb │ │ ├── tasked_reading_representer_spec.rb │ │ └── tasked_video_representer_spec.rb │ │ ├── teacher_course_guide_representer_spec.rb │ │ ├── teacher_representer_spec.rb │ │ ├── term_year_representer_spec.rb │ │ ├── updates_representer_spec.rb │ │ └── user_representer_spec.rb ├── requests │ ├── admin │ │ ├── catalog_offerings_controller_spec.rb │ │ ├── console_controller_spec.rb │ │ ├── courses_controller_spec.rb │ │ ├── demo_controller_spec.rb │ │ ├── districts_controller_spec.rb │ │ ├── ecosystems_controller_spec.rb │ │ ├── jobs_controller_spec.rb │ │ ├── payments_controller_spec.rb │ │ ├── periods_controller_spec.rb │ │ ├── research_data_controller_spec.rb │ │ ├── salesforce_controller_spec.rb │ │ ├── schools_controller_spec.rb │ │ ├── students_controller_spec.rb │ │ ├── tags_controller_spec.rb │ │ ├── teachers_controller_spec.rb │ │ └── users_controller_spec.rb │ ├── api │ │ └── v1 │ │ │ ├── course_exercises_controller_spec.rb │ │ │ ├── courses_controller_spec.rb │ │ │ ├── demo_controller_spec.rb │ │ │ ├── ecosystems_controller_spec.rb │ │ │ ├── enrollment_controller_spec.rb │ │ │ ├── grading_templates_controller_spec.rb │ │ │ ├── guides_controller_spec.rb │ │ │ ├── jobs_controller_spec.rb │ │ │ ├── lms │ │ │ └── courses_controller_spec.rb │ │ │ ├── log_controller_spec.rb │ │ │ ├── notes_controller_spec.rb │ │ │ ├── offerings_controller_spec.rb │ │ │ ├── pages_controller_spec.rb │ │ │ ├── payment_codes_controller_spec.rb │ │ │ ├── performance_reports_controller_spec.rb │ │ │ ├── periods_controller_spec.rb │ │ │ ├── practice_questions_controller_spec.rb │ │ │ ├── practices_controller_spec.rb │ │ │ ├── purchases_controller_spec.rb │ │ │ ├── research_surveys_controller_spec.rb │ │ │ ├── students_controller_spec.rb │ │ │ ├── task_plan_reassignment_works_spec.rb │ │ │ ├── task_plans_controller_spec.rb │ │ │ ├── task_steps_controller_spec.rb │ │ │ ├── tasking_plans_controller_spec.rb │ │ │ ├── tasks_controller_spec.rb │ │ │ ├── teachers_controller_spec.rb │ │ │ ├── terms_controller_spec.rb │ │ │ ├── updates_controller_spec.rb │ │ │ └── users_controller_spec.rb │ ├── content_analyst │ │ ├── console_controller_spec.rb │ │ ├── ecosystems_controller_spec.rb │ │ └── jobs_controller_spec.rb │ ├── courses_controller_spec.rb │ ├── customer_service │ │ ├── console_controller_spec.rb │ │ ├── courses_controller_spec.rb │ │ ├── ecosystems_controller_spec.rb │ │ ├── jobs_controller_spec.rb │ │ ├── students_controller_spec.rb │ │ ├── tags_controller_spec.rb │ │ └── users_controller_spec.rb │ ├── lms_controller_spec.rb │ ├── openstax │ │ └── accounts │ │ │ └── sessions_controller_spec.rb │ ├── purchases_controller_spec.rb │ ├── research │ │ ├── cohorts_controller_spec.rb │ │ ├── console_controller_spec.rb │ │ ├── studies_controller_spec.rb │ │ └── survey_plans_controller_spec.rb │ ├── short_codes_controller_spec.rb │ ├── static_pages_controller_spec.rb │ ├── terms_controller_spec.rb │ └── webview_controller_spec.rb ├── routines │ ├── add_spy_info_spec.rb │ ├── add_user_as_course_teacher_spec.rb │ ├── add_user_as_period_student_spec.rb │ ├── background_migrate_spec.rb │ ├── build_teacher_exercise_content_hash_spec.rb │ ├── calculate_task_plan_scores_spec.rb │ ├── calculate_task_stats_spec.rb │ ├── choose_course_role_spec.rb │ ├── choose_exercises_spec.rb │ ├── clone_course_spec.rb │ ├── collect_course_info_spec.rb │ ├── collect_jobs_data_spec.rb │ ├── copy_teacher_exercises_spec.rb │ ├── create_course_spec.rb │ ├── create_or_claim_course_spec.rb │ ├── create_or_reset_teacher_student_spec.rb │ ├── create_period_spec.rb │ ├── create_teacher_exercise_spec.rb │ ├── delete_teacher_exercise_spec.rb │ ├── demo │ │ ├── all_spec.rb │ │ ├── assign_spec.rb │ │ ├── course_spec.rb │ │ ├── export_spec.rb │ │ ├── import_spec.rb │ │ ├── users_spec.rb │ │ └── work_spec.rb │ ├── distribute_tasks_spec.rb │ ├── does_tasking_exist_spec.rb │ ├── export_and_upload_research_data_spec.rb │ ├── export_exercise_exclusions_spec.rb │ ├── export_users_info_to_match_with_consent_forms_spec.rb │ ├── fetch_and_import_book_and_create_ecosystem_spec.rb │ ├── filter_excluded_exercises_spec.rb │ ├── find_or_create_practice_saved_task_spec.rb │ ├── find_or_create_practice_specific_topics_task_spec.rb │ ├── find_or_create_practice_worst_topics_task_spec.rb │ ├── generate_payment_code_report_spec.rb │ ├── generate_payment_codes_spec.rb │ ├── get_dashboard_spec.rb │ ├── get_exercises_spec.rb │ ├── get_page_exercise_ids_by_pool_types_spec.rb │ ├── get_practice_question_exercises_spec.rb │ ├── get_salesforce_book_names_spec.rb │ ├── get_student_guide_spec.rb │ ├── get_student_roster_spec.rb │ ├── get_task_core_page_ids_spec.rb │ ├── get_teacher_guide_spec.rb │ ├── get_user_courses_spec.rb │ ├── get_user_terms_infos_spec.rb │ ├── individualize_tasking_plans_spec.rb │ ├── integration │ │ └── course_roles_spec.rb │ ├── mark_task_step_completed_spec.rb │ ├── move_student_spec.rb │ ├── populate_preview_course_content_spec.rb │ ├── push_salesforce_course_stats_spec.rb │ ├── ratings │ │ ├── calculate_g_and_e_spec.rb │ │ ├── update_glicko_spec.rb │ │ ├── update_period_book_parts_spec.rb │ │ └── update_role_book_parts_spec.rb │ ├── reassign_published_period_task_plans_spec.rb │ ├── redeem_payment_code_spec.rb │ ├── refund_payment_spec.rb │ ├── search_courses_spec.rb │ ├── secure_random_token_generator_spec.rb │ ├── shared_examples_for_create_practice_task_routines.rb │ ├── task_exercise_spec.rb │ ├── track_tutor_onboarding_event_spec.rb │ ├── update_assigned_exercise_version_spec.rb │ ├── update_course_spec.rb │ ├── update_payment_status_spec.rb │ ├── update_task_plan_ecosystem_spec.rb │ ├── user_is_course_student_spec.rb │ ├── user_is_course_teacher_spec.rb │ └── work_preview_course_tasks_spec.rb ├── routing │ ├── api │ │ └── v1 │ │ │ ├── course_exercises_controller_routing_spec.rb │ │ │ ├── courses_controller_routing_spec.rb │ │ │ ├── ecosystems_controller_routing_spec.rb │ │ │ ├── guides_controller_routing_spec.rb │ │ │ ├── pages_controller_routing_spec.rb │ │ │ ├── performance_reports_controller_routing_spec.rb │ │ │ ├── practices_controller_routing_spec.rb │ │ │ └── students_controller_routing_spec.rb │ └── courses_controller_routing_spec.rb ├── spec_helper.rb ├── subsystems │ ├── catalog │ │ ├── models │ │ │ └── offering_spec.rb │ │ └── update_offering_spec.rb │ ├── content │ │ ├── delete_ecosystem_spec.rb │ │ ├── import_book_spec.rb │ │ ├── list_ecosystems_spec.rb │ │ ├── manifest_spec.rb │ │ ├── map_spec.rb │ │ ├── models │ │ │ ├── book_spec.rb │ │ │ ├── ecosystem_spec.rb │ │ │ ├── exercise_spec.rb │ │ │ ├── exercise_tag_spec.rb │ │ │ ├── lo_teks_tag_spec.rb │ │ │ ├── map_spec.rb │ │ │ ├── note_spec.rb │ │ │ ├── page_spec.rb │ │ │ ├── page_tag_spec.rb │ │ │ └── tag_spec.rb │ │ ├── routines │ │ │ ├── import_exercises_spec.rb │ │ │ ├── import_page_spec.rb │ │ │ ├── populate_exercise_pools_spec.rb │ │ │ ├── remap_practice_question_exercises_spec.rb │ │ │ ├── remap_teacher_exercises_spec.rb │ │ │ ├── tag_resource_spec.rb │ │ │ └── transform_and_cache_page_content_spec.rb │ │ └── upload_ecosystem_manifest_to_validator_spec.rb │ ├── course_content │ │ ├── add_ecosystem_to_course_spec.rb │ │ ├── get_course_ecosystems_spec.rb │ │ ├── models │ │ │ ├── course_ecosystem_spec.rb │ │ │ └── excluded_exercise_spec.rb │ │ └── update_exercise_exclusions_spec.rb │ ├── course_membership │ │ ├── activate_student_spec.rb │ │ ├── add_enrollment_spec.rb │ │ ├── add_student_spec.rb │ │ ├── add_teacher_spec.rb │ │ ├── add_teacher_student_spec.rb │ │ ├── create_enrollment_change_spec.rb │ │ ├── create_period_spec.rb │ │ ├── get_course_periods_spec.rb │ │ ├── get_course_roles_spec.rb │ │ ├── get_course_teacher_roles_spec.rb │ │ ├── get_period_student_roles_spec.rb │ │ ├── get_role_courses_spec.rb │ │ ├── get_teachers_spec.rb │ │ ├── inactivate_student_spec.rb │ │ ├── is_course_student_spec.rb │ │ ├── is_course_teacher_spec.rb │ │ ├── models │ │ │ ├── enrollment_change_spec.rb │ │ │ ├── enrollment_spec.rb │ │ │ ├── period_spec.rb │ │ │ ├── student_spec.rb │ │ │ ├── teacher_spec.rb │ │ │ └── teacher_student_spec.rb │ │ ├── process_enrollment_change_spec.rb │ │ └── validate_enrollment_parameters_spec.rb │ ├── course_profile │ │ ├── build_preview_courses_spec.rb │ │ ├── claim_preview_course_spec.rb │ │ ├── mark_course_enrolled_spec.rb │ │ ├── models │ │ │ ├── cache_spec.rb │ │ │ └── course_spec.rb │ │ └── update_course_spec.rb │ ├── legal │ │ └── legal_spec.rb │ ├── lms │ │ ├── launch_spec.rb │ │ ├── models │ │ │ └── context_spec.rb │ │ ├── outcome_response_spec.rb │ │ ├── pair_launch_to_course_spec.rb │ │ ├── queries_spec.rb │ │ ├── remove_last_course_pairing_spec.rb │ │ ├── send_course_scores_spec.rb │ │ └── willo_labs_spec.rb │ ├── research │ │ ├── add_course_to_study_spec.rb │ │ ├── admit_students_to_studies_spec.rb │ │ ├── assign_missing_surveys_spec.rb │ │ ├── cohort_membership_manager_spec.rb │ │ ├── complete_survey_spec.rb │ │ ├── export_and_upload_survey_data_spec.rb │ │ ├── hide_survey_plan_spec.rb │ │ ├── models │ │ │ ├── cohort_member_spec.rb │ │ │ ├── cohort_spec.rb │ │ │ ├── study_course_spec.rb │ │ │ └── study_spec.rb │ │ ├── publish_survey_plan_spec.rb │ │ ├── study_spec.rb │ │ ├── unhide_survey_plan_spec.rb │ │ └── update_study_activations_spec.rb │ ├── role │ │ └── create_user_role_spec.rb │ ├── school_district │ │ └── update_school_spec.rb │ ├── short_code │ │ ├── create_spec.rb │ │ ├── find_short_code_spec.rb │ │ ├── get_short_code_url_spec.rb │ │ └── url_for_spec.rb │ ├── tasks │ │ ├── assistants │ │ │ ├── event_assistant_spec.rb │ │ │ ├── external_assignment_assistant_spec.rb │ │ │ ├── homework_assistant_spec.rb │ │ │ └── i_reading_assistant_spec.rb │ │ ├── close_practice_task_spec.rb │ │ ├── export_performance_report_spec.rb │ │ ├── fetch_assignment_pes_spec.rb │ │ ├── fetch_assignment_spes_spec.rb │ │ ├── fetch_practice_worst_areas_exercises_spec.rb │ │ ├── freeze_ended_course_teacher_performance_reports_spec.rb │ │ ├── get_assistant_spec.rb │ │ ├── get_performance_report_exports_spec.rb │ │ ├── get_performance_report_spec.rb │ │ ├── get_redirect_url_spec.rb │ │ ├── get_task_plans_spec.rb │ │ ├── models │ │ │ ├── assistant_spec.rb │ │ │ ├── concept_coach_task_spec.rb │ │ │ ├── course_assistant_spec.rb │ │ │ ├── dropped_question_spec.rb │ │ │ ├── extension_spec.rb │ │ │ ├── grading_template_spec.rb │ │ │ ├── performance_report_export_spec.rb │ │ │ ├── practice_question_spec.rb │ │ │ ├── previous_attempt_spec.rb │ │ │ ├── task_plan_spec.rb │ │ │ ├── task_spec.rb │ │ │ ├── task_step_spec.rb │ │ │ ├── tasked_exercise_spec.rb │ │ │ ├── tasked_external_url_spec.rb │ │ │ ├── tasked_interactive_spec.rb │ │ │ ├── tasked_placeholder_spec.rb │ │ │ ├── tasked_reading_spec.rb │ │ │ ├── tasked_video_spec.rb │ │ │ ├── tasking_plan_spec.rb │ │ │ └── tasking_spec.rb │ │ ├── performance_report │ │ │ ├── export_cc_xlsx_spec.rb │ │ │ ├── export_pre_wrm_xlsx_spec.rb │ │ │ └── export_xlsx_spec.rb │ │ ├── populate_placeholder_steps_spec.rb │ │ ├── update_task_caches_spec.rb │ │ └── update_task_plan_caches_spec.rb │ └── user │ │ ├── find_or_create_user_spec.rb │ │ ├── make_administrator_spec.rb │ │ ├── models │ │ ├── administrator_spec.rb │ │ ├── anonymous_profile_spec.rb │ │ ├── content_analyst_spec.rb │ │ ├── customer_service_spec.rb │ │ ├── profile_spec.rb │ │ ├── suggestion.spec.rb │ │ └── tour_view_spec.rb │ │ ├── record_tour_view_spec.rb │ │ └── search_users_spec.rb ├── support │ ├── capture_stdout_helper.rb │ ├── capybara_wait.rb │ ├── course_assistants.rb │ ├── create_student_history.rb │ ├── fake_exercise_uuids.rb │ ├── lms │ │ ├── launch_helper.rb │ │ └── simulator.rb │ ├── populate_exercise_content.rb │ ├── rake.rb │ ├── screenshots.rb │ ├── setup_performance_report_data.rb │ ├── signin_helper.rb │ ├── test_routes.rb │ ├── user_agent_helper.rb │ ├── vcr_configuration_helper.rb │ └── without_exception.rb ├── vcr_helper.rb ├── views │ ├── admin │ │ ├── courses │ │ │ └── index.html.erb_spec.rb │ │ ├── ecosystems │ │ │ └── index.html.erb_spec.rb │ │ └── salesforce │ │ │ └── failures.html.erb_spec.rb │ ├── content_analyst │ │ └── ecosystems │ │ │ └── index.html.erb_spec.rb │ ├── customer_service │ │ ├── courses │ │ │ └── index.html.erb_spec.rb │ │ └── ecosystems │ │ │ └── index.html.erb_spec.rb │ ├── research │ │ └── survey_plans │ │ │ └── index.html.erb_spec.rb │ └── webview │ │ └── index.html.erb_spec.rb ├── webdrivers_helper.rb └── whenever_spec.rb ├── storage └── .keep ├── tmp ├── cache │ └── assets │ │ └── .keep ├── exports │ └── .keep ├── pids │ └── .keep ├── sessions │ └── .keep └── sockets │ └── .keep ├── vendor └── assets │ ├── javascripts │ ├── jquery.datetimepicker.js │ ├── jquery.stickytableheaders.min.js │ └── moment.min.js │ └── stylesheets │ └── jquery.datetimepicker.css └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .byebug_history 2 | .env 3 | .git 4 | .rspec 5 | .rspec_last_failures 6 | .webdrivers_update 7 | coverage 8 | node_modules 9 | tmp 10 | log 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | /spec/cassettes export-ignore 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @openstax/tutor-devs 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/template-epic.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Epic 3 | about: Epic template 4 | 5 | --- 6 | ### Description 7 | As a \ I want \ so that \. 8 | 9 | ### Acceptance Criteria 10 | Give criteria for acceptance. 11 | 12 | ### Epic Doc 13 | URL 14 | 15 | **Link the appropriate story issues** 16 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.rspec_parallel: -------------------------------------------------------------------------------- 1 | --require rspec/instafail 2 | --format RSpec::Instafail 3 | --format progress 4 | --format ParallelTests::RSpec::RuntimeLogger --out tmp/parallel_runtime_rspec.log 5 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | tutor 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Dantems Soares 2 | JP Slavinsky 3 | Karen Chan 4 | Amanda Shih 5 | Nathan Stitt 6 | Karina Méndez 7 | -------------------------------------------------------------------------------- /LICENSING: -------------------------------------------------------------------------------- 1 | OpenStax Tutor welcomes interaction from other entities in the education industry. 2 | If you are a part of an organization that wants to build on the OpenStax Tutor 3 | infrastructure and content but are concerned about the copyleft nature of the 4 | AGPL, please contact us to discuss other open license options. 5 | -------------------------------------------------------------------------------- /app/access_policies/allow_all_access_policy.rb: -------------------------------------------------------------------------------- 1 | class AllowAllAccessPolicy 2 | def self.action_allowed?(action, requestor, resource) 3 | return true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/access_policies/cc/task_access_policy.rb: -------------------------------------------------------------------------------- 1 | class Cc::TaskAccessPolicy 2 | def self.action_allowed?(action, requestor, task) 3 | return false if requestor.is_anonymous? || !requestor.is_human? 4 | 5 | action == :show 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/access_policies/exercise_access_policy.rb: -------------------------------------------------------------------------------- 1 | class ExerciseAccessPolicy 2 | def self.action_allowed?(action, requestor, exercise) 3 | return false if requestor.is_anonymous? || !requestor.is_human? 4 | 5 | case action 6 | when :delete 7 | exercise.user_profile_id == requestor.id 8 | else 9 | false 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/access_policies/job_access_policy.rb: -------------------------------------------------------------------------------- 1 | class JobAccessPolicy 2 | def self.action_allowed?(action, requestor, job) 3 | return false unless requestor.is_human? 4 | 5 | case action 6 | when :index 7 | requestor.is_admin? 8 | when :read 9 | true 10 | else 11 | false 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/access_policies/note_access_policy.rb: -------------------------------------------------------------------------------- 1 | class NoteAccessPolicy 2 | def self.action_allowed?(action, requestor, note) 3 | return false unless note.present? && requestor.is_human? 4 | 5 | case action.to_sym 6 | when :index 7 | true 8 | when :create, :update, :destroy 9 | note.role.user_profile_id == requestor.id 10 | else 11 | false 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/access_policies/practice_question_access_policy.rb: -------------------------------------------------------------------------------- 1 | class PracticeQuestionAccessPolicy 2 | def self.action_allowed?(action, requestor, question) 3 | return false unless requestor.is_human? 4 | case action 5 | when :read, :create, :destroy 6 | return question.role.user_profile_id == requestor.id 7 | else 8 | false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/access_policies/research_survey_access_policy.rb: -------------------------------------------------------------------------------- 1 | class ResearchSurveyAccessPolicy 2 | 3 | def self.action_allowed?(action, requestor, survey) 4 | return false if !requestor.is_human? || requestor.is_anonymous? 5 | 6 | case action 7 | when :complete 8 | survey.student.role.user_profile_id == requestor.id 9 | else 10 | false 11 | end 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/access_policies/role_access_policy.rb: -------------------------------------------------------------------------------- 1 | class RoleAccessPolicy 2 | def self.action_allowed?(action, requestor, role) 3 | return false if !requestor.is_human? || requestor.is_anonymous? 4 | 5 | case action 6 | when :become 7 | role.user_profile_id == requestor.id 8 | else 9 | false 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/access_policies/suggestion_access_policy.rb: -------------------------------------------------------------------------------- 1 | class SuggestionAccessPolicy 2 | 3 | def self.action_allowed?(action, requestor, suggestion) 4 | return false if requestor.is_anonymous? || requestor.account.student? 5 | 6 | true 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /app/access_policies/tasking_plan_access_policy.rb: -------------------------------------------------------------------------------- 1 | class TaskingPlanAccessPolicy 2 | def self.action_allowed?(action, requestor, tasking_plan) 3 | return false if requestor.is_anonymous? || 4 | !requestor.is_human? || 5 | action != :grade || 6 | !tasking_plan.past_due? 7 | 8 | UserIsCourseTeacher[user: requestor, course: tasking_plan.task_plan.course] 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/assets/fonts/lato-v11-latin-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/fonts/lato-v11-latin-regular.ttf -------------------------------------------------------------------------------- /app/assets/fonts/lato-v11-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/fonts/lato-v11-latin-regular.woff -------------------------------------------------------------------------------- /app/assets/fonts/lato-v11-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/fonts/lato-v11-latin-regular.woff2 -------------------------------------------------------------------------------- /app/assets/html/404.html.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'error_page_builder' 3 | 4 | ErrorPageBuilder.build( 5 | view: self, code: 404, heading: "We couldn't find what you were looking for." 6 | ) 7 | %> 8 | -------------------------------------------------------------------------------- /app/assets/html/422.html.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'error_page_builder' 3 | 4 | ErrorPageBuilder.build( 5 | view: self, code: 422, heading: 'The change you were trying to make was rejected.' 6 | ) 7 | %> 8 | -------------------------------------------------------------------------------- /app/assets/html/500.html.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'error_page_builder' 3 | 4 | ErrorPageBuilder.build view: self, code: 500, heading: 'An unexpected error occurred!' 5 | %> 6 | -------------------------------------------------------------------------------- /app/assets/html/503.html.erb: -------------------------------------------------------------------------------- 1 | <%= 2 | require 'error_page_builder' 3 | 4 | ErrorPageBuilder.build view: self, code: 503, heading: "We're offline for a brief upgrade!" 5 | %> 6 | -------------------------------------------------------------------------------- /app/assets/images/blue_green_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/images/blue_green_bg.jpg -------------------------------------------------------------------------------- /app/assets/images/gates_red.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/images/gates_red.jpg -------------------------------------------------------------------------------- /app/assets/images/ljaf_color_tagline_transp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/images/ljaf_color_tagline_transp.gif -------------------------------------------------------------------------------- /app/assets/images/openstax-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/images/openstax-logo-white.png -------------------------------------------------------------------------------- /app/assets/images/ost-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/images/ost-hero.jpg -------------------------------------------------------------------------------- /app/assets/images/rice-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/assets/images/rice-logo.png -------------------------------------------------------------------------------- /app/assets/javascripts/home.js: -------------------------------------------------------------------------------- 1 | //= require jquery 2 | //= require bootstrap-sprockets 3 | -------------------------------------------------------------------------------- /app/assets/stylesheets/admin_navbar.scss: -------------------------------------------------------------------------------- 1 | .admin { 2 | .navbar { 3 | a { 4 | padding-left: 10px; 5 | padding-right: 10px; 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/browser_upgrade.scss: -------------------------------------------------------------------------------- 1 | body.static_pages-browser_upgrade { 2 | 3 | @include hero-fullpage-background(); 4 | 5 | &::before { 6 | top: 60px; 7 | } 8 | 9 | .warning_card { 10 | margin-top: 80px; 11 | a { 12 | color: #337ab7; 13 | } 14 | } 15 | 16 | footer { 17 | display: none; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/assets/stylesheets/customer_service.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | @import "common_colors"; 4 | @import "fonts"; 5 | @import "jquery-ui"; 6 | @import "manager"; 7 | -------------------------------------------------------------------------------- /app/assets/stylesheets/payments/stub.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require ../vendor/picnic.css 3 | *= require_self 4 | */ 5 | 6 | 7 | .actions { 8 | display: flex; 9 | height: 100%; 10 | justify-content: space-around; 11 | align-items: center; 12 | } 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/syntax_highlight.scss: -------------------------------------------------------------------------------- 1 | 2 | pre.code, pre.prettyprint { 3 | outline: 1px solid #ccc; 4 | padding: 5px; 5 | margin: 5px; 6 | 7 | .string { color: $os_green; } 8 | .number { color: $os_dark_orange; } 9 | .boolean { color: blue; } 10 | .null { color: purple; } 11 | .key { color: $os_orange; } 12 | } 13 | -------------------------------------------------------------------------------- /app/controllers/admin/jobs_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class JobsController < BaseController 3 | include Manager::JobActions 4 | 5 | self.job_search_url_proc = -> { admin_jobs_path } 6 | self.job_url_proc = ->(job) { admin_job_path(job.id) } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/admin/tests_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::TestsController < Admin::BaseController 2 | 3 | # A place to test things, e.g. page layouts 4 | 5 | layout 'minimal_error', only: :minimal_error 6 | 7 | def minimal_error; end 8 | 9 | def minimal_error_iframe; end 10 | 11 | def launch_iframe; end 12 | 13 | def launch 14 | render template: 'lms/launch', layout: false 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/api/v1/storage_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::StorageController < ApplicationController 2 | 3 | 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/content_analyst/base_controller.rb: -------------------------------------------------------------------------------- 1 | class ContentAnalyst::BaseController < ApplicationController 2 | before_action :authenticate_content_analyst! 3 | 4 | layout 'content_analyst' 5 | 6 | protected 7 | 8 | def authenticate_content_analyst! 9 | raise SecurityTransgression unless current_user.is_content_analyst? || current_user.is_admin? 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/content_analyst/console_controller.rb: -------------------------------------------------------------------------------- 1 | class ContentAnalyst::ConsoleController < ContentAnalyst::BaseController 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/content_analyst/ecosystems_controller.rb: -------------------------------------------------------------------------------- 1 | class ContentAnalyst::EcosystemsController < ContentAnalyst::BaseController 2 | include Manager::EcosystemsActions 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/content_analyst/jobs_controller.rb: -------------------------------------------------------------------------------- 1 | module ContentAnalyst 2 | class JobsController < BaseController 3 | include Manager::JobActions 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/customer_service/base_controller.rb: -------------------------------------------------------------------------------- 1 | class CustomerService::BaseController < ApplicationController 2 | before_action :authenticate_customer_service! 3 | 4 | layout 'customer_service' 5 | 6 | protected 7 | 8 | def authenticate_customer_service! 9 | raise SecurityTransgression unless current_user.is_customer_support? || current_user.is_admin? 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/customer_service/console_controller.rb: -------------------------------------------------------------------------------- 1 | class CustomerService::ConsoleController < CustomerService::BaseController 2 | end 3 | -------------------------------------------------------------------------------- /app/controllers/customer_service/ecosystems_controller.rb: -------------------------------------------------------------------------------- 1 | class CustomerService::EcosystemsController < CustomerService::BaseController 2 | include Manager::EcosystemsActions 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/customer_service/jobs_controller.rb: -------------------------------------------------------------------------------- 1 | module CustomerService 2 | class JobsController < BaseController 3 | include Manager::JobActions 4 | 5 | self.job_search_url_proc = -> { customer_service_jobs_path } 6 | self.job_url_proc = ->(job) { customer_service_job_path(job.id) } 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/controllers/customer_service/students_controller.rb: -------------------------------------------------------------------------------- 1 | class CustomerService::StudentsController < CustomerService::BaseController 2 | include Manager::StudentActions 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/customer_service/tags_controller.rb: -------------------------------------------------------------------------------- 1 | class CustomerService::TagsController < CustomerService::BaseController 2 | def index 3 | @tags = Content::SearchTags[tag_value: "%#{params[:query]}%"] 4 | .paginate(page: params[:page], per_page: 100) if params[:query].present? 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/controllers/customer_service/users_controller.rb: -------------------------------------------------------------------------------- 1 | class CustomerService::UsersController < CustomerService::BaseController 2 | include Manager::SearchUsers 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/manager/course_details.rb: -------------------------------------------------------------------------------- 1 | module Manager::CourseDetails 2 | protected 3 | 4 | def get_course_details 5 | @course = CourseProfile::Models::Course.find(params[:id]) 6 | @teachers = @course.teachers 7 | .preload(role: { profile: :account }) 8 | .sort_by { |teacher| teacher.last_name || teacher.name } 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/controllers/purchases_controller.rb: -------------------------------------------------------------------------------- 1 | class PurchasesController < ApplicationController 2 | def show 3 | student = CourseMembership::Models::Student.find_by!(uuid: params[:id]) 4 | raise SecurityTransgression if student.role.profile != current_user 5 | redirect_to course_dashboard_path(student.course) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/research/base_controller.rb: -------------------------------------------------------------------------------- 1 | class Research::BaseController < ApplicationController 2 | before_action :authenticate_researcher! 3 | 4 | layout 'research' 5 | 6 | protected 7 | 8 | def authenticate_researcher! 9 | raise SecurityTransgression unless current_user.is_researcher? 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/research/console_controller.rb: -------------------------------------------------------------------------------- 1 | class Research::ConsoleController < Research::BaseController 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/webview_helper.rb: -------------------------------------------------------------------------------- 1 | module WebviewHelper 2 | def homepage_background_pack_path(image) 3 | return unless image 4 | asset_pack_path("media/images/homepage/#{image}") 5 | end 6 | 7 | def homepage_background_style(image) 8 | return unless image 9 | "background-image: url(#{homepage_background_pack_path(image)})" 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/javascript/images/homepage/OST_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/OST_color.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/OST_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/OST_dark.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/OST_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/OST_light.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/add_edit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/add_edit.jpg -------------------------------------------------------------------------------- /app/javascript/images/homepage/add_edit@2x.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/add_edit@2x.jpg -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/american-government-2e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/american-government-2e.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/american-government-3e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/american-government-3e.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/anatomy-physiology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/anatomy-physiology.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/biology-2e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/biology-2e.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/biology-for-ap-courses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/biology-for-ap-courses.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/college-physics-for-ap-courses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/college-physics-for-ap-courses.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/college-physics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/college-physics.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/entrepreneurship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/entrepreneurship.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/introduction-to-sociology-2e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/introduction-to-sociology-2e.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/introduction-to-sociology-3e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/introduction-to-sociology-3e.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/life-liberty-and-the-pursuit-of-happiness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/life-liberty-and-the-pursuit-of-happiness.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/psychology-2e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/psychology-2e.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/books/us-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/books/us-history.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/new_subjects.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/new_subjects.jpg -------------------------------------------------------------------------------- /app/javascript/images/homepage/rice_logo_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/rice_logo_dark.png -------------------------------------------------------------------------------- /app/javascript/images/homepage/rice_logo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstax/tutor-server/7cc6d5f29e10f67b993253f7cabb6355e9ca7b68/app/javascript/images/homepage/rice_logo_light.png -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | def self.perform(*args) 3 | new.perform(*args) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/models/environment.rb: -------------------------------------------------------------------------------- 1 | class Environment < IndestructibleRecord 2 | has_many :courses, subsystem: :course_profile, inverse_of: :environment 3 | 4 | validates :name, presence: true, uniqueness: true 5 | 6 | def self.current 7 | find_or_create_by! name: Rails.application.secrets.environment_name 8 | end 9 | 10 | def current? 11 | name == Rails.application.secrets.environment_name 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/indestructible_record.rb: -------------------------------------------------------------------------------- 1 | class IndestructibleRecord < ApplicationRecord 2 | self.abstract_class = true 3 | 4 | before_destroy do 5 | raise ActiveRecord::IndestructibleRecord, "#{self.class.name} is marked as indestructible" 6 | end 7 | 8 | def delete 9 | destroy 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/models/purchased_item.rb: -------------------------------------------------------------------------------- 1 | class PurchasedItem 2 | 3 | def self.exists?(uuid:) 4 | find(uuid: uuid).present? 5 | end 6 | 7 | def self.find(uuid:) 8 | CourseMembership::Models::Student.find_by(uuid: uuid) || 9 | OpenStax::Payments::FakePurchasedItem.find(uuid) 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /app/models/settings/redis.rb: -------------------------------------------------------------------------------- 1 | module Settings 2 | class Redis 3 | redis_secrets = Rails.application.secrets.redis 4 | @store = ::Redis::Store.new( 5 | url: redis_secrets[:url], 6 | namespace: redis_secrets[:namespaces][:settings] 7 | ) 8 | 9 | class << self 10 | extend Forwardable 11 | 12 | def_delegators :@store, :get, :set, :hget, :hset, :hdel, :hgetall 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/representers/api/v1/admin/user_search_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | module Admin 3 | class UserSearchRepresenter < OpenStax::Api::V1::AbstractSearchRepresenter 4 | 5 | collection :items, inherit: true, extend: Api::V1::Admin::UserRepresenter 6 | 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/representers/api/v1/approve_enrollment_change_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class ApproveEnrollmentChangeRepresenter < Roar::Decorator 3 | 4 | include Roar::JSON 5 | 6 | property :student_identifier, 7 | type: String, 8 | readable: true, 9 | writeable: true 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/representers/api/v1/book_tocs_representer.rb: -------------------------------------------------------------------------------- 1 | # If you modify this representer, you must run `Content::Models::Ecosystem.find_each(&:touch)` 2 | class Api::V1::BookTocsRepresenter < Roar::Decorator 3 | include Representable::JSON::Collection 4 | 5 | items extend: Api::V1::BookTocRepresenter 6 | end 7 | -------------------------------------------------------------------------------- /app/representers/api/v1/courses_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::CoursesRepresenter < Roar::Decorator 2 | include Representable::JSON::Collection 3 | 4 | items extend: Api::V1::CourseRepresenter 5 | end 6 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/assign/representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::Assign::Representer < Api::V1::Demo::BaseRepresenter 2 | property :course, 3 | extend: Api::V1::Demo::Assign::Course::Representer, 4 | class: Demo::Mash, 5 | readable: true, 6 | writeable: true, 7 | schema_info: { required: true } 8 | end 9 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/base_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::BaseRepresenter < Roar::Decorator 2 | include Roar::JSON 3 | include Representable::Hash::AllowSymbols 4 | include Representable::Coercion 5 | end 6 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/catalog_offering_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::CatalogOfferingRepresenter < Api::V1::Demo::BaseRepresenter 2 | # One of either id or title is required 3 | property :id, 4 | type: String, 5 | readable: false, 6 | writeable: true 7 | 8 | property :title, 9 | type: String, 10 | readable: true, 11 | writeable: true 12 | end 13 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/course/user_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::Course::UserRepresenter < Api::V1::Demo::UserRepresenter 2 | property :deleted?, 3 | as: :is_dropped, 4 | type: Virtus::Attribute::Boolean, 5 | readable: true, 6 | writeable: true 7 | end 8 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/course_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::CourseRepresenter < Api::V1::Demo::BaseRepresenter 2 | # One of either id or name is required 3 | property :id, 4 | type: String, 5 | readable: false, 6 | writeable: true 7 | 8 | property :name, 9 | type: String, 10 | readable: true, 11 | writeable: true 12 | end 13 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/period_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::PeriodRepresenter < Api::V1::Demo::BaseRepresenter 2 | property :name, 3 | type: String, 4 | readable: true, 5 | writeable: true, 6 | schema_info: { required: true } 7 | end 8 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/task_plan_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::TaskPlanRepresenter < Api::V1::Demo::BaseRepresenter 2 | # One of either id or title is required 3 | property :id, 4 | type: String, 5 | readable: false, 6 | writeable: true 7 | 8 | property :title, 9 | type: String, 10 | readable: true, 11 | writeable: true 12 | end 13 | -------------------------------------------------------------------------------- /app/representers/api/v1/demo/work/representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::Demo::Work::Representer < Api::V1::Demo::BaseRepresenter 2 | property :course, 3 | extend: Api::V1::Demo::Work::Course::Representer, 4 | class: Demo::Mash, 5 | readable: true, 6 | writeable: true, 7 | schema_info: { required: true } 8 | end 9 | -------------------------------------------------------------------------------- /app/representers/api/v1/ecosystems_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | 3 | class EcosystemsRepresenter < Roar::Decorator 4 | 5 | include Representable::JSON::Collection 6 | 7 | items extend: Api::V1::EcosystemRepresenter 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/representers/api/v1/exercise_search_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class ExerciseSearchRepresenter < OpenStax::Api::V1::AbstractSearchRepresenter 3 | collection :items, 4 | inherit: true, 5 | extend: Api::V1::ExerciseRepresenter 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/representers/api/v1/exercises_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class ExercisesRepresenter < Roar::Decorator 3 | include Representable::JSON::Collection 4 | 5 | items extend: Api::V1::ExerciseRepresenter, class: Hashie::Mash 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/representers/api/v1/grading_template_search_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::GradingTemplateSearchRepresenter < OpenStax::Api::V1::AbstractSearchRepresenter 2 | collection :items, inherit: true, extend: Api::V1::GradingTemplateRepresenter 3 | end 4 | -------------------------------------------------------------------------------- /app/representers/api/v1/jobs_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::JobsRepresenter < Roar::Decorator 2 | include Representable::JSON::Collection 3 | 4 | items extend: Api::V1::JobRepresenter 5 | end 6 | -------------------------------------------------------------------------------- /app/representers/api/v1/notes_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | 3 | class NotesRepresenter < Roar::Decorator 4 | include Representable::JSON::Collection 5 | items extend: NoteRepresenter 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /app/representers/api/v1/offering_search_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::OfferingSearchRepresenter < OpenStax::Api::V1::AbstractSearchRepresenter 2 | collection :items, inherit: true, extend: Api::V1::OfferingRepresenter 3 | end 4 | -------------------------------------------------------------------------------- /app/representers/api/v1/performance_report/exports_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1::PerformanceReport 2 | class ExportsRepresenter < Roar::Decorator 3 | include Representable::JSON::Collection 4 | 5 | items extend: ExportRepresenter 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/representers/api/v1/performance_report/representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1::PerformanceReport 2 | class Representer < Roar::Decorator 3 | include Representable::JSON::Collection 4 | 5 | items extend: ::Api::V1::PerformanceReport::PeriodRepresenter 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/representers/api/v1/performance_report/student/data/null_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1::PerformanceReport::Student::Data 2 | class NullRepresenter < Roar::Decorator 3 | include Roar::JSON 4 | 5 | def to_hash(*args) 6 | nil 7 | end 8 | 9 | def to_json(*args) 10 | 'null' 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/representers/api/v1/practice_questions_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class PracticeQuestionsRepresenter < Roar::Decorator 3 | include Representable::JSON::Collection 4 | items extend: PracticeQuestionRepresenter 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/representers/api/v1/task_plan/search_representer.rb: -------------------------------------------------------------------------------- 1 | class Api::V1::TaskPlan::SearchRepresenter < OpenStax::Api::V1::AbstractSearchRepresenter 2 | collection :items, inherit: true, 3 | class: ::Tasks::Models::TaskPlan, 4 | extend: ::Api::V1::TaskPlan::Representer 5 | end 6 | -------------------------------------------------------------------------------- /app/representers/api/v1/task_step_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class TaskStepRepresenter < Roar::Decorator 3 | def self.prepare(task_step) 4 | tasked = task_step.tasked 5 | TaskedRepresenterMapper.representer_for(tasked).prepare(tasked) 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/representers/api/v1/teacher_course_guide_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class TeacherCourseGuideRepresenter < Roar::Decorator 3 | include Representable::JSON::Collection 4 | items extend: CourseGuidePeriodRepresenter 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/representers/api/v1/teachers_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class TeachersRepresenter < Roar::Decorator 3 | include Representable::JSON::Collection 4 | 5 | items extend: TeacherRepresenter 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/representers/api/v1/ui_settings_representer.rb: -------------------------------------------------------------------------------- 1 | module Api::V1 2 | class UiSettingsRepresenter < Roar::Decorator 3 | 4 | include Roar::JSON 5 | 6 | property :ui_settings, 7 | type: Hash, 8 | readable: true, 9 | writeable: true, 10 | schema_info: { required: true } 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/routines/add_ecosystem_to_course.rb: -------------------------------------------------------------------------------- 1 | class AddEcosystemToCourse 2 | lev_routine delegates_to: CourseContent::AddEcosystemToCourse, use_jobba: true 3 | end 4 | -------------------------------------------------------------------------------- /app/routines/delete_teacher_exercise.rb: -------------------------------------------------------------------------------- 1 | class DeleteTeacherExercise 2 | lev_routine 3 | 4 | protected 5 | 6 | def exec(number:) 7 | updated = Content::Models::Exercise.where(number: number).where.not( 8 | user_profile_id: 0 9 | ).update_all(deleted_at: Time.current) 10 | 11 | raise "Invalid number: #{number}" if updated == 0 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/routines/get_course_ecosystem.rb: -------------------------------------------------------------------------------- 1 | class GetCourseEcosystem 2 | lev_routine transaction: :no_transaction, express_output: :ecosystem 3 | 4 | protected 5 | 6 | def exec(course:) 7 | outputs.ecosystem = course.ecosystem 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/routines/move_student.rb: -------------------------------------------------------------------------------- 1 | class MoveStudent 2 | lev_routine express_output: :student 3 | 4 | uses_routine CourseMembership::AddEnrollment, 5 | translations: { outputs: { type: :verbatim } }, 6 | as: :add_enrollment 7 | 8 | def exec(period:, student:) 9 | run(:add_enrollment, period: period, student: student) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/routines/update_course.rb: -------------------------------------------------------------------------------- 1 | class UpdateCourse 2 | lev_routine 3 | 4 | uses_routine CourseProfile::UpdateCourse, as: :update_course 5 | 6 | protected 7 | 8 | def exec(id, params) 9 | run(:update_course, id, params) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/catalog/create_offering.rb: -------------------------------------------------------------------------------- 1 | module Catalog 2 | class CreateOffering 3 | lev_routine express_output: :offering 4 | 5 | protected 6 | 7 | def exec(attributes) 8 | outputs.offering = Catalog::Models::Offering.create(attributes) 9 | transfer_errors_from(outputs.offering, { type: :verbatim }, true) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/subsystems/content/book.rb: -------------------------------------------------------------------------------- 1 | class Content::Book < Content::BookPart 2 | end 3 | -------------------------------------------------------------------------------- /app/subsystems/content/chapter.rb: -------------------------------------------------------------------------------- 1 | class Content::Chapter < Content::BookPart 2 | def units 3 | [] 4 | end 5 | 6 | def chapters 7 | [ self ] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/content/list_ecosystems.rb: -------------------------------------------------------------------------------- 1 | class Content::ListEcosystems 2 | lev_routine express_output: :ecosystems 3 | 4 | protected 5 | 6 | def exec 7 | outputs[:ecosystems] = ::Content::Models::Ecosystem.without_deleted 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/content/map_invalid_error.rb: -------------------------------------------------------------------------------- 1 | module Content 2 | class MapInvalidError < StandardError; end 3 | end 4 | -------------------------------------------------------------------------------- /app/subsystems/content/models/exercise_tag.rb: -------------------------------------------------------------------------------- 1 | class Content::Models::ExerciseTag < IndestructibleRecord 2 | belongs_to :exercise, inverse_of: :exercise_tags 3 | belongs_to :tag, inverse_of: :exercise_tags 4 | 5 | validates :tag, uniqueness: { scope: :content_exercise_id } 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/content/models/lo_teks_tag.rb: -------------------------------------------------------------------------------- 1 | class Content::Models::LoTeksTag < IndestructibleRecord 2 | belongs_to :lo, class_name: 'Tag', foreign_key: :lo_id 3 | belongs_to :teks, class_name: 'Tag', foreign_key: :teks_id 4 | 5 | validates :teks, uniqueness: { scope: :lo_id } 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/content/models/note.rb: -------------------------------------------------------------------------------- 1 | module Content 2 | module Models 3 | class Note < ApplicationRecord 4 | attr_accessor :course_id 5 | 6 | belongs_to :page, subsystem: :content 7 | belongs_to :role, subsystem: :entity 8 | 9 | has_one :chapter, through: :page 10 | has_one :book, through: :chapter 11 | has_one :ecosystem, through: :book 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/subsystems/content/models/page_tag.rb: -------------------------------------------------------------------------------- 1 | class Content::Models::PageTag < IndestructibleRecord 2 | belongs_to :page, inverse_of: :page_tags 3 | belongs_to :tag, inverse_of: :page_tags 4 | 5 | validates :tag, uniqueness: { scope: :content_page_id } 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/content/page.rb: -------------------------------------------------------------------------------- 1 | class Content::Page < Content::BookPart 2 | def units 3 | [] 4 | end 5 | 6 | def chapters 7 | [] 8 | end 9 | 10 | def pages 11 | [ self ] 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/subsystems/content/question.rb: -------------------------------------------------------------------------------- 1 | Content::Question = Struct.new(:id, :content_hash, keyword_init: true) do 2 | def question_hash 3 | content_hash['questions'].first 4 | end 5 | 6 | delegate :[], to: :question_hash 7 | 8 | def content 9 | content_hash.to_json 10 | end 11 | 12 | def question_content 13 | question_hash.to_json 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/subsystems/content/search_tags.rb: -------------------------------------------------------------------------------- 1 | class Content::SearchTags 2 | lev_routine express_output: :tags 3 | 4 | protected 5 | 6 | def exec(tag_value:) 7 | tg = Content::Models::Tag.arel_table 8 | outputs.tags = Content::Models::Tag.where(tg[:value].matches(tag_value)) 9 | .order(:value, :created_at) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/content/unit.rb: -------------------------------------------------------------------------------- 1 | class Content::Unit < Content::BookPart 2 | def units 3 | [ self ] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/subsystems/content/upload_ecosystem_manifest_to_validator.rb: -------------------------------------------------------------------------------- 1 | class Content::UploadEcosystemManifestToValidator 2 | lev_routine transaction: :no_transaction 3 | 4 | def exec(ecosystem_or_manifest) 5 | OpenStax::Validator::V1.upload_ecosystem_manifest ecosystem_or_manifest 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/subsystems/course_content/get_course_ecosystems.rb: -------------------------------------------------------------------------------- 1 | class CourseContent::GetCourseEcosystems 2 | lev_routine express_output: :ecosystems 3 | 4 | protected 5 | 6 | def exec(course:) 7 | outputs.ecosystems = course.ecosystems 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/course_content/models/course_ecosystem.rb: -------------------------------------------------------------------------------- 1 | class CourseContent::Models::CourseEcosystem < IndestructibleRecord 2 | belongs_to :course, subsystem: :course_profile 3 | belongs_to :ecosystem, subsystem: :content 4 | 5 | default_scope -> { order created_at: :desc } 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/course_content/models/excluded_exercise.rb: -------------------------------------------------------------------------------- 1 | class CourseContent::Models::ExcludedExercise < ApplicationRecord 2 | belongs_to :course, subsystem: :course_profile 3 | 4 | validates :exercise_number, presence: true, uniqueness: { scope: :course_profile_course_id } 5 | end 6 | -------------------------------------------------------------------------------- /app/subsystems/course_membership/add_teacher.rb: -------------------------------------------------------------------------------- 1 | class CourseMembership::AddTeacher 2 | lev_routine 3 | 4 | protected 5 | 6 | def exec(course:, role:) 7 | outputs.teacher = CourseMembership::Models::Teacher.create(course_profile_course_id: course.id, 8 | entity_role_id: role.id) 9 | transfer_errors_from(outputs.teacher, {type: :verbatim}, true) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/course_membership/get_course_teacher_roles.rb: -------------------------------------------------------------------------------- 1 | class CourseMembership::GetCourseTeacherRoles 2 | lev_routine express_output: :roles 3 | 4 | protected 5 | def exec(course:) 6 | outputs.roles = course.teachers.map(&:role) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/subsystems/course_membership/get_teachers.rb: -------------------------------------------------------------------------------- 1 | class CourseMembership::GetTeachers 2 | lev_routine express_output: :teacher_roles 3 | 4 | protected 5 | 6 | def exec(course) 7 | outputs.teachers = CourseMembership::Models::Teacher.where(course_profile_course_id: course.id) 8 | .preload(:role) 9 | outputs.teacher_roles = outputs.teachers.map(&:role) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/course_profile/create_course.rb: -------------------------------------------------------------------------------- 1 | class CourseProfile::CreateCourse 2 | lev_routine 3 | 4 | protected 5 | def exec(attrs = {}) 6 | attrs[:is_lms_enabling_allowed] = Settings::Db.default_is_lms_enabling_allowed 7 | attrs[:is_lms_enabled] = nil 8 | 9 | outputs.course = CourseProfile::Models::Course.create(attrs) 10 | transfer_errors_from outputs.course, { type: :verbatim }, true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/subsystems/course_profile/mark_course_enrolled.rb: -------------------------------------------------------------------------------- 1 | class CourseProfile::MarkCourseEnrolled 2 | 3 | lev_routine 4 | 5 | protected 6 | 7 | def exec(course:) 8 | course.update_attribute :is_access_switchable, false 9 | transfer_errors_from(course, { type: :verbatim }, true) 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /app/subsystems/course_profile/models/cache.rb: -------------------------------------------------------------------------------- 1 | class CourseProfile::Models::Cache < ApplicationRecord 2 | belongs_to :course, inverse_of: :cache 3 | end 4 | -------------------------------------------------------------------------------- /app/subsystems/legal/destroy_targeted_contract.rb: -------------------------------------------------------------------------------- 1 | class Legal::DestroyTargetedContract 2 | lev_routine 3 | 4 | protected 5 | 6 | def exec(id:) 7 | tc = Legal::Models::TargetedContract.find(id) 8 | tc.destroy 9 | transfer_errors_from(tc, {type: :verbatim}) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/legal/forget_about.rb: -------------------------------------------------------------------------------- 1 | class Legal::ForgetAbout 2 | lev_routine 3 | 4 | protected 5 | 6 | def exec(item:) 7 | gid = Legal::Utils.gid(item) 8 | 9 | Legal::Models::TargetedContract.where(target_gid: gid).destroy_all 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/legal/models/targeted_contract.rb: -------------------------------------------------------------------------------- 1 | class Legal::Models::TargetedContract < ApplicationRecord 2 | 3 | json_serialize :masked_contract_names, String, array: true 4 | 5 | def as_poro 6 | Legal::TargetedContract.new(self) 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/legal/targeted_contract.rb: -------------------------------------------------------------------------------- 1 | class Legal::TargetedContract 2 | 3 | def initialize(repository) 4 | @repository = repository 5 | end 6 | 7 | delegate :target_gid, :target_name, :contract_name, :masked_contract_names, 8 | :is_proxy_signed, :is_end_user_visible, :id, 9 | to: :repository 10 | 11 | protected 12 | 13 | attr_reader :repository 14 | end 15 | -------------------------------------------------------------------------------- /app/subsystems/legal/utils.rb: -------------------------------------------------------------------------------- 1 | module Legal::Utils 2 | 3 | def self.gid(object) 4 | object.to_global_id.to_s 5 | end 6 | 7 | def self.available_contract_names 8 | FinePrint::Contract.published.map(&:name).uniq 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/lms/models/nonce.rb: -------------------------------------------------------------------------------- 1 | class Lms::Models::Nonce < ApplicationRecord 2 | validates :value, presence: true, uniqueness: { scope: :lms_app_id } 3 | 4 | enum app_type: [:course, :willo] 5 | 6 | def app 7 | @app ||= case app_type 8 | when 'willo' then ::Lms::WilloLabs.new 9 | when 'course' then ::Lms::Models::App.find_by_id(lms_app_id) 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/subsystems/lms/models/stubbed_app.rb: -------------------------------------------------------------------------------- 1 | class Lms::Models::StubbedApp 2 | attr_reader :key, :secret 3 | 4 | # This app is passed the course's original environment on initialization 5 | # It is used with ::Api::V1::Lms::LinkingRepresenter to return a stubbed response 6 | def initialize(environment) 7 | @key = @secret = "Copied from #{environment.name}" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/lms/models/tool_consumer.rb: -------------------------------------------------------------------------------- 1 | class Lms::Models::ToolConsumer < IndestructibleRecord 2 | # In LTI-speak, the ToolConsumer is the LMS. We have this record mostly 3 | # as a way to keep track of Contexts and to record metadata about the LMS. 4 | 5 | has_many :contexts, subsystem: :lms, dependent: :destroy 6 | 7 | validates :guid, presence: true, uniqueness: true 8 | end 9 | -------------------------------------------------------------------------------- /app/subsystems/lms/models/user.rb: -------------------------------------------------------------------------------- 1 | class Lms::Models::User < ApplicationRecord 2 | belongs_to :account, 3 | class_name: 'OpenStax::Accounts::Account', 4 | subsystem: 'none', 5 | optional: true 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/research/activate_study.rb: -------------------------------------------------------------------------------- 1 | class Research::ActivateStudy 2 | lev_routine 3 | 4 | def exec(study) 5 | study.update_attribute(:last_activated_at, Time.current) 6 | transfer_errors_from(study, {type: :verbatim}, true) 7 | 8 | # side effects? 9 | 10 | Rails.logger.info{ "Activated study #{id} '#{study.name}' at #{study.last_activated_at}"} 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/subsystems/research/complete_survey.rb: -------------------------------------------------------------------------------- 1 | class Research::CompleteSurvey 2 | 3 | lev_routine 4 | 5 | def exec(survey:, response:) 6 | survey.update_attributes(survey_js_response: response, completed_at: Time.now) 7 | transfer_errors_from(survey, {type: :verbatim}) 8 | end 9 | 10 | end 11 | -------------------------------------------------------------------------------- /app/subsystems/research/deactivate_study.rb: -------------------------------------------------------------------------------- 1 | class Research::DeactivateStudy 2 | lev_routine 3 | 4 | def exec(study) 5 | study.update_attribute(:last_deactivated_at, Time.current) 6 | transfer_errors_from(study, {type: :verbatim}, true) 7 | 8 | # ... 9 | 10 | Rails.logger.info{ "Deactivated study #{id} '#{study.name}' at #{study.last_deactivated_at}"} 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/subsystems/research/hide_survey_plan.rb: -------------------------------------------------------------------------------- 1 | class Research::HideSurveyPlan 2 | 3 | lev_routine 4 | 5 | def exec(survey_plan:) 6 | return if survey_plan.is_hidden? 7 | 8 | survey_plan.update_attributes(hidden_at: Time.now) 9 | transfer_errors_from(survey_plan, {type: :verbatim}, true) 10 | 11 | survey_plan.surveys.update_all(hidden_at: Time.now) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/subsystems/research/models/cohort_member.rb: -------------------------------------------------------------------------------- 1 | class Research::Models::CohortMember < ApplicationRecord 2 | belongs_to :cohort, inverse_of: :cohort_members, counter_cache: true 3 | belongs_to :student, subsystem: :course_membership, inverse_of: :research_cohort_members 4 | 5 | validates :student, uniqueness: { scope: :research_cohort_id } 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/role/create_user_role.rb: -------------------------------------------------------------------------------- 1 | class Role::CreateUserRole 2 | lev_routine express_output: :role 3 | 4 | protected 5 | 6 | def exec(user, role_type = :unassigned) 7 | outputs.role = ::Entity::Role.new(role_type: role_type) 8 | user.roles << outputs.role 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/subsystems/school_district/create_district.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class CreateDistrict 3 | lev_routine express_output: :district 4 | 5 | protected 6 | 7 | def exec(name:) 8 | outputs.district = ::SchoolDistrict::Models::District.create(name: name) 9 | 10 | transfer_errors_from(outputs.district, {type: :verbatim}) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/subsystems/school_district/create_school.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class CreateSchool 3 | lev_routine express_output: :school 4 | 5 | protected 6 | 7 | def exec(name:, district: nil) 8 | outputs.school = ::SchoolDistrict::Models::School.create(name: name, district: district) 9 | 10 | transfer_errors_from(outputs.school, {type: :verbatim}, true) 11 | end 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/subsystems/school_district/delete_district.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class DeleteDistrict 3 | lev_routine 4 | 5 | protected 6 | 7 | def exec(district:) 8 | fatal_error( 9 | code: :district_has_schools, message: 'Cannot delete a district that has schools.' 10 | ) unless district.destroy 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/subsystems/school_district/delete_school.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class DeleteSchool 3 | lev_routine 4 | 5 | protected 6 | 7 | def exec(school:) 8 | fatal_error( 9 | code: :school_has_courses, message: 'Cannot delete a school that has courses.' 10 | ) unless school.destroy 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/subsystems/school_district/get_district.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class GetDistrict 3 | lev_routine express_output: :district 4 | 5 | protected 6 | 7 | def exec(id:) 8 | outputs.district = ::SchoolDistrict::Models::District.find_by(id: id) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/school_district/list_districts.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class ListDistricts 3 | lev_routine express_output: :districts 4 | 5 | protected 6 | 7 | def exec 8 | outputs.districts = ::SchoolDistrict::Models::District.all 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/school_district/list_schools.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class ListSchools 3 | lev_routine express_output: :schools 4 | 5 | protected 6 | 7 | def exec 8 | outputs.schools = ::SchoolDistrict::Models::School.all 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/school_district/update_district.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class UpdateDistrict 3 | lev_routine express_output: :district 4 | 5 | protected 6 | 7 | def exec(district:, name:) 8 | district.update_attributes(name: name) 9 | 10 | transfer_errors_from(district, {type: :verbatim}) 11 | 12 | outputs.district = district 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/subsystems/school_district/update_school.rb: -------------------------------------------------------------------------------- 1 | module SchoolDistrict 2 | class UpdateSchool 3 | lev_routine express_output: :school 4 | 5 | protected 6 | 7 | def exec(school:, name:, district:) 8 | school.update_attributes(name: name, district: district) 9 | 10 | transfer_errors_from(school, {type: :verbatim}) 11 | 12 | outputs.school = school 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/subsystems/short_code/create.rb: -------------------------------------------------------------------------------- 1 | class ShortCode::Create 2 | lev_routine express_output: :short_code 3 | 4 | protected 5 | 6 | def exec(uri) 7 | short_code = ShortCode::Models::ShortCode.create(uri: uri) 8 | outputs.short_code = short_code.code 9 | transfer_errors_from(short_code, type: :verbatim) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/subsystems/short_code/find_short_code.rb: -------------------------------------------------------------------------------- 1 | class ShortCode::FindShortCode 2 | lev_routine express_output: :short_code 3 | 4 | protected 5 | 6 | def exec(uri) 7 | outputs.short_code = ShortCode::Models::ShortCode.find_by_uri(uri).try(:code) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/short_code/short_code_redirect.rb: -------------------------------------------------------------------------------- 1 | class ShortCode::ShortCodeRedirect 2 | lev_handler 3 | 4 | uses_routine ShortCode::GetShortCodeUrl, translations: { outputs: { type: :verbatim } } 5 | 6 | protected 7 | 8 | def authorized? 9 | true 10 | end 11 | 12 | def handle 13 | run(:short_code_get_short_code_url, short_code: params[:short_code], user: caller) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/subsystems/tasks/create_tasking.rb: -------------------------------------------------------------------------------- 1 | class Tasks::CreateTasking 2 | lev_routine 3 | 4 | protected 5 | 6 | def exec(role:, task:, period: nil) 7 | outputs.tasking = Tasks::Models::Tasking.new role: role, task: task 8 | task.taskings << outputs.tasking 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/subsystems/tasks/models/dropped_question.rb: -------------------------------------------------------------------------------- 1 | class Tasks::Models::DroppedQuestion < ApplicationRecord 2 | belongs_to :task_plan, inverse_of: :dropped_questions, touch: true 3 | 4 | enum drop_method: [ :zeroed, :full_credit ] 5 | 6 | validates :question_id, presence: true, uniqueness: { scope: :tasks_task_plan_id } 7 | validates :drop_method, presence: true 8 | end 9 | -------------------------------------------------------------------------------- /app/subsystems/tasks/models/previous_attempt.rb: -------------------------------------------------------------------------------- 1 | class Tasks::Models::PreviousAttempt < ApplicationRecord 2 | belongs_to :tasked_exercise, inverse_of: :previous_attempts 3 | 4 | validates :number, presence: true, uniqueness: { scope: :tasks_tasked_exercise_id } 5 | validates :attempted_at, presence: true 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/tasks/models/tasked_external_url.rb: -------------------------------------------------------------------------------- 1 | class Tasks::Models::TaskedExternalUrl < IndestructibleRecord 2 | acts_as_tasked 3 | 4 | validates :url, presence: true 5 | 6 | def content_preview 7 | title || "External Url step ##{id}" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/tasks/models/tasked_interactive.rb: -------------------------------------------------------------------------------- 1 | class Tasks::Models::TaskedInteractive < IndestructibleRecord 2 | acts_as_tasked 3 | 4 | validates :url, presence: true 5 | validates :content, presence: true 6 | 7 | def has_content? 8 | true 9 | end 10 | 11 | def content_preview 12 | title || "External Interactive step ##{id}" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/subsystems/tasks/models/tasked_video.rb: -------------------------------------------------------------------------------- 1 | class Tasks::Models::TaskedVideo < IndestructibleRecord 2 | acts_as_tasked 3 | 4 | validates :url, presence: true 5 | validates :content, presence: true 6 | 7 | def has_content? 8 | true 9 | end 10 | 11 | def content_preview 12 | title || "External Reading step ##{id}" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/subsystems/tasks/models/tasking.rb: -------------------------------------------------------------------------------- 1 | class Tasks::Models::Tasking < IndestructibleRecord 2 | belongs_to :task, inverse_of: :taskings 3 | belongs_to :role, subsystem: :entity, inverse_of: :taskings 4 | 5 | validates :role, uniqueness: { scope: :tasks_task_id } 6 | end 7 | -------------------------------------------------------------------------------- /app/subsystems/user/make_administrator.rb: -------------------------------------------------------------------------------- 1 | module User 2 | class MakeAdministrator 3 | lev_routine 4 | 5 | uses_routine ::User::SetAdministratorState, as: :set_admin 6 | 7 | protected 8 | def exec(user:) 9 | raise 'The given user is already an administrator' if user.is_admin? 10 | run(:set_admin, user: user, administrator: true) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/subsystems/user/models/administrator.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class Administrator < ApplicationRecord 4 | belongs_to :profile, inverse_of: :administrator 5 | 6 | validates :profile, uniqueness: true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/user/models/anonymous_author_profile.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class AnonymousAuthorProfile 4 | ID = -1 5 | 6 | def self.id 7 | ID 8 | end 9 | 10 | def self.name 11 | 'OpenStax user' 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/subsystems/user/models/content_analyst.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class ContentAnalyst < ApplicationRecord 4 | belongs_to :profile, inverse_of: :content_analyst 5 | 6 | validates :profile, uniqueness: true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/user/models/customer_service.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class CustomerService < ApplicationRecord 4 | belongs_to :profile, inverse_of: :customer_support 5 | 6 | validates :profile, uniqueness: true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/user/models/openstax_profile.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class OpenStaxProfile 4 | ID = 0 5 | 6 | def self.id 7 | ID 8 | end 9 | 10 | def self.name 11 | 'OpenStax' 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/subsystems/user/models/researcher.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class Researcher < ApplicationRecord 4 | belongs_to :profile, inverse_of: :researcher 5 | 6 | validates :profile, uniqueness: true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/user/models/tour.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class Tour < ApplicationRecord 4 | has_many :tour_views, dependent: :destroy, inverse_of: :tour 5 | 6 | validates :identifier, presence: true, uniqueness: true, format: /\A[0-9a-z\-]+\z/ 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/subsystems/user/models/tour_view.rb: -------------------------------------------------------------------------------- 1 | module User 2 | module Models 3 | class TourView < ApplicationRecord 4 | 5 | belongs_to :tour, inverse_of: :tour_views 6 | 7 | belongs_to :profile, inverse_of: :tour_views 8 | 9 | validates :tour, uniqueness: { scope: :user_profile_id } 10 | validates :view_count, presence: true 11 | 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/subsystems/user/set_administrator_state.rb: -------------------------------------------------------------------------------- 1 | module User 2 | class SetAdministratorState 3 | lev_routine 4 | 5 | protected 6 | 7 | def exec(user:, administrator: false) 8 | return if (administrator && user.is_admin?) || (!administrator && !user.is_admin?) 9 | 10 | administrator ? user.create_administrator! : user.administrator.destroy 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/subsystems/user/set_researcher_state.rb: -------------------------------------------------------------------------------- 1 | module User 2 | class SetResearcherState 3 | lev_routine 4 | 5 | protected 6 | 7 | def exec(user:, researcher: false) 8 | return if (researcher && user.is_researcher?) || (!researcher && !user.is_researcher?) 9 | 10 | researcher ? user.create_researcher! : user.researcher.destroy 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/views/admin/console/index.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = "Admin Console" %> 2 | 3 |
    4 |
  • <%= link_to "Raise a test StandardError", admin_raise_path %>
  • 5 |
  • <%= link_to "Raise a test ActiveRecord::RecordNotFound", admin_raise_path('record_not_found') %>
  • 6 |
  • <%= link_to "Raise a test ArgumentError", admin_raise_path('argument_error') %>
  • 7 |
8 | -------------------------------------------------------------------------------- /app/views/admin/courses/new.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = "New Course" %> 2 | 3 | <%= render 'form', url: admin_courses_path %> 4 | -------------------------------------------------------------------------------- /app/views/admin/demo/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 | <% [ 'users', 'import', 'course', 'assign', 'work', 'all' ].each do |method| %> 3 |
  • <%= link_to method.capitalize, send("admin_demo_#{method}_url") %>
  • 4 | <% end %> 5 |
6 | -------------------------------------------------------------------------------- /app/views/admin/districts/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @district, as: :district, url: url do |f| %> 2 |
3 | <%= f.label :name %> 4 | <%= f.text_field :name, class: 'form-control' %> 5 |
6 | 7 | <%= f.submit 'Save', class: 'btn btn-primary' %> 8 | <% end %> 9 | -------------------------------------------------------------------------------- /app/views/admin/districts/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'form', url: admin_district_path(@district) %> 2 | -------------------------------------------------------------------------------- /app/views/admin/districts/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'form', url: admin_districts_path %> 2 | -------------------------------------------------------------------------------- /app/views/admin/ecosystems/new.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = 'Import a new Ecosystem' %> 2 | 3 | <%= render partial: 'manager/ecosystems/import', 4 | locals: { ecosystems_path: admin_ecosystems_path } %> 5 | -------------------------------------------------------------------------------- /app/views/admin/notifications/index.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = "Active Notifications" %> 2 | 3 | Current time on page load: <%= Time.current.to_s %> 4 | 5 | <%= render partial: 'index', locals: { type: :general } %> 6 | 7 | <%= render partial: 'index', locals: { type: :instructor } %> 8 | -------------------------------------------------------------------------------- /app/views/admin/periods/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = "Edit Period" %> 2 | 3 | <%= render 'form', url: admin_period_path(@period.id), method: :put %> 4 | -------------------------------------------------------------------------------- /app/views/admin/periods/new.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = "New Period for #{@course.name}" %> 2 | 3 | <%= render 'form', url: admin_course_periods_path(@course.id) %> 4 | -------------------------------------------------------------------------------- /app/views/admin/schools/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'form', url: admin_school_path(@school) %> 2 | -------------------------------------------------------------------------------- /app/views/admin/schools/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'form', url: admin_schools_path %> 2 | -------------------------------------------------------------------------------- /app/views/admin/tags/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = "Edit Tag" %> 2 | 3 | <%= render 'form', url: admin_tag_path, method: :put %> 4 | -------------------------------------------------------------------------------- /app/views/admin/targeted_contracts/new.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_header = "Create a targeted contract" %> 2 | 3 | <%= render 'form', url: admin_targeted_contracts_path, method: :post %> 4 | -------------------------------------------------------------------------------- /app/views/admin/tests/launch_iframe.html.erb: -------------------------------------------------------------------------------- 1 |