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.
Files changed (354) hide show
  1. example_project/rapid_router_test_settings.py +164 -0
  2. example_project/settings.py +152 -0
  3. example_project/urls.py +15 -0
  4. example_project/{example_project/wsgi.py → wsgi.py} +2 -1
  5. game/__init__.py +1 -1
  6. game/admin.py +43 -4
  7. game/app_settings.py +3 -7
  8. game/character.py +26 -18
  9. game/decor.py +172 -97
  10. game/end_to_end_tests/base_game_test.py +44 -33
  11. game/end_to_end_tests/editor_page.py +17 -2
  12. game/end_to_end_tests/game_page.py +127 -45
  13. game/end_to_end_tests/selenium_test_case.py +1 -20
  14. game/end_to_end_tests/test_cow_crashes.py +3 -5
  15. game/end_to_end_tests/test_language_dropdown.py +14 -0
  16. game/end_to_end_tests/test_level_editor.py +290 -0
  17. game/end_to_end_tests/test_level_failures.py +1 -1
  18. game/end_to_end_tests/test_level_selection.py +79 -0
  19. game/end_to_end_tests/test_play_through.py +240 -102
  20. game/end_to_end_tests/test_python_levels.py +44 -13
  21. game/end_to_end_tests/test_saving_workspace.py +22 -0
  22. game/forms.py +9 -2
  23. game/level_management.py +38 -29
  24. game/messages.py +1218 -203
  25. game/migrations/0001_squashed_0025_levels_ordering_pt1.py +19 -1
  26. game/migrations/0026_levels_pt2.py +13 -2
  27. game/migrations/0032_cannot_turn_left_level.py +13 -2
  28. game/migrations/0033_recursion_level.py +13 -2
  29. game/migrations/0034_joes_level.py +13 -2
  30. game/migrations/0035_disable_route_score_level_70.py +0 -2
  31. game/migrations/0036_level_score_73.py +0 -2
  32. game/migrations/0037_level_score_79.py +0 -2
  33. game/migrations/0038_level_score_40.py +0 -1
  34. game/migrations/0042_level_score_73.py +0 -2
  35. game/migrations/0048_add_cow_field_and_blocks.py +0 -2
  36. game/migrations/0049_level_score_34.py +0 -2
  37. game/migrations/0050_level_score_40.py +0 -2
  38. game/migrations/0051_level_score_49.py +0 -1
  39. game/migrations/0076_level_locked_for_class.py +19 -0
  40. game/migrations/0077_alter_level_next_level.py +52 -0
  41. game/migrations/0078_add_block_types.py +23 -0
  42. game/migrations/0079_populate_block_type_add_cow_blocks.py +60 -0
  43. game/migrations/0080_level_disable_algorithm_score.py +18 -0
  44. game/migrations/0081_first_12_levels_no_algo_score.py +29 -0
  45. game/migrations/0082_level_43_solution.py +16 -0
  46. game/migrations/0083_add_cows_to_existing_levels.py +195 -0
  47. game/migrations/0084_alter_block_block_type.py +18 -0
  48. game/migrations/0085_add_new_blocks.py +53 -0
  49. game/migrations/0086_loop_levels.py +482 -0
  50. game/migrations/0087_workspace_python_view_enabled.py +18 -0
  51. game/migrations/0088_rename_episodes.py +35 -0
  52. game/migrations/0089_episodes_in_development.py +30 -0
  53. game/migrations/0090_add_missing_model_solutions.py +144 -0
  54. game/migrations/0091_disable_algo_score_if_no_model_solution.py +46 -0
  55. game/migrations/0092_disable_algo_score_in_custom_levels.py +28 -0
  56. game/migrations/0093_alter_level_character_name.py +18 -0
  57. game/migrations/0094_add_hint_lesson_subtitle_to_levels.py +28 -0
  58. game/migrations/0095_level_commands.py +18 -0
  59. game/migrations/0096_alter_level_commands.py +18 -0
  60. game/migrations/0097_add_python_den_levels.py +1515 -0
  61. game/migrations/0098_add_episode_link_fields.py +44 -0
  62. game/migrations/0099_python_episodes_links.py +103 -0
  63. game/migrations/0100_reorder_python_levels.py +179 -0
  64. game/migrations/0101_rename_episodes.py +45 -0
  65. game/migrations/0102_reoder_episodes_13_14.py +136 -0
  66. game/migrations/0103_level_1015_solution.py +26 -0
  67. game/migrations/0104_remove_level_direct_drive.py +17 -0
  68. game/migrations/0105_delete_invalid_attempts.py +18 -0
  69. game/migrations/0106_fields_to_snake_case.py +48 -0
  70. game/migrations/0107_rename_worksheet_link_episode_student_worksheet_link.py +18 -0
  71. game/migrations/0108_episode_indy_worksheet_link.py +18 -0
  72. game/migrations/0109_create_episodes_23_and_24.py +99 -0
  73. game/migrations/0110_remove_episode_indy_worksheet_link_and_more.py +100 -0
  74. game/migrations/0111_create_worksheets.py +149 -0
  75. game/migrations/0112_worksheet_locked_classes.py +21 -0
  76. game/migrations/0113_level_needs_approval.py +18 -0
  77. game/migrations/0114_default_and_non_student_levels_no_approval.py +31 -0
  78. game/migrations/0115_level_level__default_does_not_need_approval.py +22 -0
  79. game/migrations/0116_update_worksheet_video_links.py +68 -0
  80. game/migrations/0117_update_solutions_to_if_else.py +61 -0
  81. game/models.py +157 -16
  82. game/permissions.py +34 -19
  83. game/python_den_urls.py +26 -0
  84. game/random_road.py +43 -127
  85. game/serializers.py +12 -17
  86. game/static/django_reverse_js/js/reverse.js +171 -0
  87. game/static/game/css/LilitaOne-Regular.ttf +0 -0
  88. game/static/game/css/backgrounds.css +14 -10
  89. game/static/game/css/dataTables.custom.css +4 -2
  90. game/static/game/css/dataTables.jqueryui.css +561 -320
  91. game/static/game/css/editor.css +47 -0
  92. game/static/game/css/game.css +43 -49
  93. game/static/game/css/game_screen.css +116 -53
  94. game/static/game/css/jquery.dataTables.css +455 -251
  95. game/static/game/css/level_editor.css +10 -1
  96. game/static/game/css/level_selection.css +32 -3
  97. game/static/game/css/level_share.css +6 -5
  98. game/static/game/css/skulpt/codemirror.css +1 -0
  99. game/static/game/image/Python_Den_hero_student.png +0 -0
  100. game/static/game/image/Python_levels_page.svg +1954 -0
  101. game/static/game/image/characters/front_view/Electric_van.svg +448 -0
  102. game/static/game/image/characters/top_view/Electric_van.svg +448 -0
  103. game/static/game/image/characters/top_view/Sleigh.svg +436 -0
  104. game/static/game/image/decor/city/solar_panel.svg +1200 -0
  105. game/static/game/image/decor/farm/solar_panel.svg +86 -0
  106. game/static/game/image/decor/grass/solar_panel.svg +86 -0
  107. game/static/game/image/decor/snow/barn.svg +1788 -0
  108. game/static/game/image/decor/snow/cfc.svg +1050 -147
  109. game/static/game/image/decor/snow/crops.svg +7370 -0
  110. game/static/game/image/decor/snow/hospital.svg +1220 -0
  111. game/static/game/image/decor/snow/house1.svg +971 -0
  112. game/static/game/image/decor/snow/house2.svg +1574 -0
  113. game/static/game/image/decor/snow/school.svg +1071 -0
  114. game/static/game/image/decor/snow/shop.svg +3211 -0
  115. game/static/game/image/decor/snow/solar_panel.svg +173 -0
  116. game/static/game/image/electric_van.svg +448 -0
  117. game/static/game/image/icons/add_house.svg +26 -0
  118. game/static/game/image/icons/delete_house.svg +26 -0
  119. game/static/game/image/icons/description.svg +1 -0
  120. game/static/game/image/icons/hint.svg +1 -0
  121. game/static/game/image/icons/if_else.svg +3 -0
  122. game/static/game/image/icons/python.svg +1 -1
  123. game/static/game/image/if_else_example.png +0 -0
  124. game/static/game/image/pigeon.svg +684 -0
  125. game/static/game/image/python_den_header.svg +19 -0
  126. game/static/game/js/animation.js +84 -28
  127. game/static/game/js/blockly/msg/js/bg.js +52 -1
  128. game/static/game/js/blockly/msg/js/ca.js +52 -1
  129. game/static/game/js/blockly/msg/js/en-gb.js +52 -1
  130. game/static/game/js/blockly/msg/js/en.js +52 -1
  131. game/static/game/js/blockly/msg/js/es.js +52 -1
  132. game/static/game/js/blockly/msg/js/fr.js +52 -1
  133. game/static/game/js/blockly/msg/js/hi.js +52 -1
  134. game/static/game/js/blockly/msg/js/it.js +52 -1
  135. game/static/game/js/blockly/msg/js/pl.js +52 -1
  136. game/static/game/js/blockly/msg/js/pt-br.js +52 -1
  137. game/static/game/js/blockly/msg/js/ru.js +52 -1
  138. game/static/game/js/blockly/msg/js/ur.js +52 -1
  139. game/static/game/js/blocklyCompiler.js +550 -392
  140. game/static/game/js/blocklyControl.js +335 -302
  141. game/static/game/js/blocklyCustomBlocks.js +691 -458
  142. game/static/game/js/blocklyCustomisations.js +3 -1
  143. game/static/game/js/button.js +12 -0
  144. game/static/game/js/cow.js +15 -130
  145. game/static/game/js/drawing.js +313 -201
  146. game/static/game/js/editor.js +23 -0
  147. game/static/game/js/game.js +148 -139
  148. game/static/game/js/jquery.dataTables.min.js +3 -159
  149. game/static/game/js/level_editor.js +823 -448
  150. game/static/game/js/level_moderation.js +33 -2
  151. game/static/game/js/level_selection.js +62 -25
  152. game/static/game/js/loadLanguages.js +21 -0
  153. game/static/game/js/map.js +106 -36
  154. game/static/game/js/model.js +55 -107
  155. game/static/game/js/pathFinder.js +73 -72
  156. game/static/game/js/program.js +184 -193
  157. game/static/game/js/pythonControl.js +14 -1
  158. game/static/game/js/scoreboard.js +0 -37
  159. game/static/game/js/scoreboardSharedLevels.js +48 -0
  160. game/static/game/js/sharing.js +22 -10
  161. game/static/game/js/skulpt/codemirror.js +5 -4
  162. game/static/game/js/skulpt/skulpt-stdlib.js +1 -1
  163. game/static/game/js/sound.js +52 -5
  164. game/static/game/js/van.js +0 -7
  165. game/static/game/raphael_image/characters/top_view/Electric_van.svg +448 -0
  166. game/static/game/raphael_image/characters/top_view/Sleigh.svg +436 -0
  167. game/static/game/raphael_image/decor/city/solar_panel.svg +1200 -0
  168. game/static/game/raphael_image/decor/farm/solar_panel.svg +86 -0
  169. game/static/game/raphael_image/decor/grass/solar_panel.svg +86 -0
  170. game/static/game/raphael_image/decor/snow/barn.svg +1788 -0
  171. game/static/game/raphael_image/decor/snow/cfc.svg +1050 -147
  172. game/static/game/raphael_image/decor/snow/crops.svg +7370 -0
  173. game/static/game/raphael_image/decor/snow/hospital.svg +1220 -0
  174. game/static/game/raphael_image/decor/snow/house1.svg +971 -0
  175. game/static/game/raphael_image/decor/snow/house2.svg +1574 -0
  176. game/static/game/raphael_image/decor/snow/school.svg +1071 -0
  177. game/static/game/raphael_image/decor/snow/shop.svg +3211 -0
  178. game/static/game/raphael_image/decor/snow/solar_panel.svg +173 -0
  179. game/static/game/raphael_image/pigeon.svg +685 -0
  180. game/static/game/raphael_image/sleigh_wreckage.svg +430 -0
  181. game/static/game/sass/game.scss +22 -6
  182. game/static/game/sound/clown_horn.mp3 +0 -0
  183. game/static/game/sound/clown_horn.ogg +0 -0
  184. game/static/game/sound/electric_van_starting.mp3 +0 -0
  185. game/static/game/sound/electric_van_starting.ogg +0 -0
  186. game/static/game/sound/pigeon.mp3 +0 -0
  187. game/static/game/sound/pigeon.ogg +0 -0
  188. game/static/game/sound/sleigh_bells.mp3 +0 -0
  189. game/static/game/sound/sleigh_bells.ogg +0 -0
  190. game/static/game/sound/sleigh_crash.mp3 +0 -0
  191. game/static/game/sound/sleigh_crash.ogg +0 -0
  192. game/templates/game/base.html +35 -15
  193. game/templates/game/basenonav.html +23 -17
  194. game/templates/game/game.html +236 -111
  195. game/templates/game/level_editor.html +353 -275
  196. game/templates/game/level_moderation.html +19 -6
  197. game/templates/game/level_selection.html +75 -62
  198. game/templates/game/python_den_level_selection.html +291 -0
  199. game/templates/game/python_den_worksheet.html +101 -0
  200. game/templates/game/scoreboard.html +88 -65
  201. game/tests/test_level_editor.py +210 -35
  202. game/tests/test_level_moderation.py +6 -20
  203. game/tests/test_level_selection.py +332 -11
  204. game/tests/test_python_den_worksheet.py +85 -0
  205. game/tests/test_scoreboard.py +258 -66
  206. game/tests/utils/level.py +43 -3
  207. game/tests/utils/teacher.py +2 -2
  208. game/theme.py +21 -21
  209. game/urls.py +125 -78
  210. game/views/language_code_conversions.py +90 -0
  211. game/views/level.py +201 -63
  212. game/views/level_editor.py +109 -48
  213. game/views/level_moderation.py +29 -6
  214. game/views/level_selection.py +179 -56
  215. game/views/level_solutions.py +600 -106
  216. game/views/scoreboard.py +181 -66
  217. game/views/worksheet.py +25 -0
  218. rapid_router-7.6.8.dist-info/METADATA +174 -0
  219. {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/RECORD +222 -242
  220. {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/WHEEL +1 -1
  221. rapid_router-7.6.8.dist-info/licenses/LICENSE.md +3 -0
  222. example_project/example_project/__init__.py +0 -1
  223. example_project/example_project/settings.py +0 -54
  224. example_project/example_project/urls.py +0 -16
  225. example_project/manage.py +0 -10
  226. game/autoconfig.py +0 -59
  227. game/csp_config.py +0 -23
  228. game/locale/ar_SA/LC_MESSAGES/django.mo +0 -0
  229. game/locale/ar_SA/LC_MESSAGES/django.po +0 -405
  230. game/locale/ar_SA/LC_MESSAGES/djangojs.mo +0 -0
  231. game/locale/ar_SA/LC_MESSAGES/djangojs.po +0 -743
  232. game/locale/bg_BG/LC_MESSAGES/django.mo +0 -0
  233. game/locale/bg_BG/LC_MESSAGES/django.po +0 -405
  234. game/locale/bg_BG/LC_MESSAGES/djangojs.mo +0 -0
  235. game/locale/bg_BG/LC_MESSAGES/djangojs.po +0 -739
  236. game/locale/ca_ES/LC_MESSAGES/django.mo +0 -0
  237. game/locale/ca_ES/LC_MESSAGES/django.po +0 -405
  238. game/locale/ca_ES/LC_MESSAGES/djangojs.mo +0 -0
  239. game/locale/ca_ES/LC_MESSAGES/djangojs.po +0 -740
  240. game/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  241. game/locale/cs_CZ/LC_MESSAGES/django.po +0 -405
  242. game/locale/cs_CZ/LC_MESSAGES/djangojs.mo +0 -0
  243. game/locale/cs_CZ/LC_MESSAGES/djangojs.po +0 -741
  244. game/locale/cy_GB/LC_MESSAGES/django.mo +0 -0
  245. game/locale/cy_GB/LC_MESSAGES/django.po +0 -405
  246. game/locale/cy_GB/LC_MESSAGES/djangojs.mo +0 -0
  247. game/locale/cy_GB/LC_MESSAGES/djangojs.po +0 -743
  248. game/locale/de_DE/LC_MESSAGES/django.mo +0 -0
  249. game/locale/de_DE/LC_MESSAGES/django.po +0 -405
  250. game/locale/de_DE/LC_MESSAGES/djangojs.mo +0 -0
  251. game/locale/de_DE/LC_MESSAGES/djangojs.po +0 -739
  252. game/locale/el_GR/LC_MESSAGES/django.mo +0 -0
  253. game/locale/el_GR/LC_MESSAGES/django.po +0 -405
  254. game/locale/el_GR/LC_MESSAGES/djangojs.mo +0 -0
  255. game/locale/el_GR/LC_MESSAGES/djangojs.po +0 -739
  256. game/locale/en_GB/LC_MESSAGES/django.mo +0 -0
  257. game/locale/en_GB/LC_MESSAGES/django.po +0 -405
  258. game/locale/en_GB/LC_MESSAGES/djangojs.mo +0 -0
  259. game/locale/en_GB/LC_MESSAGES/djangojs.po +0 -739
  260. game/locale/es_ES/LC_MESSAGES/django.mo +0 -0
  261. game/locale/es_ES/LC_MESSAGES/django.po +0 -405
  262. game/locale/es_ES/LC_MESSAGES/djangojs.mo +0 -0
  263. game/locale/es_ES/LC_MESSAGES/djangojs.po +0 -739
  264. game/locale/fi_FI/LC_MESSAGES/django.mo +0 -0
  265. game/locale/fi_FI/LC_MESSAGES/django.po +0 -405
  266. game/locale/fi_FI/LC_MESSAGES/djangojs.mo +0 -0
  267. game/locale/fi_FI/LC_MESSAGES/djangojs.po +0 -739
  268. game/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  269. game/locale/fr_FR/LC_MESSAGES/django.po +0 -405
  270. game/locale/fr_FR/LC_MESSAGES/djangojs.mo +0 -0
  271. game/locale/fr_FR/LC_MESSAGES/djangojs.po +0 -739
  272. game/locale/gu_IN/LC_MESSAGES/django.mo +0 -0
  273. game/locale/gu_IN/LC_MESSAGES/django.po +0 -405
  274. game/locale/gu_IN/LC_MESSAGES/djangojs.mo +0 -0
  275. game/locale/gu_IN/LC_MESSAGES/djangojs.po +0 -739
  276. game/locale/hi_IN/LC_MESSAGES/django.mo +0 -0
  277. game/locale/hi_IN/LC_MESSAGES/django.po +0 -405
  278. game/locale/hi_IN/LC_MESSAGES/djangojs.mo +0 -0
  279. game/locale/hi_IN/LC_MESSAGES/djangojs.po +0 -739
  280. game/locale/id_ID/LC_MESSAGES/django.mo +0 -0
  281. game/locale/id_ID/LC_MESSAGES/django.po +0 -405
  282. game/locale/id_ID/LC_MESSAGES/djangojs.mo +0 -0
  283. game/locale/id_ID/LC_MESSAGES/djangojs.po +0 -738
  284. game/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  285. game/locale/it_IT/LC_MESSAGES/django.po +0 -405
  286. game/locale/it_IT/LC_MESSAGES/djangojs.mo +0 -0
  287. game/locale/it_IT/LC_MESSAGES/djangojs.po +0 -739
  288. game/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
  289. game/locale/ja_JP/LC_MESSAGES/django.po +0 -405
  290. game/locale/ja_JP/LC_MESSAGES/djangojs.mo +0 -0
  291. game/locale/ja_JP/LC_MESSAGES/djangojs.po +0 -738
  292. game/locale/lol_US/LC_MESSAGES/django.mo +0 -0
  293. game/locale/lol_US/LC_MESSAGES/django.po +0 -405
  294. game/locale/lol_US/LC_MESSAGES/djangojs.mo +0 -0
  295. game/locale/lol_US/LC_MESSAGES/djangojs.po +0 -739
  296. game/locale/nb_NO/LC_MESSAGES/django.mo +0 -0
  297. game/locale/nb_NO/LC_MESSAGES/django.po +0 -405
  298. game/locale/nb_NO/LC_MESSAGES/djangojs.mo +0 -0
  299. game/locale/nb_NO/LC_MESSAGES/djangojs.po +0 -739
  300. game/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  301. game/locale/nl_NL/LC_MESSAGES/django.po +0 -405
  302. game/locale/nl_NL/LC_MESSAGES/djangojs.mo +0 -0
  303. game/locale/nl_NL/LC_MESSAGES/djangojs.po +0 -739
  304. game/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  305. game/locale/pl_PL/LC_MESSAGES/django.po +0 -405
  306. game/locale/pl_PL/LC_MESSAGES/djangojs.mo +0 -0
  307. game/locale/pl_PL/LC_MESSAGES/djangojs.po +0 -741
  308. game/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  309. game/locale/pt_BR/LC_MESSAGES/django.po +0 -405
  310. game/locale/pt_BR/LC_MESSAGES/djangojs.mo +0 -0
  311. game/locale/pt_BR/LC_MESSAGES/djangojs.po +0 -739
  312. game/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  313. game/locale/pt_PT/LC_MESSAGES/django.po +0 -405
  314. game/locale/pt_PT/LC_MESSAGES/djangojs.mo +0 -0
  315. game/locale/pt_PT/LC_MESSAGES/djangojs.po +0 -739
  316. game/locale/ro_RO/LC_MESSAGES/django.mo +0 -0
  317. game/locale/ro_RO/LC_MESSAGES/django.po +0 -405
  318. game/locale/ro_RO/LC_MESSAGES/djangojs.mo +0 -0
  319. game/locale/ro_RO/LC_MESSAGES/djangojs.po +0 -740
  320. game/locale/ru_RU/LC_MESSAGES/django.mo +0 -0
  321. game/locale/ru_RU/LC_MESSAGES/django.po +0 -405
  322. game/locale/ru_RU/LC_MESSAGES/djangojs.mo +0 -0
  323. game/locale/ru_RU/LC_MESSAGES/djangojs.po +0 -741
  324. game/locale/sv_SE/LC_MESSAGES/django.mo +0 -0
  325. game/locale/sv_SE/LC_MESSAGES/django.po +0 -405
  326. game/locale/sv_SE/LC_MESSAGES/djangojs.mo +0 -0
  327. game/locale/sv_SE/LC_MESSAGES/djangojs.po +0 -739
  328. game/locale/tl_PH/LC_MESSAGES/django.mo +0 -0
  329. game/locale/tl_PH/LC_MESSAGES/django.po +0 -405
  330. game/locale/tl_PH/LC_MESSAGES/djangojs.mo +0 -0
  331. game/locale/tl_PH/LC_MESSAGES/djangojs.po +0 -739
  332. game/locale/tlh_AA/LC_MESSAGES/django.mo +0 -0
  333. game/locale/tlh_AA/LC_MESSAGES/django.po +0 -405
  334. game/locale/tlh_AA/LC_MESSAGES/djangojs.mo +0 -0
  335. game/locale/tlh_AA/LC_MESSAGES/djangojs.po +0 -739
  336. game/locale/tr_TR/LC_MESSAGES/django.mo +0 -0
  337. game/locale/tr_TR/LC_MESSAGES/django.po +0 -405
  338. game/locale/tr_TR/LC_MESSAGES/djangojs.mo +0 -0
  339. game/locale/tr_TR/LC_MESSAGES/djangojs.po +0 -740
  340. game/locale/ur_IN/LC_MESSAGES/django.mo +0 -0
  341. game/locale/ur_IN/LC_MESSAGES/django.po +0 -405
  342. game/locale/ur_IN/LC_MESSAGES/djangojs.mo +0 -0
  343. game/locale/ur_IN/LC_MESSAGES/djangojs.po +0 -739
  344. game/locale/ur_PK/LC_MESSAGES/django.mo +0 -0
  345. game/locale/ur_PK/LC_MESSAGES/django.po +0 -405
  346. game/locale/ur_PK/LC_MESSAGES/djangojs.mo +0 -0
  347. game/locale/ur_PK/LC_MESSAGES/djangojs.po +0 -739
  348. game/static/game/image/actions/go.svg +0 -18
  349. game/static/game/image/icons/destination.svg +0 -9
  350. game/static/game/js/pqselect.min.js +0 -9
  351. game/static/game/js/widget-scroller.js +0 -906
  352. rapid_router-5.4.1.dist-info/LICENSE.md +0 -577
  353. rapid_router-5.4.1.dist-info/METADATA +0 -24
  354. {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/top_level.txt +0 -0
game/models.py CHANGED
@@ -1,8 +1,10 @@
1
+ import typing as t
1
2
  from builtins import str
2
3
 
3
- from common.models import UserProfile, Student
4
+ from common.models import Class, Student, UserProfile
4
5
  from django.contrib.auth.models import User
5
6
  from django.db import models
7
+ from django.db.models.query import QuerySet
6
8
 
7
9
 
8
10
  def theme_choices():
@@ -19,17 +21,33 @@ def character_choices():
19
21
 
20
22
  class Block(models.Model):
21
23
  type = models.CharField(max_length=200)
24
+ block_type = models.IntegerField(
25
+ choices=[
26
+ (0, "Start"),
27
+ (1, "Action"),
28
+ (2, "Condition"),
29
+ (3, "Procedure"),
30
+ (4, "ControlFlow"),
31
+ (5, "Variable"),
32
+ (6, "Math"),
33
+ ]
34
+ )
22
35
 
23
36
  def __str__(self):
24
37
  return self.type
25
38
 
39
+ class Meta:
40
+ ordering = ["block_type", "pk"]
41
+
26
42
 
27
43
  class Episode(models.Model):
28
44
  """Variables prefixed with r_ signify they are parameters for random level generation"""
29
45
 
46
+ worksheets: QuerySet["Worksheet"]
47
+
30
48
  name = models.CharField(max_length=200)
31
49
  next_episode = models.ForeignKey(
32
- "self", null=True, default=None, on_delete=models.SET_NULL
50
+ "self", null=True, blank=True, default=None, on_delete=models.SET_NULL
33
51
  )
34
52
  in_development = models.BooleanField(default=False)
35
53
 
@@ -39,9 +57,9 @@ class Episode(models.Model):
39
57
  r_curviness = models.FloatField(default=0, null=True)
40
58
  r_num_tiles = models.IntegerField(default=5, null=True)
41
59
  r_blocks = models.ManyToManyField(Block, related_name="episodes")
42
- r_blocklyEnabled = models.BooleanField(default=True)
43
- r_pythonEnabled = models.BooleanField(default=False)
44
- r_trafficLights = models.BooleanField(default=False)
60
+ r_blockly_enabled = models.BooleanField(default=True)
61
+ r_python_enabled = models.BooleanField(default=False)
62
+ r_traffic_lights = models.BooleanField(default=False)
45
63
  r_cows = models.BooleanField(default=False)
46
64
 
47
65
  @property
@@ -70,8 +88,23 @@ class Episode(models.Model):
70
88
  7: "medium-hard",
71
89
  8: "medium-hard",
72
90
  9: "brainteasers",
73
- 10: "hard",
74
- 11: "advanced",
91
+ 10: "early-python",
92
+ 11: "early-python",
93
+ 12: "late-python",
94
+ 13: "late-python",
95
+ 14: "late-python",
96
+ 15: "late-python",
97
+ 16: "early-python",
98
+ 17: "early-python",
99
+ 18: "early-python",
100
+ 19: "early-python",
101
+ 20: "late-python",
102
+ 21: "late-python",
103
+ 22: "late-python",
104
+ 23: "late-python",
105
+ 24: "late-python",
106
+ 25: "late-python",
107
+ 26: "late-python",
75
108
  }
76
109
 
77
110
  return difficulty_map.get(self.id, "easy")
@@ -85,7 +118,11 @@ class LevelManager(models.Manager):
85
118
  # Sorts all the levels by integer conversion of "name" which should equate to the correct play order
86
119
  # Custom levels do not have an episode
87
120
 
88
- return sort_levels(self.model.objects.filter(episode__isnull=False))
121
+ return sort_levels(
122
+ self.model.objects.filter(episode__isnull=False).exclude(
123
+ episode__name__icontains="coming soon"
124
+ )
125
+ )
89
126
 
90
127
 
91
128
  def sort_levels(levels):
@@ -93,6 +130,8 @@ def sort_levels(levels):
93
130
 
94
131
 
95
132
  class Level(models.Model):
133
+ after_worksheet: t.Optional["Worksheet"]
134
+
96
135
  name = models.CharField(max_length=100)
97
136
  episode = models.ForeignKey(
98
137
  Episode, blank=True, null=True, default=None, on_delete=models.PROTECT
@@ -112,26 +151,94 @@ class Level(models.Model):
112
151
  )
113
152
  fuel_gauge = models.BooleanField(default=True)
114
153
  max_fuel = models.IntegerField(default=50)
115
- direct_drive = models.BooleanField(default=False)
116
154
  next_level = models.ForeignKey(
117
- "self", null=True, default=None, on_delete=models.SET_NULL
155
+ "self",
156
+ null=True,
157
+ blank=True,
158
+ default=None,
159
+ on_delete=models.SET_NULL,
160
+ related_name="prev_level",
118
161
  )
119
162
  shared_with = models.ManyToManyField(User, related_name="shared", blank=True)
120
163
  model_solution = models.CharField(blank=True, max_length=20, default="[]")
121
164
  disable_route_score = models.BooleanField(default=False)
165
+ disable_algorithm_score = models.BooleanField(default=False)
122
166
  threads = models.IntegerField(blank=False, default=1)
123
- blocklyEnabled = models.BooleanField(default=True)
124
- pythonEnabled = models.BooleanField(default=True)
125
- pythonViewEnabled = models.BooleanField(default=False)
167
+ blockly_enabled = models.BooleanField(default=True)
168
+ python_enabled = models.BooleanField(default=True)
169
+ python_view_enabled = models.BooleanField(default=False)
126
170
  theme_name = models.CharField(
127
- max_length=10, choices=theme_choices(), blank=True, null=True, default=None
171
+ max_length=10,
172
+ choices=theme_choices(),
173
+ blank=True,
174
+ null=True,
175
+ default=None,
128
176
  )
129
177
  character_name = models.CharField(
130
- max_length=20, choices=character_choices(), blank=True, null=True, default=None
178
+ max_length=20,
179
+ choices=character_choices(),
180
+ blank=True,
181
+ null=True,
182
+ default=None,
183
+ )
184
+ subtitle = models.TextField(max_length=100, blank=True, null=True)
185
+ lesson = models.TextField(
186
+ max_length=10000, default="Can you find the shortest route?"
187
+ )
188
+ hint = models.TextField(
189
+ max_length=10000,
190
+ default="Think back to earlier levels. What did you learn?",
191
+ )
192
+ commands = models.TextField(
193
+ max_length=10000,
194
+ default='<div class="row">'
195
+ + '<div class="large-4 columns">'
196
+ + "<p><b>Movement</b>"
197
+ + "<br>my_van.move_forwards()"
198
+ + "<br>my_van.turn_around()"
199
+ + "<br>my_van.turn_left()"
200
+ + "<br>my_van.turn_right()"
201
+ + "<br>my_van.wait()<p></div>"
202
+ + '<div class="large-4 columns">'
203
+ + "<p><b>Position</b>"
204
+ + "<br>my_van.at_dead_end()"
205
+ + "<br>my_van.at_destination()"
206
+ + "<br>my_van.at_red_traffic_light()"
207
+ + "<br>my_van.at_green_traffic_light()"
208
+ + "<br>my_van.at_traffic_light(c)"
209
+ + "<br><i>where c is 'RED' or 'GREEN'</i></p></div>"
210
+ + '<div class="large-4 columns">'
211
+ + "<p><br>my_van.is_road_right()"
212
+ + "<br>my_van.is_road_left()"
213
+ + "<br>my_van.is_road_forward()"
214
+ + "<br>my_van.is_road(d)"
215
+ + "<br><i>where d is 'FORWARD', 'LEFT', or 'RIGHT'</i></p></div>"
216
+ + "</div>"
217
+ + '<div class="row">'
218
+ + '<div class="large-4 columns">'
219
+ + "<p><b>Animals</b>"
220
+ + "<br>my_van.is_animal_crossing()"
221
+ + "<br>my_van.sound_horn()</div>"
222
+ + "</div>",
131
223
  )
132
224
  anonymous = models.BooleanField(default=False)
225
+ locked_for_class = models.ManyToManyField(
226
+ Class, blank=True, related_name="locked_levels"
227
+ )
228
+ needs_approval = models.BooleanField(default=True)
133
229
  objects = LevelManager()
134
230
 
231
+ class Meta:
232
+ constraints = [
233
+ models.CheckConstraint(
234
+ check=~models.Q(
235
+ default=True,
236
+ needs_approval=True,
237
+ ),
238
+ name="level__default_does_not_need_approval",
239
+ ),
240
+ ]
241
+
135
242
  def __str__(self):
136
243
  return f"Level {self.name}"
137
244
 
@@ -180,7 +287,7 @@ class LevelDecor(models.Model):
180
287
  x = models.IntegerField()
181
288
  y = models.IntegerField()
182
289
  level = models.ForeignKey(Level, on_delete=models.CASCADE)
183
- decorName = models.CharField(max_length=100, default="tree1")
290
+ decor_name = models.CharField(max_length=100, default="tree1")
184
291
 
185
292
 
186
293
  class Workspace(models.Model):
@@ -196,6 +303,7 @@ class Workspace(models.Model):
196
303
  python_contents = models.TextField(default="")
197
304
  blockly_enabled = models.BooleanField(default=False)
198
305
  python_enabled = models.BooleanField(default=False)
306
+ python_view_enabled = models.BooleanField(default=False)
199
307
 
200
308
  def __str__(self):
201
309
  return str(self.name)
@@ -220,3 +328,36 @@ class Attempt(models.Model):
220
328
 
221
329
  def elapsed_time(self):
222
330
  return self.finish_time - self.start_time
331
+
332
+
333
+ class Worksheet(models.Model):
334
+ episode = models.ForeignKey(
335
+ Episode,
336
+ blank=True,
337
+ null=True,
338
+ default=None,
339
+ on_delete=models.PROTECT,
340
+ related_name="worksheets",
341
+ )
342
+ before_level = models.OneToOneField(
343
+ Level,
344
+ blank=True,
345
+ null=True,
346
+ default=None,
347
+ on_delete=models.PROTECT,
348
+ related_name="after_worksheet",
349
+ )
350
+ lesson_plan_link = models.CharField(
351
+ max_length=500, null=True, blank=True, default=None
352
+ )
353
+ slides_link = models.CharField(max_length=500, null=True, blank=True, default=None)
354
+ student_worksheet_link = models.CharField(
355
+ max_length=500, null=True, blank=True, default=None
356
+ )
357
+ indy_worksheet_link = models.CharField(
358
+ max_length=500, null=True, blank=True, default=None
359
+ )
360
+ video_link = models.CharField(max_length=500, null=True, blank=True, default=None)
361
+ locked_classes = models.ManyToManyField(
362
+ Class, blank=True, related_name="locked_worksheets"
363
+ )
game/permissions.py CHANGED
@@ -1,9 +1,5 @@
1
- import logging
2
-
3
1
  from rest_framework import permissions
4
2
 
5
- LOGGER = logging.getLogger(__name__)
6
-
7
3
 
8
4
  def _get_userprofile_school(userprofile):
9
5
  if hasattr(userprofile, "teacher"):
@@ -11,9 +7,6 @@ def _get_userprofile_school(userprofile):
11
7
  elif hasattr(userprofile, "student"):
12
8
  return userprofile.student.class_field.teacher.school
13
9
  else:
14
- LOGGER.error(
15
- f"Userprofile ID {userprofile.id} has no teacher or student attribute"
16
- )
17
10
  return None
18
11
 
19
12
 
@@ -37,6 +30,7 @@ def can_save_workspace(user, workspace):
37
30
  def can_delete_workspace(user, workspace):
38
31
  return not user.is_anonymous and workspace.owner == user.userprofile
39
32
 
33
+
40
34
  #####################
41
35
  # Level permissions #
42
36
  #####################
@@ -51,15 +45,32 @@ def can_play_or_delete_level(user, level):
51
45
  # from their own classes
52
46
  if user.userprofile.teacher.is_admin and hasattr(level.owner, "student"):
53
47
  return (
54
- user.userprofile.teacher.school
55
- == level.owner.student.class_field.teacher.school
48
+ user.userprofile.teacher.school
49
+ == level.owner.student.class_field.teacher.school
56
50
  )
57
51
  else:
58
52
  return user.userprofile.teacher.teaches(level.owner)
59
53
 
60
54
 
55
+ def can_approve_level(user, level):
56
+ return (
57
+ hasattr(user.userprofile, "teacher")
58
+ and level.shared_with.filter(id=user.id).exists()
59
+ )
60
+
61
+
61
62
  def can_play_level(user, level, early_access):
62
- if level.default and not level.episode.in_development:
63
+ if (
64
+ not user.is_anonymous
65
+ and hasattr(user.userprofile, "student")
66
+ and user.userprofile.student.class_field
67
+ ):
68
+ # If the user is a student, check that the level isn't locked for their class
69
+ return user.userprofile.id == level.owner_id or (
70
+ user.userprofile.student.class_field not in level.locked_for_class.all()
71
+ and not level.needs_approval
72
+ )
73
+ elif level.default and not level.episode.in_development:
63
74
  return True
64
75
  elif level.anonymous:
65
76
  return False
@@ -115,8 +126,8 @@ def can_delete_level(user, level):
115
126
  class CanShareLevel(permissions.BasePermission):
116
127
  """
117
128
  Used to verify that an incoming request is made by a user who is authorised to share
118
- the level - that is, that they are the owner of the level as a student, or if they're a teacher that the level was
119
- shared with them.
129
+ the level - that is, that they are the owner of the level as a student and the level has been approved by a teacher,
130
+ or if they're a teacher that the level was shared with them.
120
131
  """
121
132
 
122
133
  def has_permission(self, request, view):
@@ -137,20 +148,19 @@ class CanShareLevel(permissions.BasePermission):
137
148
  ):
138
149
  return True
139
150
  else:
140
- return obj.owner == request.user.userprofile
151
+ return obj.owner == request.user.userprofile and not obj.needs_approval
141
152
 
142
153
 
143
154
  class CanShareLevelWith(permissions.BasePermission):
144
155
  """
145
- Used to verify that an the user who is requesting to share their level is
146
- authorised to share the level with a specific recipient.
156
+ Used to verify that the user who is requesting to share their level is authorised to share the level with a specific
157
+ recipient.
147
158
  The user is authorised if:
148
159
  - neither they nor the recipient are anonymous,
149
160
  - neither they nor the recipient are independent students,
150
- - they are a student and the recipient is a student in the same class, or their
151
- teacher
152
- - they are a teacher and the recipient is a teacher in the same school, or their
153
- student
161
+ - they are a student and the recipient is a student in the same class, or their teacher
162
+ - they are a teacher and the recipient is a teacher in the same school, or their student
163
+ - they are an admin teacher and the recipient is a student in the same school
154
164
  """
155
165
 
156
166
  def has_permission(self, request, view):
@@ -193,6 +203,11 @@ class CanShareLevelWith(permissions.BasePermission):
193
203
  ):
194
204
  # Are they in the same organisation?
195
205
  return recipient_profile.teacher.school == sharer_profile.teacher.school
206
+ elif hasattr(sharer_profile, "teacher") and sharer_profile.teacher.is_admin:
207
+ return (
208
+ recipient_profile.student.class_field.teacher.school
209
+ == sharer_profile.teacher.school
210
+ )
196
211
  else:
197
212
  return False
198
213
 
@@ -0,0 +1,26 @@
1
+ from django.urls import re_path
2
+
3
+ from game.views.level import play_default_python_level, start_python_episode
4
+ from game.views.level_selection import python_levels
5
+ from game.views.scoreboard import python_scoreboard
6
+ from game.views.worksheet import worksheet
7
+
8
+ urlpatterns = [
9
+ re_path(r"^$", python_levels, name="python_levels"),
10
+ re_path(
11
+ r"^(?P<level_name>[A-Z0-9]+)/$",
12
+ play_default_python_level,
13
+ name="play_python_default_level",
14
+ ),
15
+ re_path(
16
+ r"^episode/(?P<episodeId>[0-9]+)/$",
17
+ start_python_episode,
18
+ name="start_python_episode",
19
+ ),
20
+ re_path(
21
+ r"^worksheet/(?P<worksheetId>[0-9]+)/$",
22
+ worksheet,
23
+ name="worksheet",
24
+ ),
25
+ re_path(r"^scoreboard/$", python_scoreboard, name="python_scoreboard"),
26
+ ]