diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..434e730d1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,141 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +max_line_length = 119 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = true +ij_smart_tabs = false +ij_visual_guides = 72, 79, 119 +ij_wrap_on_typing = false + +[{*.markdown,*.md}] +max_line_length = 79 +ij_visual_guides = 79 +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true +ij_markdown_format_tables = true +ij_markdown_insert_quote_arrows_on_wrap = true +ij_markdown_keep_indents_on_empty_lines = false +ij_markdown_keep_line_breaks_inside_text_blocks = true +ij_markdown_max_lines_around_block_elements = 1 +ij_markdown_max_lines_around_header = 1 +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_min_lines_around_header = 1 +ij_markdown_min_lines_between_paragraphs = 1 +ij_markdown_wrap_text_if_long = true +ij_markdown_wrap_text_inside_blockquotes = true + +[{*.py,*.pyw}] +ij_python_add_indent_inside_injections = false +ij_python_align_collections_and_comprehensions = true +ij_python_align_multiline_imports = true +ij_python_align_multiline_parameters = true +ij_python_align_multiline_parameters_in_calls = true +ij_python_blank_line_at_file_end = true +ij_python_blank_lines_after_imports = 1 +ij_python_blank_lines_after_local_imports = 0 +ij_python_blank_lines_around_class = 1 +ij_python_blank_lines_around_method = 1 +ij_python_blank_lines_around_top_level_classes_functions = 2 +ij_python_blank_lines_before_first_method = 0 +ij_python_call_parameters_new_line_after_left_paren = false +ij_python_call_parameters_right_paren_on_new_line = false +ij_python_call_parameters_wrap = normal +ij_python_dict_alignment = 0 +ij_python_dict_new_line_after_left_brace = false +ij_python_dict_new_line_before_right_brace = false +ij_python_dict_wrapping = normal +ij_python_format_injected_fragments = true +ij_python_from_import_new_line_after_left_parenthesis = false +ij_python_from_import_new_line_before_right_parenthesis = false +ij_python_from_import_parentheses_force_if_multiline = false +ij_python_from_import_trailing_comma_if_multiline = false +ij_python_from_import_wrapping = normal +ij_python_hang_closing_brackets = false +ij_python_keep_blank_lines_in_code = 1 +ij_python_keep_blank_lines_in_declarations = 1 +ij_python_keep_indents_on_empty_lines = false +ij_python_keep_line_breaks = true +ij_python_list_new_line_after_left_bracket = false +ij_python_list_new_line_before_right_bracket = false +ij_python_list_wrapping = normal +ij_python_method_parameters_new_line_after_left_paren = false +ij_python_method_parameters_right_paren_on_new_line = false +ij_python_method_parameters_wrap = normal +ij_python_new_line_after_colon = false +ij_python_new_line_after_colon_multi_clause = true +ij_python_optimize_imports_always_split_from_imports = false +ij_python_optimize_imports_case_insensitive_order = false +ij_python_optimize_imports_join_from_imports_with_same_source = false +ij_python_optimize_imports_sort_by_type_first = true +ij_python_optimize_imports_sort_imports = true +ij_python_optimize_imports_sort_names_in_from_imports = false +ij_python_set_new_line_after_left_brace = false +ij_python_set_new_line_before_right_brace = false +ij_python_set_wrapping = normal +ij_python_space_after_comma = true +ij_python_space_after_number_sign = true +ij_python_space_after_py_colon = true +ij_python_space_before_backslash = true +ij_python_space_before_comma = false +ij_python_space_before_for_semicolon = false +ij_python_space_before_lbracket = false +ij_python_space_before_method_call_parentheses = false +ij_python_space_before_method_parentheses = false +ij_python_space_before_number_sign = true +ij_python_space_before_py_colon = false +ij_python_space_within_empty_method_call_parentheses = false +ij_python_space_within_empty_method_parentheses = false +ij_python_spaces_around_additive_operators = true +ij_python_spaces_around_assignment_operators = true +ij_python_spaces_around_bitwise_operators = true +ij_python_spaces_around_eq_in_keyword_argument = false +ij_python_spaces_around_eq_in_named_parameter = false +ij_python_spaces_around_equality_operators = true +ij_python_spaces_around_multiplicative_operators = true +ij_python_spaces_around_power_operator = true +ij_python_spaces_around_relational_operators = true +ij_python_spaces_around_shift_operators = true +ij_python_spaces_within_braces = false +ij_python_spaces_within_brackets = false +ij_python_spaces_within_method_call_parentheses = false +ij_python_spaces_within_method_parentheses = false +ij_python_tuple_new_line_after_left_parenthesis = false +ij_python_tuple_new_line_before_right_parenthesis = false +ij_python_tuple_wrapping = normal +ij_python_use_continuation_indent_for_arguments = false +ij_python_use_continuation_indent_for_collection_and_comprehensions = false +ij_python_use_continuation_indent_for_parameters = true +ij_python_use_trailing_comma_in_arguments_list = false +ij_python_use_trailing_comma_in_collections = false +ij_python_use_trailing_comma_in_parameter_list = false +ij_python_wrap_long_lines = false + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock,uv.lock}] +ij_visual_guides = 119 +ij_toml_keep_indents_on_empty_lines = false + +[{*.yml,*.yaml}] +ij_visual_guides = 119 +tab_width = 2 +indent_size = tab + +[{*.xml,*.xsd}] +ij_visual_guides = 119 +tab_width = 2 +indent_size = tab + +[*.json] +indent_size = 2 +ij_visual_guides = 119 diff --git a/.gitignore b/.gitignore index 744eb8cad..c228bc652 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,266 @@ +# IDE configuration +/.vscode/ /.idea/ -.vscode + +# Python virtual environment +/.[Vv]env/ +/.[Ee]nv/ +/[Vv]env/ +/[Ee]nv/ + +# Local Python version +.python-version + +# Temporary files storage +/temp/ +/tmp/ + +# MkDocs builds +site/ + +### MicrosoftOffice template +*.tmp + +# Word temporary +~$*.doc* + +# Word Auto Backup File +Backup of *.doc* + +# Excel temporary +~$*.xls* + +# Excel Backup File +*.xlk + +# PowerPoint temporary +~$*.ppt* + +# Visio autosave temporary files +*.~vsd* + +### PythonVanilla template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + + +### Linux template +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Diff template +*.patch +*.diff + +### Emacs template +# -*- mode: gitignore; -*- +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +### Backup template +*.bak +*.gho +*.ori +*.orig + +### Vim template +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### Windows template +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Vagrant template +# General +.vagrant/ + +# Log files (if you are creating logs in debug mode, uncomment this) +# *.log + +### GPG template +secring.* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..4926cdfdb --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,57 @@ +# Pre-commit Configuration for Code Quality Management +# ================================================= +# +# This configuration establishes automated code quality checks that run at different +# git hook stages. It ensures consistent code style, identifies potential security +# issues, and maintains high code quality standards across the project. +# +# Installation and Usage +# --------------------- +# 1. Install pre-commit in your development environment: +# pip install pre-commit +# +# 2. Install the git hooks: +# pre-commit install +# +# 3. To run all checks manually: +# pre-commit run --all-files + +# Default hook types installed by 'pre-commit install' +default_install_hook_types: + - pre-commit + +# Specify the Python version for consistency across a team +default_language_version: + python: python3.12 + +repos: + # Standard Pre-commit Hooks + # ------------------------ + # These hooks provide basic file and code quality checks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + # File Quality Checks + - id: check-added-large-files + stages: [ pre-commit ] + - id: check-case-conflict + stages: [ pre-commit ] + - id: check-toml + stages: [ pre-commit ] + + # Security Checks + - id: debug-statements + stages: [ pre-commit ] + + # File Formatting + - id: end-of-file-fixer + stages: [ pre-commit ] + require_serial: true + - id: mixed-line-ending + args: [ "--fix=lf" ] # Enforces Linux-style line endings + stages: [ pre-commit ] + + # Whitespace Management + - id: trailing-whitespace + args: [ "--markdown-linebreak-ext=md" ] + stages: [ pre-commit ] diff --git a/schemas/olx/assets.schema.json b/schemas/olx/assets.schema.json new file mode 100644 index 000000000..6e13d1d8b --- /dev/null +++ b/schemas/olx/assets.schema.json @@ -0,0 +1,68 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/schemas/olx/assets.schema.json", + "title": "OLX Course Assets Policies", + "description": "Course assets policies are defined in \"assets.json\" file inside \"policy\" directory.", + "type": "object", + "additionalProperties": false, + "properties": { + "content_type": { + "description": "The MIME type of the file.", + "type": "string" + }, + "display_name": { + "description": "The file name.", + "type": "string" + }, + "locked": { + "description": "true if users can only access the file from within your course.", + "type": "boolean", + "default": true + }, + "content_son": { + "$ref": "#/$defs/content_son" + }, + "filename": { + "description": "The full path and name of the file in the Open edX Platform.", + "type": "string", + "format": "url" + }, + "import_path": { + "type": "string" + }, + "thumbnail_location": { + "type": "array" + } + }, + "$defs": { + "content_son": { + "type": "object", + "additionalProperties": false, + "properties": { + "category": { + "enum": [ + "asset" + ] + }, + "name": { + "description": "The file name.", + "type": "string" + }, + "course": { + "description": "The course number.", + "type": "string" + }, + "tag": { + "type": "string" + }, + "org": { + "description": "The organization that created the course.", + "type": "string" + }, + "revision": { + "type": "string" + } + } + } + } +} diff --git a/schemas/olx/grading_policy.schema.json b/schemas/olx/grading_policy.schema.json new file mode 100644 index 000000000..3fc5d34c1 --- /dev/null +++ b/schemas/olx/grading_policy.schema.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/schemas/olx/grading_policy.schema.json", + "title": "OLX Course Grading Policy", + "description": "Course grading policies are defined in \"grading_policy.json\" file inside \"policy/\" directory.", + "type": "object", + "additionalProperties": false, + "properties": { + "GRADE_CUTOFFS": { + "description": "The minimal grade for passing the course, and receiving a certificate if offered." + }, + "GRADER": { + "description": "For each assignment type.", + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "min_count": { + "type": "integer" + }, + "weight": { + "$comment": "The total weight of all assignment types must equal 1.0", + "description": "The percentage of the total grade determined by assignments of this type.", + "type": "number" + }, + "type": { + "description": "The name of the assignment type.", + "type": "string" + }, + "drop_count": { + "description": "The number of assignments of this type that can be dropped when calculating the final grade.", + "type": "integer" + }, + "short_label": { + "description": "The label for the assignment type shown on the student’s Progress page.", + "type": "string" + } + } + } + } + } +} diff --git a/schemas/olx/policy.schema.json b/schemas/olx/policy.schema.json new file mode 100644 index 000000000..7ad3de733 --- /dev/null +++ b/schemas/olx/policy.schema.json @@ -0,0 +1,113 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "/schemas/olx/policy.schema.json", + "title": "OLX Course Policies", + "description": "Course policies are defined in \"policy.json\" file inside \"policy/\" directory.", + "type": "object", + "additionalProperties": false, + "properties": { + "start": { + "description": "The start date for the course.", + "$ref": "#/$defs/datetime" + }, + "advertised_start": { + "description": "The start date displayed in the course listing and course about pages.", + "$ref": "#/$defs/datetime" + }, + "disable_policy_graph": { + "description": "Whether the policy graph should be disabled (true) or not (false).", + "type": "boolean" + }, + "enrollment_start": { + "description": "The dates in which students can enroll in the course.", + "$ref": "#/$defs/datetime" + }, + "enrollment_end": { + "description": "The dates in which students can enroll in the course.", + "$ref": "#/$defs/datetime" + }, + "end": { + "description": "The end date for the course.", + "$ref": "#/$defs/datetime" + }, + "end_of_course_survey_url": { + "description": "The URL for an end of course survey. The link is shown after the course is over, next to certificate download links.", + "type": "string", + "format": "url" + }, + "tabs": { + "description": "Custom pages, or tabs, in the courseware.", + "type": "object" + }, + "discussion_blackouts": { + "description": "An array of time intervals during which students cannot create or edit discussion posts.", + "type": "array", + "items": { + "$ref": "#/$defs/datetime_range" + } + }, + "show_calculator": { + "description": "Whether the calculator is shown in the course (true) or not (false).", + "type": "boolean" + }, + "days_early_for_beta": { + "description": "The number of days early that students in the beta-testers group can access the course.", + "type": "integer" + }, + "cohort_config": {}, + "pdf_textbooks": { + "description": "Have pdf-based textbooks on tabs in the courseware." + }, + "html_textbooks": { + "description": "The addition of HTML-based textbooks on tabs in the courseware.", + "deprecated": true + } + }, + "$defs": { + "cohort_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "cohorted": { + "description": "...", + "type": "boolean", + "default": false + }, + "cohorted_discussions": { + "description": "list of discussion topics that should be cohorted. Any not specified in this list are not cohorted.", + "type": "array", + "items": { + "type": "string" + } + }, + "auto_cohort_groups": { + "description": "If cohorted is true, each student is automatically assigned to a random group from this list, creating the group if needed.", + "type": "array", + "items": { + "type": "string" + }, + "examples": [ + ["group A", "group B"] + ] + } + } + }, + "datetime": { + "type": "string", + "pattern": "^20\\d{2}-(?:0\\d|1[0-2])-(?:[0-2]\\d|3[01])T(?:[01]\\d|2[0-3]):[0-5]\\d$", + "examples": [ + "2020-02-20T20:20", + "2020-01-01T00:00", + "2020-12-31T23:59" + ] + }, + "datetime_range": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "$ref": "#/$defs/datetime" + } + } + } +}