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
@@ -1,28 +1,349 @@
1
+ import json
2
+ from datetime import datetime
3
+ from unittest.mock import patch
4
+
5
+ from common.models import Teacher
6
+ from common.tests.utils.classes import create_class_directly
7
+ from common.tests.utils.student import create_school_student_directly
8
+ from common.tests.utils.teacher import signup_teacher_directly
9
+ from deploy import captcha
1
10
  from django.test.client import Client
2
11
  from django.test.testcases import TestCase
3
12
  from django.urls import reverse
4
- from hamcrest import *
13
+
14
+ from game.models import Level
15
+ from game.permissions import can_play_level
16
+ from game.tests.utils.level import create_save_level
17
+ from game.tests.utils.teacher import add_teacher_to_school, create_school
18
+ from game.views.level import _next_level_url, _prev_level_url
5
19
 
6
20
 
7
21
  class LevelSelectionTestCase(TestCase):
22
+ @classmethod
23
+ def setUpClass(cls):
24
+ cls.orig_captcha_enabled = captcha.CAPTCHA_ENABLED
25
+ captcha.CAPTCHA_ENABLED = False
26
+ super(LevelSelectionTestCase, cls).setUpClass()
27
+
28
+ @classmethod
29
+ def tearDownClass(cls):
30
+ captcha.CAPTCHA_ENABLED = cls.orig_captcha_enabled
31
+ super(LevelSelectionTestCase, cls).tearDownClass()
32
+
8
33
  def setUp(self):
9
34
  self.client = Client()
10
35
 
36
+ def login(self, email, password):
37
+ self.client.post(
38
+ reverse("teacher_login"),
39
+ {
40
+ "auth-username": email,
41
+ "auth-password": password,
42
+ "teacher_login_view-current_step": "auth",
43
+ },
44
+ follow=True,
45
+ )
46
+
47
+ def logout(self):
48
+ self.client.post(reverse("logout_view"), follow=True)
49
+
50
+ def student_login(self, name, access_code, password):
51
+ self.client.post(
52
+ reverse("student_login", kwargs={"access_code": access_code}),
53
+ {"username": name, "password": password},
54
+ follow=True,
55
+ )
56
+
57
+ def level_data(self, levelID):
58
+ data = {
59
+ "origin": '{"coordinate":[3,5],"direction":"S"}',
60
+ "python_enabled": False,
61
+ "decor": [],
62
+ "blockly_enabled": True,
63
+ "blocks": [
64
+ {"type": "move_forwards"},
65
+ {"type": "turn_left"},
66
+ {"type": "turn_right"},
67
+ ],
68
+ "max_fuel": "50",
69
+ "python_view_enabled": False,
70
+ "character": "3",
71
+ "name": f"level{levelID}",
72
+ "theme": 1,
73
+ "anonymous": False,
74
+ "cows": "[]",
75
+ "path": '[{"coordinate":[3,5],"connectedNodes":[1]},{"coordinate":[3,4],"connectedNodes":[0]}]',
76
+ "traffic_lights": "[]",
77
+ "destinations": "[[3,4]]",
78
+ }
79
+ return json.dumps(data)
80
+
11
81
  def test_list_episodes(self):
12
82
  url = reverse("levels")
13
83
  response = self.client.get(url)
14
84
 
15
- assert_that(response.status_code, equal_to(200))
16
- self._assert_that_response_contains_episode_name(response, "Getting Started")
17
- self._assert_that_response_contains_level_with_title(
18
- response, "Can you help the van get to the house?"
85
+ assert response.status_code == 200
86
+ assert (
87
+ response.context["blocklyEpisodes"][0]["name"] == "Getting Started"
88
+ )
89
+ assert (
90
+ response.context["blocklyEpisodes"][0]["levels"][0]["title"]
91
+ == "Can you help the van get to the house?"
92
+ )
93
+
94
+ def test_list_python_episodes(self):
95
+ url = reverse("python_levels")
96
+ response = self.client.get(url)
97
+
98
+ assert response.status_code == 200
99
+ assert (
100
+ response.context["pythonEpisodes"][0]["name"]
101
+ == "Output, Operators, and Data"
102
+ )
103
+
104
+ def test_custom_levels_access(self):
105
+ email1, password1 = signup_teacher_directly()
106
+ email2, password2 = signup_teacher_directly()
107
+
108
+ teacher1 = Teacher.objects.get(new_user__email=email1)
109
+ teacher2 = Teacher.objects.get(new_user__email=email2)
110
+
111
+ school1 = create_school()
112
+ add_teacher_to_school(teacher1, school1, is_admin=True)
113
+ add_teacher_to_school(teacher2, school1)
114
+
115
+ # Create a class and a student for each teacher
116
+ _, class_name1, access_code1 = create_class_directly(email1)
117
+ _, class_name2, access_code2 = create_class_directly(email2)
118
+ (
119
+ student_name1,
120
+ student_password1,
121
+ student1,
122
+ ) = create_school_student_directly(access_code1)
123
+ (
124
+ student_name2,
125
+ student_password2,
126
+ student2,
127
+ ) = create_school_student_directly(access_code2)
128
+
129
+ save_url = "save_level_for_editor"
130
+
131
+ # Log in as the second teacher
132
+ self.login(email2, password2)
133
+ teacher2_level = create_save_level(teacher2)
134
+ save_level_url = reverse(save_url)
135
+
136
+ response = self.client.post(
137
+ save_level_url, {"data": self.level_data(teacher2_level.id)}
138
+ )
139
+
140
+ assert response.status_code == 200
141
+
142
+ # Log in as the first student
143
+ self.logout()
144
+ self.student_login(student_name1, access_code1, student_password1)
145
+
146
+ student1_level = create_save_level(student1)
147
+
148
+ response = self.client.post(
149
+ save_level_url, {"data": self.level_data(student1_level.id)}
150
+ )
151
+
152
+ assert response.status_code == 200
153
+
154
+ # Log in as the second student
155
+ self.logout()
156
+ self.student_login(student_name2, access_code2, student_password2)
157
+
158
+ student2_level = create_save_level(student2)
159
+
160
+ response = self.client.post(
161
+ save_level_url, {"data": self.level_data(student2_level.id)}
162
+ )
163
+
164
+ assert response.status_code == 200
165
+
166
+ # Log in as first teacher again and check they have access to all the levels created above
167
+ self.logout()
168
+ self.login(email1, password1)
169
+
170
+ url = reverse("levels")
171
+ response = self.client.get(url)
172
+
173
+ assert response.status_code == 200
174
+ assert len(response.context["directly_shared_levels"]) == 1
175
+ assert (
176
+ response.context["directly_shared_levels"][0]["owner"]
177
+ == student1.new_user
178
+ )
179
+ assert response.context["indirectly_shared_levels"][teacher2.new_user]
180
+ assert (
181
+ len(response.context["indirectly_shared_levels"][teacher2.new_user])
182
+ == 2
183
+ )
184
+ assert (
185
+ response.context["indirectly_shared_levels"][teacher2.new_user][0][
186
+ "owner"
187
+ ]
188
+ == teacher2.new_user
189
+ )
190
+ assert (
191
+ response.context["indirectly_shared_levels"][teacher2.new_user][1][
192
+ "owner"
193
+ ]
194
+ == student2.new_user
195
+ )
196
+
197
+ # Log in as second teacher again and check they have access to only their student's level
198
+ self.logout()
199
+ self.login(email2, password2)
200
+
201
+ url = reverse("levels")
202
+ response = self.client.get(url)
203
+
204
+ assert response.status_code == 200
205
+ assert len(response.context["directly_shared_levels"]) == 1
206
+ assert (
207
+ response.context["directly_shared_levels"][0]["owner"]
208
+ == student2.new_user
209
+ )
210
+ assert response.context["indirectly_shared_levels"] == {}
211
+
212
+ def test_cannot_access_locked_level(self):
213
+ email, password = signup_teacher_directly()
214
+
215
+ teacher = Teacher.objects.get(new_user__email=email)
216
+
217
+ school = create_school()
218
+ add_teacher_to_school(teacher, school, is_admin=True)
219
+
220
+ class1, _, access_code1 = create_class_directly(email)
221
+ class2, _, access_code2 = create_class_directly(email)
222
+ _, _, student1 = create_school_student_directly(access_code1)
223
+ _, _, student2 = create_school_student_directly(access_code2)
224
+
225
+ level1 = Level.objects.get(id=1)
226
+
227
+ level1.locked_for_class.add(class1)
228
+
229
+ assert not can_play_level(student1.new_user, level1, False)
230
+ assert can_play_level(student2.new_user, level1, False)
231
+
232
+ def test_next_level_for_locked_levels(self):
233
+ email, password = signup_teacher_directly()
234
+
235
+ teacher = Teacher.objects.get(new_user__email=email)
236
+
237
+ school = create_school()
238
+ add_teacher_to_school(teacher, school, is_admin=True)
239
+
240
+ klass, _, access_code = create_class_directly(email)
241
+ _, _, student = create_school_student_directly(access_code)
242
+
243
+ level1 = Level.objects.get(name="1")
244
+ level2 = Level.objects.get(name="2")
245
+ level3 = Level.objects.get(name="3")
246
+ level4 = Level.objects.get(name="4")
247
+ level76 = Level.objects.get(name="76")
248
+ level77 = Level.objects.get(name="77")
249
+ level78 = Level.objects.get(name="78")
250
+ level79 = Level.objects.get(name="79")
251
+ level1014 = Level.objects.get(name="1014")
252
+ level1015 = Level.objects.get(name="1015")
253
+ level1016 = Level.objects.get(name="1016")
254
+
255
+ level2.locked_for_class.add(klass)
256
+ level3.locked_for_class.add(klass)
257
+ level77.locked_for_class.add(klass)
258
+ level78.locked_for_class.add(klass)
259
+ level1015.locked_for_class.add(klass)
260
+
261
+ next_level_url = _next_level_url(level1, student.new_user, False, False)
262
+
263
+ assert next_level_url == f"/rapidrouter/{level4.name}/"
264
+
265
+ prev_level_url = _prev_level_url(level4, student.new_user, False, False)
266
+ assert prev_level_url == f"/rapidrouter/{level1.name}/"
267
+
268
+ next_level_url = _next_level_url(
269
+ level76, student.new_user, False, False
270
+ )
271
+
272
+ assert next_level_url == f"/rapidrouter/{level79.name}/"
273
+
274
+ prev_level_url = _prev_level_url(
275
+ level79, student.new_user, False, False
276
+ )
277
+ assert prev_level_url == f"/rapidrouter/{level76.name}/"
278
+
279
+ next_level_url = _next_level_url(
280
+ level1014, student.new_user, False, True
281
+ )
282
+
283
+ assert next_level_url == f"/pythonden/16/"
284
+
285
+ prev_level_url = _prev_level_url(
286
+ level1016, student.new_user, False, True
287
+ )
288
+ assert prev_level_url == f"/pythonden/14/"
289
+
290
+ @patch(
291
+ "game.views.level.datetime",
292
+ side_effect=lambda *args, **kw: datetime(*args, **kw),
293
+ )
294
+ def test_xmas_theme(self, mock_datetime):
295
+ november = datetime(2023, 11, 1, 0, 0, 0, 0)
296
+ mock_datetime.now.return_value = november
297
+
298
+ response = self.client.get(f"{reverse('home')}/rapidrouter/1/")
299
+ assert """CHARACTER_NAME = "Van"\n""" in response.content.decode(
300
+ "utf-8"
301
+ )
302
+ assert (
303
+ """CHARACTER_URL = "characters/top_view/Van.svg"\n"""
304
+ in response.content.decode("utf-8")
305
+ )
306
+ assert (
307
+ """WRECKAGE_URL = "van_wreckage.svg"\n"""
308
+ in response.content.decode("utf-8")
309
+ )
310
+ assert (
311
+ """BACKGROUND_URL = "decor/grass/tile1.svg"\n"""
312
+ in response.content.decode("utf-8")
313
+ )
314
+ assert (
315
+ """HOUSE_URL = "decor/grass/house.svg"\n"""
316
+ in response.content.decode("utf-8")
317
+ )
318
+ assert (
319
+ """CFC_URL = "decor/grass/cfc.svg"\n"""
320
+ in response.content.decode("utf-8")
19
321
  )
20
322
 
21
- def _assert_that_response_contains_episode_name(self, response, expected):
22
- assert_that(response.context["blocklyEpisodes"][0]["name"], equal_to(expected))
323
+ december = datetime(2023, 12, 1, 0, 0, 0, 0)
324
+ mock_datetime.now.return_value = december
23
325
 
24
- def _assert_that_response_contains_level_with_title(self, response, expected):
25
- assert_that(
26
- response.context["blocklyEpisodes"][0]["levels"][0]["title"],
27
- equal_to(expected),
326
+ response = self.client.get(f"{reverse('home')}/rapidrouter/1/")
327
+ assert """CHARACTER_NAME = "Van"\n""" in response.content.decode(
328
+ "utf-8"
329
+ )
330
+ assert (
331
+ """CHARACTER_URL = "characters/top_view/Sleigh.svg"\n"""
332
+ in response.content.decode("utf-8")
333
+ )
334
+ assert (
335
+ """WRECKAGE_URL = "sleigh_wreckage.svg"\n"""
336
+ in response.content.decode("utf-8")
337
+ )
338
+ assert (
339
+ """BACKGROUND_URL = "decor/snow/tile1.svg"\n"""
340
+ in response.content.decode("utf-8")
341
+ )
342
+ assert (
343
+ """HOUSE_URL = "decor/snow/house.svg"\n"""
344
+ in response.content.decode("utf-8")
345
+ )
346
+ assert (
347
+ """CFC_URL = "decor/snow/cfc.svg"\n"""
348
+ in response.content.decode("utf-8")
28
349
  )
@@ -0,0 +1,85 @@
1
+ from deploy import captcha
2
+ from django.test.client import Client
3
+ from django.test.testcases import TestCase
4
+ from django.urls import reverse
5
+
6
+ from game import messages
7
+ from game.models import Worksheet
8
+
9
+
10
+ class PythonDenWorksheetTestCase(TestCase):
11
+ @classmethod
12
+ def setUpClass(cls):
13
+ cls.orig_captcha_enabled = captcha.CAPTCHA_ENABLED
14
+ captcha.CAPTCHA_ENABLED = False
15
+ super(PythonDenWorksheetTestCase, cls).setUpClass()
16
+
17
+ @classmethod
18
+ def tearDownClass(cls):
19
+ captcha.CAPTCHA_ENABLED = cls.orig_captcha_enabled
20
+ super(PythonDenWorksheetTestCase, cls).tearDownClass()
21
+
22
+ def setUp(self):
23
+ self.client = Client()
24
+
25
+ def login(self, email, password):
26
+ self.client.post(
27
+ reverse("teacher_login"),
28
+ {
29
+ "auth-username": email,
30
+ "auth-password": password,
31
+ "teacher_login_view-current_step": "auth",
32
+ },
33
+ follow=True,
34
+ )
35
+
36
+ def logout(self):
37
+ self.client.post(reverse("logout_view"), follow=True)
38
+
39
+ def student_login(self, name, access_code, password):
40
+ self.client.post(
41
+ reverse("student_login", kwargs={"access_code": access_code}),
42
+ {"username": name, "password": password},
43
+ follow=True,
44
+ )
45
+
46
+ def independent_student_login(self, email, password):
47
+ self.client.login(username=email, password=password)
48
+
49
+ def test_page_permissions(self):
50
+ response = self.client.get(reverse("worksheet", args="1"))
51
+
52
+ assert response.status_code == 200
53
+ assert (
54
+ response.context["title"]
55
+ == messages.no_permission_python_den_worksheet_title()
56
+ )
57
+ assert (
58
+ response.context["message"]
59
+ == messages.no_permission_python_den_worksheet_page()
60
+ )
61
+
62
+ self.login("alberteinstein@codeforlife.com", "Password1")
63
+
64
+ response = self.client.get(reverse("worksheet", args="1"))
65
+
66
+ assert response.status_code == 200
67
+ assert response.context["worksheet"] == Worksheet.objects.get(pk=1)
68
+
69
+ self.logout()
70
+ self.student_login("Leonardo", "AB123", "Password1")
71
+
72
+ response = self.client.get(reverse("worksheet", args="1"))
73
+
74
+ assert response.status_code == 200
75
+ assert response.context["worksheet"] == Worksheet.objects.get(pk=1)
76
+
77
+ self.logout()
78
+ self.independent_student_login("indianajones@codeforlife.com", "Password1")
79
+
80
+ response = self.client.get(reverse("worksheet", args="1"))
81
+
82
+ assert response.status_code == 200
83
+ assert response.context["worksheet"] == Worksheet.objects.get(pk=1)
84
+
85
+ self.logout()