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
@@ -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.noPermissionLevelModerationTitle(),
38
- messages.noPermissionLevelModerationPage(),
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.noPermissionLevelModerationTitle(),
58
- messages.noDataToShowLevelModeration(),
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.noPermissionLevelModerationTitle(),
78
- messages.noPermissionLevelModerationClass(),
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
+ )
@@ -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
- current = start
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
- current += 1
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
- return [
85
- dict(
86
- episode,
87
- name=messages.get_episode_title(episode["id"]),
88
- levels=[
89
- dict(level, title=get_level_title(level["name"]))
90
- for level in episode["levels"]
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
- for episode in data
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 levels(request):
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
- shared_level_data = []
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(request.user)
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
- shared_level_data.append(
170
- {
171
- "id": level.id,
172
- "title": level.name,
173
- "owner": level.owner.user,
174
- "score": attempts.get(level.id),
175
- "maxScore": 10,
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/level_selection.html",
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