rapid-router 5.4.1__py2.py3-none-any.whl → 7.6.8__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- example_project/rapid_router_test_settings.py +164 -0
- example_project/settings.py +152 -0
- example_project/urls.py +15 -0
- example_project/{example_project/wsgi.py → wsgi.py} +2 -1
- game/__init__.py +1 -1
- game/admin.py +43 -4
- game/app_settings.py +3 -7
- game/character.py +26 -18
- game/decor.py +172 -97
- game/end_to_end_tests/base_game_test.py +44 -33
- game/end_to_end_tests/editor_page.py +17 -2
- game/end_to_end_tests/game_page.py +127 -45
- game/end_to_end_tests/selenium_test_case.py +1 -20
- game/end_to_end_tests/test_cow_crashes.py +3 -5
- game/end_to_end_tests/test_language_dropdown.py +14 -0
- game/end_to_end_tests/test_level_editor.py +290 -0
- game/end_to_end_tests/test_level_failures.py +1 -1
- game/end_to_end_tests/test_level_selection.py +79 -0
- game/end_to_end_tests/test_play_through.py +240 -102
- game/end_to_end_tests/test_python_levels.py +44 -13
- game/end_to_end_tests/test_saving_workspace.py +22 -0
- game/forms.py +9 -2
- game/level_management.py +38 -29
- game/messages.py +1218 -203
- game/migrations/0001_squashed_0025_levels_ordering_pt1.py +19 -1
- game/migrations/0026_levels_pt2.py +13 -2
- game/migrations/0032_cannot_turn_left_level.py +13 -2
- game/migrations/0033_recursion_level.py +13 -2
- game/migrations/0034_joes_level.py +13 -2
- game/migrations/0035_disable_route_score_level_70.py +0 -2
- game/migrations/0036_level_score_73.py +0 -2
- game/migrations/0037_level_score_79.py +0 -2
- game/migrations/0038_level_score_40.py +0 -1
- game/migrations/0042_level_score_73.py +0 -2
- game/migrations/0048_add_cow_field_and_blocks.py +0 -2
- game/migrations/0049_level_score_34.py +0 -2
- game/migrations/0050_level_score_40.py +0 -2
- game/migrations/0051_level_score_49.py +0 -1
- game/migrations/0076_level_locked_for_class.py +19 -0
- game/migrations/0077_alter_level_next_level.py +52 -0
- game/migrations/0078_add_block_types.py +23 -0
- game/migrations/0079_populate_block_type_add_cow_blocks.py +60 -0
- game/migrations/0080_level_disable_algorithm_score.py +18 -0
- game/migrations/0081_first_12_levels_no_algo_score.py +29 -0
- game/migrations/0082_level_43_solution.py +16 -0
- game/migrations/0083_add_cows_to_existing_levels.py +195 -0
- game/migrations/0084_alter_block_block_type.py +18 -0
- game/migrations/0085_add_new_blocks.py +53 -0
- game/migrations/0086_loop_levels.py +482 -0
- game/migrations/0087_workspace_python_view_enabled.py +18 -0
- game/migrations/0088_rename_episodes.py +35 -0
- game/migrations/0089_episodes_in_development.py +30 -0
- game/migrations/0090_add_missing_model_solutions.py +144 -0
- game/migrations/0091_disable_algo_score_if_no_model_solution.py +46 -0
- game/migrations/0092_disable_algo_score_in_custom_levels.py +28 -0
- game/migrations/0093_alter_level_character_name.py +18 -0
- game/migrations/0094_add_hint_lesson_subtitle_to_levels.py +28 -0
- game/migrations/0095_level_commands.py +18 -0
- game/migrations/0096_alter_level_commands.py +18 -0
- game/migrations/0097_add_python_den_levels.py +1515 -0
- game/migrations/0098_add_episode_link_fields.py +44 -0
- game/migrations/0099_python_episodes_links.py +103 -0
- game/migrations/0100_reorder_python_levels.py +179 -0
- game/migrations/0101_rename_episodes.py +45 -0
- game/migrations/0102_reoder_episodes_13_14.py +136 -0
- game/migrations/0103_level_1015_solution.py +26 -0
- game/migrations/0104_remove_level_direct_drive.py +17 -0
- game/migrations/0105_delete_invalid_attempts.py +18 -0
- game/migrations/0106_fields_to_snake_case.py +48 -0
- game/migrations/0107_rename_worksheet_link_episode_student_worksheet_link.py +18 -0
- game/migrations/0108_episode_indy_worksheet_link.py +18 -0
- game/migrations/0109_create_episodes_23_and_24.py +99 -0
- game/migrations/0110_remove_episode_indy_worksheet_link_and_more.py +100 -0
- game/migrations/0111_create_worksheets.py +149 -0
- game/migrations/0112_worksheet_locked_classes.py +21 -0
- game/migrations/0113_level_needs_approval.py +18 -0
- game/migrations/0114_default_and_non_student_levels_no_approval.py +31 -0
- game/migrations/0115_level_level__default_does_not_need_approval.py +22 -0
- game/migrations/0116_update_worksheet_video_links.py +68 -0
- game/migrations/0117_update_solutions_to_if_else.py +61 -0
- game/models.py +157 -16
- game/permissions.py +34 -19
- game/python_den_urls.py +26 -0
- game/random_road.py +43 -127
- game/serializers.py +12 -17
- game/static/django_reverse_js/js/reverse.js +171 -0
- game/static/game/css/LilitaOne-Regular.ttf +0 -0
- game/static/game/css/backgrounds.css +14 -10
- game/static/game/css/dataTables.custom.css +4 -2
- game/static/game/css/dataTables.jqueryui.css +561 -320
- game/static/game/css/editor.css +47 -0
- game/static/game/css/game.css +43 -49
- game/static/game/css/game_screen.css +116 -53
- game/static/game/css/jquery.dataTables.css +455 -251
- game/static/game/css/level_editor.css +10 -1
- game/static/game/css/level_selection.css +32 -3
- game/static/game/css/level_share.css +6 -5
- game/static/game/css/skulpt/codemirror.css +1 -0
- game/static/game/image/Python_Den_hero_student.png +0 -0
- game/static/game/image/Python_levels_page.svg +1954 -0
- game/static/game/image/characters/front_view/Electric_van.svg +448 -0
- game/static/game/image/characters/top_view/Electric_van.svg +448 -0
- game/static/game/image/characters/top_view/Sleigh.svg +436 -0
- game/static/game/image/decor/city/solar_panel.svg +1200 -0
- game/static/game/image/decor/farm/solar_panel.svg +86 -0
- game/static/game/image/decor/grass/solar_panel.svg +86 -0
- game/static/game/image/decor/snow/barn.svg +1788 -0
- game/static/game/image/decor/snow/cfc.svg +1050 -147
- game/static/game/image/decor/snow/crops.svg +7370 -0
- game/static/game/image/decor/snow/hospital.svg +1220 -0
- game/static/game/image/decor/snow/house1.svg +971 -0
- game/static/game/image/decor/snow/house2.svg +1574 -0
- game/static/game/image/decor/snow/school.svg +1071 -0
- game/static/game/image/decor/snow/shop.svg +3211 -0
- game/static/game/image/decor/snow/solar_panel.svg +173 -0
- game/static/game/image/electric_van.svg +448 -0
- game/static/game/image/icons/add_house.svg +26 -0
- game/static/game/image/icons/delete_house.svg +26 -0
- game/static/game/image/icons/description.svg +1 -0
- game/static/game/image/icons/hint.svg +1 -0
- game/static/game/image/icons/if_else.svg +3 -0
- game/static/game/image/icons/python.svg +1 -1
- game/static/game/image/if_else_example.png +0 -0
- game/static/game/image/pigeon.svg +684 -0
- game/static/game/image/python_den_header.svg +19 -0
- game/static/game/js/animation.js +84 -28
- game/static/game/js/blockly/msg/js/bg.js +52 -1
- game/static/game/js/blockly/msg/js/ca.js +52 -1
- game/static/game/js/blockly/msg/js/en-gb.js +52 -1
- game/static/game/js/blockly/msg/js/en.js +52 -1
- game/static/game/js/blockly/msg/js/es.js +52 -1
- game/static/game/js/blockly/msg/js/fr.js +52 -1
- game/static/game/js/blockly/msg/js/hi.js +52 -1
- game/static/game/js/blockly/msg/js/it.js +52 -1
- game/static/game/js/blockly/msg/js/pl.js +52 -1
- game/static/game/js/blockly/msg/js/pt-br.js +52 -1
- game/static/game/js/blockly/msg/js/ru.js +52 -1
- game/static/game/js/blockly/msg/js/ur.js +52 -1
- game/static/game/js/blocklyCompiler.js +550 -392
- game/static/game/js/blocklyControl.js +335 -302
- game/static/game/js/blocklyCustomBlocks.js +691 -458
- game/static/game/js/blocklyCustomisations.js +3 -1
- game/static/game/js/button.js +12 -0
- game/static/game/js/cow.js +15 -130
- game/static/game/js/drawing.js +313 -201
- game/static/game/js/editor.js +23 -0
- game/static/game/js/game.js +148 -139
- game/static/game/js/jquery.dataTables.min.js +3 -159
- game/static/game/js/level_editor.js +823 -448
- game/static/game/js/level_moderation.js +33 -2
- game/static/game/js/level_selection.js +62 -25
- game/static/game/js/loadLanguages.js +21 -0
- game/static/game/js/map.js +106 -36
- game/static/game/js/model.js +55 -107
- game/static/game/js/pathFinder.js +73 -72
- game/static/game/js/program.js +184 -193
- game/static/game/js/pythonControl.js +14 -1
- game/static/game/js/scoreboard.js +0 -37
- game/static/game/js/scoreboardSharedLevels.js +48 -0
- game/static/game/js/sharing.js +22 -10
- game/static/game/js/skulpt/codemirror.js +5 -4
- game/static/game/js/skulpt/skulpt-stdlib.js +1 -1
- game/static/game/js/sound.js +52 -5
- game/static/game/js/van.js +0 -7
- game/static/game/raphael_image/characters/top_view/Electric_van.svg +448 -0
- game/static/game/raphael_image/characters/top_view/Sleigh.svg +436 -0
- game/static/game/raphael_image/decor/city/solar_panel.svg +1200 -0
- game/static/game/raphael_image/decor/farm/solar_panel.svg +86 -0
- game/static/game/raphael_image/decor/grass/solar_panel.svg +86 -0
- game/static/game/raphael_image/decor/snow/barn.svg +1788 -0
- game/static/game/raphael_image/decor/snow/cfc.svg +1050 -147
- game/static/game/raphael_image/decor/snow/crops.svg +7370 -0
- game/static/game/raphael_image/decor/snow/hospital.svg +1220 -0
- game/static/game/raphael_image/decor/snow/house1.svg +971 -0
- game/static/game/raphael_image/decor/snow/house2.svg +1574 -0
- game/static/game/raphael_image/decor/snow/school.svg +1071 -0
- game/static/game/raphael_image/decor/snow/shop.svg +3211 -0
- game/static/game/raphael_image/decor/snow/solar_panel.svg +173 -0
- game/static/game/raphael_image/pigeon.svg +685 -0
- game/static/game/raphael_image/sleigh_wreckage.svg +430 -0
- game/static/game/sass/game.scss +22 -6
- game/static/game/sound/clown_horn.mp3 +0 -0
- game/static/game/sound/clown_horn.ogg +0 -0
- game/static/game/sound/electric_van_starting.mp3 +0 -0
- game/static/game/sound/electric_van_starting.ogg +0 -0
- game/static/game/sound/pigeon.mp3 +0 -0
- game/static/game/sound/pigeon.ogg +0 -0
- game/static/game/sound/sleigh_bells.mp3 +0 -0
- game/static/game/sound/sleigh_bells.ogg +0 -0
- game/static/game/sound/sleigh_crash.mp3 +0 -0
- game/static/game/sound/sleigh_crash.ogg +0 -0
- game/templates/game/base.html +35 -15
- game/templates/game/basenonav.html +23 -17
- game/templates/game/game.html +236 -111
- game/templates/game/level_editor.html +353 -275
- game/templates/game/level_moderation.html +19 -6
- game/templates/game/level_selection.html +75 -62
- game/templates/game/python_den_level_selection.html +291 -0
- game/templates/game/python_den_worksheet.html +101 -0
- game/templates/game/scoreboard.html +88 -65
- game/tests/test_level_editor.py +210 -35
- game/tests/test_level_moderation.py +6 -20
- game/tests/test_level_selection.py +332 -11
- game/tests/test_python_den_worksheet.py +85 -0
- game/tests/test_scoreboard.py +258 -66
- game/tests/utils/level.py +43 -3
- game/tests/utils/teacher.py +2 -2
- game/theme.py +21 -21
- game/urls.py +125 -78
- game/views/language_code_conversions.py +90 -0
- game/views/level.py +201 -63
- game/views/level_editor.py +109 -48
- game/views/level_moderation.py +29 -6
- game/views/level_selection.py +179 -56
- game/views/level_solutions.py +600 -106
- game/views/scoreboard.py +181 -66
- game/views/worksheet.py +25 -0
- rapid_router-7.6.8.dist-info/METADATA +174 -0
- {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/RECORD +222 -242
- {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/WHEEL +1 -1
- rapid_router-7.6.8.dist-info/licenses/LICENSE.md +3 -0
- example_project/example_project/__init__.py +0 -1
- example_project/example_project/settings.py +0 -54
- example_project/example_project/urls.py +0 -16
- example_project/manage.py +0 -10
- game/autoconfig.py +0 -59
- game/csp_config.py +0 -23
- game/locale/ar_SA/LC_MESSAGES/django.mo +0 -0
- game/locale/ar_SA/LC_MESSAGES/django.po +0 -405
- game/locale/ar_SA/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/ar_SA/LC_MESSAGES/djangojs.po +0 -743
- game/locale/bg_BG/LC_MESSAGES/django.mo +0 -0
- game/locale/bg_BG/LC_MESSAGES/django.po +0 -405
- game/locale/bg_BG/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/bg_BG/LC_MESSAGES/djangojs.po +0 -739
- game/locale/ca_ES/LC_MESSAGES/django.mo +0 -0
- game/locale/ca_ES/LC_MESSAGES/django.po +0 -405
- game/locale/ca_ES/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/ca_ES/LC_MESSAGES/djangojs.po +0 -740
- game/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
- game/locale/cs_CZ/LC_MESSAGES/django.po +0 -405
- game/locale/cs_CZ/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/cs_CZ/LC_MESSAGES/djangojs.po +0 -741
- game/locale/cy_GB/LC_MESSAGES/django.mo +0 -0
- game/locale/cy_GB/LC_MESSAGES/django.po +0 -405
- game/locale/cy_GB/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/cy_GB/LC_MESSAGES/djangojs.po +0 -743
- game/locale/de_DE/LC_MESSAGES/django.mo +0 -0
- game/locale/de_DE/LC_MESSAGES/django.po +0 -405
- game/locale/de_DE/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/de_DE/LC_MESSAGES/djangojs.po +0 -739
- game/locale/el_GR/LC_MESSAGES/django.mo +0 -0
- game/locale/el_GR/LC_MESSAGES/django.po +0 -405
- game/locale/el_GR/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/el_GR/LC_MESSAGES/djangojs.po +0 -739
- game/locale/en_GB/LC_MESSAGES/django.mo +0 -0
- game/locale/en_GB/LC_MESSAGES/django.po +0 -405
- game/locale/en_GB/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/en_GB/LC_MESSAGES/djangojs.po +0 -739
- game/locale/es_ES/LC_MESSAGES/django.mo +0 -0
- game/locale/es_ES/LC_MESSAGES/django.po +0 -405
- game/locale/es_ES/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/es_ES/LC_MESSAGES/djangojs.po +0 -739
- game/locale/fi_FI/LC_MESSAGES/django.mo +0 -0
- game/locale/fi_FI/LC_MESSAGES/django.po +0 -405
- game/locale/fi_FI/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/fi_FI/LC_MESSAGES/djangojs.po +0 -739
- game/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
- game/locale/fr_FR/LC_MESSAGES/django.po +0 -405
- game/locale/fr_FR/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/fr_FR/LC_MESSAGES/djangojs.po +0 -739
- game/locale/gu_IN/LC_MESSAGES/django.mo +0 -0
- game/locale/gu_IN/LC_MESSAGES/django.po +0 -405
- game/locale/gu_IN/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/gu_IN/LC_MESSAGES/djangojs.po +0 -739
- game/locale/hi_IN/LC_MESSAGES/django.mo +0 -0
- game/locale/hi_IN/LC_MESSAGES/django.po +0 -405
- game/locale/hi_IN/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/hi_IN/LC_MESSAGES/djangojs.po +0 -739
- game/locale/id_ID/LC_MESSAGES/django.mo +0 -0
- game/locale/id_ID/LC_MESSAGES/django.po +0 -405
- game/locale/id_ID/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/id_ID/LC_MESSAGES/djangojs.po +0 -738
- game/locale/it_IT/LC_MESSAGES/django.mo +0 -0
- game/locale/it_IT/LC_MESSAGES/django.po +0 -405
- game/locale/it_IT/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/it_IT/LC_MESSAGES/djangojs.po +0 -739
- game/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
- game/locale/ja_JP/LC_MESSAGES/django.po +0 -405
- game/locale/ja_JP/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/ja_JP/LC_MESSAGES/djangojs.po +0 -738
- game/locale/lol_US/LC_MESSAGES/django.mo +0 -0
- game/locale/lol_US/LC_MESSAGES/django.po +0 -405
- game/locale/lol_US/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/lol_US/LC_MESSAGES/djangojs.po +0 -739
- game/locale/nb_NO/LC_MESSAGES/django.mo +0 -0
- game/locale/nb_NO/LC_MESSAGES/django.po +0 -405
- game/locale/nb_NO/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/nb_NO/LC_MESSAGES/djangojs.po +0 -739
- game/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
- game/locale/nl_NL/LC_MESSAGES/django.po +0 -405
- game/locale/nl_NL/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/nl_NL/LC_MESSAGES/djangojs.po +0 -739
- game/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
- game/locale/pl_PL/LC_MESSAGES/django.po +0 -405
- game/locale/pl_PL/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/pl_PL/LC_MESSAGES/djangojs.po +0 -741
- game/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- game/locale/pt_BR/LC_MESSAGES/django.po +0 -405
- game/locale/pt_BR/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/pt_BR/LC_MESSAGES/djangojs.po +0 -739
- game/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- game/locale/pt_PT/LC_MESSAGES/django.po +0 -405
- game/locale/pt_PT/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/pt_PT/LC_MESSAGES/djangojs.po +0 -739
- game/locale/ro_RO/LC_MESSAGES/django.mo +0 -0
- game/locale/ro_RO/LC_MESSAGES/django.po +0 -405
- game/locale/ro_RO/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/ro_RO/LC_MESSAGES/djangojs.po +0 -740
- game/locale/ru_RU/LC_MESSAGES/django.mo +0 -0
- game/locale/ru_RU/LC_MESSAGES/django.po +0 -405
- game/locale/ru_RU/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/ru_RU/LC_MESSAGES/djangojs.po +0 -741
- game/locale/sv_SE/LC_MESSAGES/django.mo +0 -0
- game/locale/sv_SE/LC_MESSAGES/django.po +0 -405
- game/locale/sv_SE/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/sv_SE/LC_MESSAGES/djangojs.po +0 -739
- game/locale/tl_PH/LC_MESSAGES/django.mo +0 -0
- game/locale/tl_PH/LC_MESSAGES/django.po +0 -405
- game/locale/tl_PH/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/tl_PH/LC_MESSAGES/djangojs.po +0 -739
- game/locale/tlh_AA/LC_MESSAGES/django.mo +0 -0
- game/locale/tlh_AA/LC_MESSAGES/django.po +0 -405
- game/locale/tlh_AA/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/tlh_AA/LC_MESSAGES/djangojs.po +0 -739
- game/locale/tr_TR/LC_MESSAGES/django.mo +0 -0
- game/locale/tr_TR/LC_MESSAGES/django.po +0 -405
- game/locale/tr_TR/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/tr_TR/LC_MESSAGES/djangojs.po +0 -740
- game/locale/ur_IN/LC_MESSAGES/django.mo +0 -0
- game/locale/ur_IN/LC_MESSAGES/django.po +0 -405
- game/locale/ur_IN/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/ur_IN/LC_MESSAGES/djangojs.po +0 -739
- game/locale/ur_PK/LC_MESSAGES/django.mo +0 -0
- game/locale/ur_PK/LC_MESSAGES/django.po +0 -405
- game/locale/ur_PK/LC_MESSAGES/djangojs.mo +0 -0
- game/locale/ur_PK/LC_MESSAGES/djangojs.po +0 -739
- game/static/game/image/actions/go.svg +0 -18
- game/static/game/image/icons/destination.svg +0 -9
- game/static/game/js/pqselect.min.js +0 -9
- game/static/game/js/widget-scroller.js +0 -906
- rapid_router-5.4.1.dist-info/LICENSE.md +0 -577
- rapid_router-5.4.1.dist-info/METADATA +0 -24
- {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/top_level.txt +0 -0
game/views/level_moderation.py
CHANGED
|
@@ -2,7 +2,10 @@ from __future__ import absolute_import
|
|
|
2
2
|
from __future__ import division
|
|
3
3
|
|
|
4
4
|
from common.models import Student, Class
|
|
5
|
+
from django.http import HttpResponse, HttpResponseRedirect
|
|
5
6
|
from django.shortcuts import render
|
|
7
|
+
from django.urls import reverse_lazy
|
|
8
|
+
from django.views.decorators.http import require_POST
|
|
6
9
|
from portal.templatetags import app_tags
|
|
7
10
|
|
|
8
11
|
import game.messages as messages
|
|
@@ -34,8 +37,8 @@ def level_moderation(request):
|
|
|
34
37
|
if not permissions.can_see_level_moderation(request.user):
|
|
35
38
|
return renderError(
|
|
36
39
|
request,
|
|
37
|
-
messages.
|
|
38
|
-
messages.
|
|
40
|
+
messages.no_permission_level_moderation_title(),
|
|
41
|
+
messages.no_permission_level_moderation_page(),
|
|
39
42
|
)
|
|
40
43
|
|
|
41
44
|
teacher = request.user.userprofile.teacher
|
|
@@ -54,8 +57,8 @@ def level_moderation(request):
|
|
|
54
57
|
if len(classes_taught) <= 0:
|
|
55
58
|
return renderError(
|
|
56
59
|
request,
|
|
57
|
-
messages.
|
|
58
|
-
messages.
|
|
60
|
+
messages.no_permission_level_moderation_title(),
|
|
61
|
+
messages.no_data_to_show_level_moderation(),
|
|
59
62
|
)
|
|
60
63
|
|
|
61
64
|
classes_taught_ids = [class_.id for class_ in classes_taught]
|
|
@@ -74,8 +77,8 @@ def level_moderation(request):
|
|
|
74
77
|
if not all(class_id in classes_taught_ids for class_id in class_ids):
|
|
75
78
|
return renderError(
|
|
76
79
|
request,
|
|
77
|
-
messages.
|
|
78
|
-
messages.
|
|
80
|
+
messages.no_permission_level_moderation_title(),
|
|
81
|
+
messages.no_permission_level_moderation_class(),
|
|
79
82
|
)
|
|
80
83
|
else:
|
|
81
84
|
class_ids = []
|
|
@@ -132,6 +135,7 @@ def level_moderation(request):
|
|
|
132
135
|
"id": level.id,
|
|
133
136
|
"name": level.name,
|
|
134
137
|
"shared_with": shared_str,
|
|
138
|
+
"needs_approval": level.needs_approval,
|
|
135
139
|
}
|
|
136
140
|
)
|
|
137
141
|
|
|
@@ -144,3 +148,22 @@ def level_moderation(request):
|
|
|
144
148
|
"thead": table_headers,
|
|
145
149
|
},
|
|
146
150
|
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@require_POST
|
|
154
|
+
def approve_level(request, levelID):
|
|
155
|
+
level = Level.objects.get(id=levelID)
|
|
156
|
+
if permissions.can_approve_level(request.user, level):
|
|
157
|
+
level.needs_approval = False
|
|
158
|
+
level.save()
|
|
159
|
+
|
|
160
|
+
return HttpResponseRedirect(reverse_lazy("level_moderation"))
|
|
161
|
+
else:
|
|
162
|
+
return HttpResponseUnauthorized()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class HttpResponseUnauthorized(HttpResponse):
|
|
166
|
+
def __init__(self):
|
|
167
|
+
super(HttpResponseUnauthorized, self).__init__(
|
|
168
|
+
content="Unauthorized", status=401
|
|
169
|
+
)
|
game/views/level_selection.py
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
from __future__ import absolute_import
|
|
2
|
-
from __future__ import division
|
|
1
|
+
from __future__ import absolute_import, division
|
|
3
2
|
|
|
4
3
|
from builtins import str
|
|
5
4
|
|
|
5
|
+
import game.level_management as level_management
|
|
6
|
+
import game.messages as messages
|
|
6
7
|
from django.core.cache import cache
|
|
7
8
|
from django.db.models import Max
|
|
8
9
|
from django.shortcuts import render
|
|
9
10
|
from django.utils.safestring import mark_safe
|
|
10
|
-
|
|
11
|
-
import game.level_management as level_management
|
|
12
|
-
import game.messages as messages
|
|
13
|
-
from game import app_settings
|
|
14
|
-
from game import random_road
|
|
11
|
+
from game import app_settings, random_road
|
|
15
12
|
from game.cache import cached_episode
|
|
16
|
-
from game.models import Attempt, Episode
|
|
13
|
+
from game.models import Attempt, Episode, Level, Worksheet
|
|
14
|
+
|
|
17
15
|
from .level_editor import play_anonymous_level
|
|
18
16
|
|
|
19
17
|
|
|
@@ -29,7 +27,7 @@ def max_score(level):
|
|
|
29
27
|
def fetch_episode_data_from_database(early_access, start, end):
|
|
30
28
|
episode_data = []
|
|
31
29
|
episode = Episode.objects.get(pk=start)
|
|
32
|
-
|
|
30
|
+
|
|
33
31
|
while episode is not None:
|
|
34
32
|
if episode.in_development and not early_access:
|
|
35
33
|
break
|
|
@@ -63,12 +61,12 @@ def fetch_episode_data_from_database(early_access, start, end):
|
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
episode_data.append(e)
|
|
66
|
-
episode = episode.next_episode
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
if current > end:
|
|
65
|
+
if episode.id == end:
|
|
70
66
|
break
|
|
71
67
|
|
|
68
|
+
episode = episode.next_episode
|
|
69
|
+
|
|
72
70
|
return episode_data
|
|
73
71
|
|
|
74
72
|
|
|
@@ -81,17 +79,56 @@ def fetch_episode_data(early_access, start=1, end=12):
|
|
|
81
79
|
if data is None:
|
|
82
80
|
data = fetch_episode_data_from_database(early_access, start, end)
|
|
83
81
|
cache.set(key, data)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
82
|
+
|
|
83
|
+
def worksheet_to_dict(index, worksheet):
|
|
84
|
+
return {
|
|
85
|
+
"id": worksheet.id,
|
|
86
|
+
"index": index,
|
|
87
|
+
"before_level": worksheet.before_level_id,
|
|
88
|
+
"lesson_plan_link": worksheet.lesson_plan_link,
|
|
89
|
+
"slides_link": worksheet.slides_link,
|
|
90
|
+
"student_worksheet_link": worksheet.student_worksheet_link,
|
|
91
|
+
"indy_worksheet_link": worksheet.indy_worksheet_link,
|
|
92
|
+
"video_link": worksheet.video_link,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
episodes = []
|
|
96
|
+
for episode in data:
|
|
97
|
+
worksheets = [
|
|
98
|
+
worksheet_to_dict(index, worksheet)
|
|
99
|
+
for index, worksheet in enumerate(
|
|
100
|
+
Worksheet.objects.filter(
|
|
101
|
+
episode=episode["id"],
|
|
102
|
+
before_level__isnull=False,
|
|
103
|
+
).order_by("before_level"),
|
|
104
|
+
start=1,
|
|
105
|
+
)
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
worksheets += [
|
|
109
|
+
worksheet_to_dict(index, worksheet)
|
|
110
|
+
for index, worksheet in enumerate(
|
|
111
|
+
Worksheet.objects.filter(
|
|
112
|
+
episode=episode["id"],
|
|
113
|
+
before_level__isnull=True,
|
|
114
|
+
),
|
|
115
|
+
start=1 + len(worksheets),
|
|
116
|
+
)
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
episodes.append(
|
|
120
|
+
dict(
|
|
121
|
+
episode,
|
|
122
|
+
name=messages.get_episode_title(episode["id"]),
|
|
123
|
+
levels=[
|
|
124
|
+
dict(level, title=get_level_title(level["name"]))
|
|
125
|
+
for level in episode["levels"]
|
|
126
|
+
],
|
|
127
|
+
worksheets=worksheets,
|
|
128
|
+
)
|
|
92
129
|
)
|
|
93
|
-
|
|
94
|
-
|
|
130
|
+
|
|
131
|
+
return episodes
|
|
95
132
|
|
|
96
133
|
|
|
97
134
|
def get_level_title(i):
|
|
@@ -111,7 +148,28 @@ def is_student(user):
|
|
|
111
148
|
return hasattr(user.userprofile, "student")
|
|
112
149
|
|
|
113
150
|
|
|
114
|
-
def
|
|
151
|
+
def is_teacher(user):
|
|
152
|
+
return hasattr(user.userprofile, "teacher")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def is_admin_teacher(user):
|
|
156
|
+
return (
|
|
157
|
+
hasattr(user.userprofile, "teacher")
|
|
158
|
+
and user.userprofile.teacher.is_admin
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_blockly_episodes(request):
|
|
163
|
+
return fetch_episode_data(app_settings.EARLY_ACCESS_FUNCTION(request), 1, 9)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def get_python_episodes(request):
|
|
167
|
+
return fetch_episode_data(
|
|
168
|
+
app_settings.EARLY_ACCESS_FUNCTION(request), 16, 24
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def levels(request, language):
|
|
115
173
|
"""Loads a page with all levels listed.
|
|
116
174
|
|
|
117
175
|
**Context**
|
|
@@ -136,24 +194,13 @@ def levels(request):
|
|
|
136
194
|
else:
|
|
137
195
|
attempts = {}
|
|
138
196
|
|
|
139
|
-
blockly_episodes = fetch_episode_data(
|
|
140
|
-
app_settings.EARLY_ACCESS_FUNCTION(request), 1, 9
|
|
141
|
-
)
|
|
142
|
-
for episode in blockly_episodes:
|
|
143
|
-
for level in episode["levels"]:
|
|
144
|
-
attach_attempts_to_level(attempts, level)
|
|
145
|
-
|
|
146
|
-
python_episodes = fetch_episode_data(
|
|
147
|
-
app_settings.EARLY_ACCESS_FUNCTION(request), 10, 11
|
|
148
|
-
)
|
|
149
|
-
for episode in python_episodes:
|
|
150
|
-
for level in episode["levels"]:
|
|
151
|
-
attach_attempts_to_level(attempts, level)
|
|
152
|
-
|
|
153
197
|
owned_level_data = []
|
|
154
|
-
|
|
198
|
+
directly_shared_levels = []
|
|
199
|
+
indirectly_shared_levels = {}
|
|
155
200
|
if not request.user.is_anonymous:
|
|
156
|
-
owned_levels, shared_levels = level_management.get_loadable_levels(
|
|
201
|
+
owned_levels, shared_levels = level_management.get_loadable_levels(
|
|
202
|
+
request.user
|
|
203
|
+
)
|
|
157
204
|
|
|
158
205
|
for level in owned_levels:
|
|
159
206
|
owned_level_data.append(
|
|
@@ -166,29 +213,105 @@ def levels(request):
|
|
|
166
213
|
)
|
|
167
214
|
|
|
168
215
|
for level in shared_levels:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
216
|
+
# if user is an admin teacher, sort levels by their own classes first (directly shared levels) and then by
|
|
217
|
+
# other classes in the school (indirectly shared levels). For each of those, get the levels owned by a
|
|
218
|
+
# teacher as well as those owned by a student.
|
|
219
|
+
if is_admin_teacher(user):
|
|
220
|
+
# get levels shared by fellow teachers
|
|
221
|
+
if is_teacher(level.owner.user):
|
|
222
|
+
teacher = level.owner.teacher.new_user
|
|
223
|
+
|
|
224
|
+
if teacher not in indirectly_shared_levels:
|
|
225
|
+
indirectly_shared_levels[teacher] = []
|
|
226
|
+
|
|
227
|
+
indirectly_shared_levels[teacher].append(
|
|
228
|
+
get_shared_level(level, attempts)
|
|
229
|
+
)
|
|
230
|
+
else:
|
|
231
|
+
student_class = level.owner.student.class_field
|
|
232
|
+
class_teacher = student_class.teacher.new_user
|
|
233
|
+
|
|
234
|
+
# get levels shared by students in the current user's classes
|
|
235
|
+
if class_teacher == user:
|
|
236
|
+
directly_shared_levels.append(
|
|
237
|
+
get_shared_level(level, attempts, student_class)
|
|
238
|
+
)
|
|
239
|
+
# get levels shared by students in the other teachers' classes
|
|
240
|
+
else:
|
|
241
|
+
if class_teacher not in indirectly_shared_levels:
|
|
242
|
+
indirectly_shared_levels[class_teacher] = []
|
|
243
|
+
|
|
244
|
+
indirectly_shared_levels[class_teacher].append(
|
|
245
|
+
get_shared_level(level, attempts, student_class)
|
|
246
|
+
)
|
|
247
|
+
# if user is a student or a standard teacher, just get levels shared with them directly.
|
|
248
|
+
else:
|
|
249
|
+
directly_shared_levels.append(get_shared_level(level, attempts))
|
|
250
|
+
|
|
251
|
+
context = {
|
|
252
|
+
"owned_levels": owned_level_data,
|
|
253
|
+
"directly_shared_levels": directly_shared_levels,
|
|
254
|
+
"indirectly_shared_levels": indirectly_shared_levels,
|
|
255
|
+
"scores": attempts,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if language == "blockly":
|
|
259
|
+
blockly_episodes = get_blockly_episodes(request)
|
|
260
|
+
|
|
261
|
+
for episode in blockly_episodes:
|
|
262
|
+
for level in episode["levels"]:
|
|
263
|
+
attach_attempts_to_level(attempts, level)
|
|
264
|
+
level["locked_for_class"] = Level.objects.get(
|
|
265
|
+
id=level["id"]
|
|
266
|
+
).locked_for_class
|
|
267
|
+
|
|
268
|
+
context["blocklyEpisodes"] = blockly_episodes
|
|
269
|
+
|
|
270
|
+
elif language == "python":
|
|
271
|
+
python_episodes = get_python_episodes(request)
|
|
272
|
+
|
|
273
|
+
for episode in python_episodes:
|
|
274
|
+
for level in episode["levels"]:
|
|
275
|
+
attach_attempts_to_level(attempts, level)
|
|
276
|
+
level["locked_for_class"] = Level.objects.get(
|
|
277
|
+
id=level["id"]
|
|
278
|
+
).locked_for_class
|
|
279
|
+
|
|
280
|
+
for worksheet in episode["worksheets"]:
|
|
281
|
+
worksheet["locked_classes"] = Worksheet.objects.get(
|
|
282
|
+
id=worksheet["id"]
|
|
283
|
+
).locked_classes
|
|
284
|
+
|
|
285
|
+
context["pythonEpisodes"] = python_episodes
|
|
286
|
+
|
|
287
|
+
return context
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def blockly_levels(request):
|
|
291
|
+
return render(
|
|
292
|
+
request, "game/level_selection.html", context=levels(request, "blockly")
|
|
293
|
+
)
|
|
294
|
+
|
|
178
295
|
|
|
296
|
+
def python_levels(request):
|
|
179
297
|
return render(
|
|
180
298
|
request,
|
|
181
|
-
"game/
|
|
182
|
-
context=
|
|
183
|
-
"blocklyEpisodes": blockly_episodes,
|
|
184
|
-
"pythonEpisodes": python_episodes,
|
|
185
|
-
"owned_levels": owned_level_data,
|
|
186
|
-
"shared_levels": shared_level_data,
|
|
187
|
-
"scores": attempts,
|
|
188
|
-
},
|
|
299
|
+
"game/python_den_level_selection.html",
|
|
300
|
+
context=levels(request, "python"),
|
|
189
301
|
)
|
|
190
302
|
|
|
191
303
|
|
|
304
|
+
def get_shared_level(level, attempts, student_class=None):
|
|
305
|
+
return {
|
|
306
|
+
"id": level.id,
|
|
307
|
+
"title": level.name,
|
|
308
|
+
"owner": level.owner.user,
|
|
309
|
+
"class": student_class,
|
|
310
|
+
"score": attempts.get(level.id),
|
|
311
|
+
"maxScore": 10,
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
|
|
192
315
|
def random_level_for_episode(request, episodeID):
|
|
193
316
|
"""Generates a new random level based on the episodeID
|
|
194
317
|
|