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
@@ -5,45 +5,92 @@ from datetime import datetime, timedelta
5
5
 
6
6
  from common.models import Class, Teacher, Student
7
7
  from common.tests.utils.classes import create_class_directly
8
- from common.tests.utils.organisation import create_organisation_directly
9
- from common.tests.utils.student import create_school_student_directly, create_independent_student_directly
8
+ from common.tests.utils.organisation import create_organisation_directly, join_teacher_to_organisation
9
+ from common.tests.utils.student import (
10
+ create_school_student_directly,
11
+ create_independent_student_directly,
12
+ )
10
13
  from common.tests.utils.teacher import signup_teacher_directly
11
14
  from django.test import Client, TestCase
12
15
  from django.urls import reverse
13
16
 
14
17
  from game.models import Attempt, Level, Episode
15
18
  from game.tests.utils.level import create_save_level
16
- from game.views.scoreboard import StudentRow, scoreboard_data, Headers, StudentInTrouble, shared_level_to_name
17
- from game.views.scoreboard_csv import scoreboard_csv, Headers as CSVHeaders, SharedHeaders as CSVSharedHeaders
19
+ from game.views.scoreboard import (
20
+ StudentRow,
21
+ scoreboard_data,
22
+ Headers,
23
+ StudentInTrouble,
24
+ shared_level_to_name,
25
+ shared_levels_data,
26
+ SharedHeaders,
27
+ )
28
+ from game.views.scoreboard_csv import (
29
+ scoreboard_csv,
30
+ Headers as CSVHeaders,
31
+ SharedHeaders as CSVSharedHeaders,
32
+ )
18
33
 
19
34
 
20
35
  class ScoreboardTestCase(TestCase):
21
36
  def test_teacher_multiple_students_multiple_levels(self):
22
- episode_ids = [1]
37
+ # Setup official levels data
38
+ episode_ids = [1, 2]
23
39
  episode1 = Episode.objects.get(id=1)
24
- level_ids = [f"{x}" for x in range(1, len(episode1.levels) + 1)]
40
+ episode2 = Episode.objects.get(id=2)
41
+ level_ids = [
42
+ f"{x}"
43
+ for x in range(1, len(episode1.levels) + len(episode2.levels) + 1)
44
+ ]
25
45
  level1 = Level.objects.get(name="1")
26
- level2 = Level.objects.get(name="2")
46
+ level13 = Level.objects.get(name="13")
27
47
 
28
48
  clas, student, student2 = set_up_data()
29
49
 
30
- create_attempt(student, level1, 10)
50
+ create_attempt(student, level1, 20)
31
51
  create_attempt(student2, level1, 2)
32
- create_attempt(student2, level2, 16)
52
+ create_attempt(student2, level13, 16)
33
53
 
34
- all_levels = [level1, level2]
54
+ # Setup custom levels data
55
+ shared_level = create_save_level(
56
+ student, "custom_level1", shared_with=[student2.new_user]
57
+ )
58
+
59
+ create_attempt(student2, shared_level, 10)
60
+
61
+ all_levels = [level1, level13]
62
+ all_shared_levels = [shared_level]
35
63
 
36
64
  attempts_per_student = {
37
- student: Attempt.objects.filter(level__in=all_levels, student=student, is_best_attempt=True).select_related(
38
- "level"
39
- ),
65
+ student: Attempt.objects.filter(
66
+ level__in=all_levels, student=student, is_best_attempt=True
67
+ ).select_related("level"),
40
68
  student2: Attempt.objects.filter(
41
69
  level__in=all_levels, student=student2, is_best_attempt=True
42
70
  ).select_related("level"),
43
71
  }
44
72
 
45
- student_data, headers, level_headers, levels_sorted = scoreboard_data(episode_ids, attempts_per_student)
73
+ shared_attempts_per_student = {
74
+ student2: Attempt.objects.filter(
75
+ level__in=all_shared_levels,
76
+ student=student2,
77
+ is_best_attempt=True,
78
+ ).select_related("level"),
79
+ }
80
+
81
+ # Generate results
82
+ student_data, headers, level_headers, levels_sorted = scoreboard_data(
83
+ episode_ids, attempts_per_student, "blockly"
84
+ )
85
+ (
86
+ shared_headers,
87
+ shared_level_headers,
88
+ shared_student_data,
89
+ ) = shared_levels_data(
90
+ student.new_user, all_shared_levels, shared_attempts_per_student
91
+ )
46
92
 
93
+ # Check data for official levels matches
47
94
  assert headers == Headers
48
95
  assert level_headers == [f"L{id}" for id in level_ids]
49
96
 
@@ -52,21 +99,40 @@ class ScoreboardTestCase(TestCase):
52
99
  student_row = student_data[0]
53
100
  assert student_row.class_field.name == clas.name
54
101
  assert student_row.name == student.user.user.first_name
55
- assert student_row.total_score == 10
102
+ assert student_row.total_score == 20
56
103
  assert student_row.total_time == timedelta(0)
57
- assert student_row.level_scores[1]["score"] == 10
104
+ assert student_row.level_scores[all_levels[0].id]["score"] == 20
58
105
  assert student_row.completed == 1
59
- assert student_row.percentage_complete == 50.0
106
+ assert student_row.success_rate == 100.0
60
107
 
61
108
  student_row = student_data[1]
62
109
  assert student_row.class_field.name == clas.name
63
110
  assert student_row.name == student2.user.user.first_name
64
111
  assert student_row.total_score == 18
65
112
  assert student_row.total_time == timedelta(0)
66
- assert student_row.level_scores[1]["score"] == 2
67
- assert student_row.level_scores[2]["score"] == 16
113
+ assert student_row.level_scores[all_levels[0].id]["score"] == 2
114
+ assert student_row.level_scores[all_levels[1].id]["score"] == 16
68
115
  assert student_row.completed == 1
69
- assert student_row.percentage_complete == 45.0
116
+ assert (
117
+ student_row.success_rate == 45.0
118
+ ) ## the scores, (2 + 16 = 18), divided by the total possible, (2 * 20 = 40), 18/40 = 45%
119
+
120
+ # Check data for custom levels matches
121
+ assert shared_headers == SharedHeaders
122
+ assert shared_level_headers == [
123
+ f"{shared_level.name} ({shared_level.owner})"
124
+ ]
125
+
126
+ assert len(shared_student_data) == 1
127
+
128
+ student_row = shared_student_data[0]
129
+ assert student_row.class_field.name == clas.name
130
+ assert student_row.name == student2.user.user.first_name
131
+ assert student_row.total_score == 10
132
+ assert student_row.total_time == timedelta(0)
133
+ assert student_row.level_scores[shared_level.id]["score"] == 10
134
+ assert student_row.completed == 1
135
+ assert student_row.success_rate == 100.0
70
136
 
71
137
  def test_scoreboard_loads(self):
72
138
  email, password = signup_teacher_directly()
@@ -86,14 +152,47 @@ class ScoreboardTestCase(TestCase):
86
152
  data = {"classes": [klass.id], "view": [""]}
87
153
 
88
154
  response = c.post(url, data)
155
+
156
+ active_levels = Level.objects.filter(episode__pk__in=range(1, 10))
157
+
89
158
  assert response.status_code == 200
90
- assert len(response.context["level_headers"]) == 109
159
+ assert len(response.context["level_headers"]) == active_levels.count()
160
+
161
+ def test_python_scoreboard_loads(self):
162
+ email, password = signup_teacher_directly()
163
+ create_organisation_directly(email)
164
+ klass, name, access_code = create_class_directly(email)
165
+ create_school_student_directly(access_code)
166
+
167
+ url = reverse("python_scoreboard")
168
+ c = Client()
169
+ c.login(username=email, password=password)
170
+
171
+ # test scoreboard page loads properly
172
+ response = c.get(url)
173
+ assert response.status_code == 200
174
+
175
+ # test scoreboard shows all episodes if no episodes are manually selected
176
+ data = {"classes": [klass.id], "view": [""]}
177
+
178
+ response = c.post(url, data)
179
+
180
+ active_levels = Level.objects.filter(episode__pk__in=range(12, 16))
181
+
182
+ assert response.status_code == 200
183
+ assert len(response.context["level_headers"]) == active_levels.count()
91
184
 
92
185
  def test_student_can_see_classes(self):
93
186
  """A student should be able to see the classes they are in"""
94
- mr_teacher = Teacher.objects.factory("Normal", "Teacher", "normal@school.edu", "secretpa$sword")
95
- klass, name1, _ = create_class_directly(mr_teacher.user.user.email, class_name="Class 1")
96
- _, name2, _ = create_class_directly(mr_teacher.user.user.email, class_name="Class 2")
187
+ mr_teacher = Teacher.objects.factory(
188
+ "Normal", "Teacher", "normal@school.edu", "secretpa$sword"
189
+ )
190
+ klass, name1, _ = create_class_directly(
191
+ mr_teacher.user.user.email, class_name="Class 1"
192
+ )
193
+ _, name2, _ = create_class_directly(
194
+ mr_teacher.user.user.email, class_name="Class 2"
195
+ )
97
196
  student = Student.objects.schoolFactory(klass, "some student", "secret")
98
197
 
99
198
  c = Client()
@@ -102,30 +201,49 @@ class ScoreboardTestCase(TestCase):
102
201
  url = reverse("scoreboard")
103
202
  response = c.get(url)
104
203
 
105
- choices_in_form = [v for (k, v) in response.context["form"]["classes"].field.choices]
204
+ choices_in_form = [
205
+ v for (k, v) in response.context["form"]["classes"].field.choices
206
+ ]
106
207
  assert name1 in choices_in_form
107
208
  assert name2 not in choices_in_form
108
209
  assert len(choices_in_form) == 1
109
210
 
110
211
  def test_admin_teacher_can_see_all_classes(self):
111
212
  """An admin should be able to see all classes, not just the ones they teach"""
112
- normal_teacher = Teacher.objects.factory("Normal", "Teacher", "normal@school.edu", "secretpa$sword")
113
- admin_teacher = Teacher.objects.factory("Admin", "Admin", "admin@school.edu", "secretpa$sword2")
213
+ teacher_email = "normal@school.edu"
214
+ admin_email = "admin@school.edu"
114
215
 
115
- admin_teacher.is_admin = True
116
- admin_teacher.save()
216
+ normal_teacher = Teacher.objects.factory(
217
+ "Normal", "Teacher", teacher_email, "secretpa$sword"
218
+ )
219
+ admin_teacher = Teacher.objects.factory(
220
+ "Admin", "Admin", admin_email, "secretpa$sword2"
221
+ )
117
222
 
118
- _, name1, _ = create_class_directly(admin_teacher.user.user.email, class_name="Class 1")
119
- _, name2, _ = create_class_directly(admin_teacher.user.user.email, class_name="Class 2")
120
- _, name3, _ = create_class_directly(normal_teacher.user.user.email, class_name="Class 3")
223
+ school = create_organisation_directly(admin_email)
224
+ join_teacher_to_organisation(teacher_email, school.name)
225
+
226
+ _, name1, _ = create_class_directly(
227
+ admin_teacher.user.user.email, class_name="Class 1"
228
+ )
229
+ _, name2, _ = create_class_directly(
230
+ admin_teacher.user.user.email, class_name="Class 2"
231
+ )
232
+ _, name3, _ = create_class_directly(
233
+ normal_teacher.user.user.email, class_name="Class 3"
234
+ )
121
235
 
122
236
  c = Client()
123
- c.login(username=admin_teacher.user.user.email, password="secretpa$sword2")
237
+ c.login(
238
+ username=admin_teacher.user.user.email, password="secretpa$sword2"
239
+ )
124
240
 
125
241
  url = reverse("scoreboard")
126
242
  response = c.get(url)
127
243
 
128
- choices_in_form = [v for (k, v) in response.context["form"]["classes"].field.choices]
244
+ choices_in_form = [
245
+ v for (k, v) in response.context["form"]["classes"].field.choices
246
+ ]
129
247
 
130
248
  assert name1 in choices_in_form
131
249
  assert name2 in choices_in_form
@@ -133,12 +251,22 @@ class ScoreboardTestCase(TestCase):
133
251
 
134
252
  def test_non_admin_teacher_can_only_see_their_own_classes(self):
135
253
  """A teacher who is not an admin should only be able to see their classes, not ones taught by others"""
136
- teacher1 = Teacher.objects.factory("First", "Teacher", "normal@school.edu", "secretpa$sword")
137
- teacher2 = Teacher.objects.factory("Second", "Teacher", "admin@school.edu", "secretpa$sword2")
254
+ teacher1 = Teacher.objects.factory(
255
+ "First", "Teacher", "normal@school.edu", "secretpa$sword"
256
+ )
257
+ teacher2 = Teacher.objects.factory(
258
+ "Second", "Teacher", "admin@school.edu", "secretpa$sword2"
259
+ )
138
260
 
139
- _, name1, _ = create_class_directly(teacher2.user.user.email, class_name="Class 1")
140
- _, name2, _ = create_class_directly(teacher2.user.user.email, class_name="Class 2")
141
- _, name3, _ = create_class_directly(teacher1.user.user.email, class_name="Class 3")
261
+ _, name1, _ = create_class_directly(
262
+ teacher2.user.user.email, class_name="Class 1"
263
+ )
264
+ _, name2, _ = create_class_directly(
265
+ teacher2.user.user.email, class_name="Class 2"
266
+ )
267
+ _, name3, _ = create_class_directly(
268
+ teacher1.user.user.email, class_name="Class 3"
269
+ )
142
270
 
143
271
  c = Client()
144
272
  # First teacher logs in. Should see only Class 3
@@ -147,7 +275,9 @@ class ScoreboardTestCase(TestCase):
147
275
  url = reverse("scoreboard")
148
276
  response = c.get(url)
149
277
 
150
- choices_in_form = [v for (k, v) in response.context["form"]["classes"].field.choices]
278
+ choices_in_form = [
279
+ v for (k, v) in response.context["form"]["classes"].field.choices
280
+ ]
151
281
 
152
282
  assert name3 in choices_in_form
153
283
  assert name1 not in choices_in_form
@@ -158,7 +288,9 @@ class ScoreboardTestCase(TestCase):
158
288
  c.login(username="admin@school.edu", password="secretpa$sword2")
159
289
 
160
290
  response = c.get(url)
161
- choices_in_form = [v for (k, v) in response.context["form"]["classes"].field.choices]
291
+ choices_in_form = [
292
+ v for (k, v) in response.context["form"]["classes"].field.choices
293
+ ]
162
294
 
163
295
  assert name3 not in choices_in_form
164
296
  assert name1 in choices_in_form
@@ -174,7 +306,10 @@ class ScoreboardTestCase(TestCase):
174
306
  url = reverse("scoreboard")
175
307
  response = c.get(url)
176
308
 
177
- assert "Scoreboard is only visible to school students and teachers" in str(response.content)
309
+ assert (
310
+ "Scoreboard is only visible to school students and teachers"
311
+ in str(response.content)
312
+ )
178
313
 
179
314
 
180
315
  class ScoreboardCsvTestCase(TestCase):
@@ -190,14 +325,25 @@ class ScoreboardCsvTestCase(TestCase):
190
325
 
191
326
  # Create 2 custom levels and create the associated student data
192
327
  shared_level_rows = [None, None]
193
- shared_level1 = create_save_level(students[0], "level1", shared_with=[students[1].new_user])
328
+ shared_level1 = create_save_level(
329
+ students[0], "level1", shared_with=[students[1].new_user]
330
+ )
194
331
  shared_level2 = create_save_level(students[1], "level2")
195
332
  shared_levels = [shared_level1, shared_level2]
196
333
 
197
- shared_levels_headers = list([shared_level_to_name(level, level.owner) for level in shared_levels])
334
+ shared_levels_headers = list(
335
+ [
336
+ shared_level_to_name(level, level.owner)
337
+ for level in shared_levels
338
+ ]
339
+ )
198
340
 
199
- shared_level_rows[0] = self.shared_student_row(students[0], shared_levels)
200
- shared_level_rows[1] = self.shared_student_row(students[1], shared_levels)
341
+ shared_level_rows[0] = self.shared_student_row(
342
+ students[0], shared_levels
343
+ )
344
+ shared_level_rows[1] = self.shared_student_row(
345
+ students[1], shared_levels
346
+ )
201
347
 
202
348
  # Create students' improvement table data
203
349
  improvement_data = []
@@ -206,23 +352,44 @@ class ScoreboardCsvTestCase(TestCase):
206
352
  improvement_data.append(stud)
207
353
 
208
354
  # Generate the CSV
209
- response = scoreboard_csv(student_rows, levels, improvement_data, shared_levels_headers, shared_level_rows)
355
+ response = scoreboard_csv(
356
+ student_rows,
357
+ levels,
358
+ improvement_data,
359
+ shared_levels_headers,
360
+ shared_level_rows,
361
+ )
210
362
 
211
363
  # Gather the data from the CSV
212
- actual_scoreboard_header, actual_scoreboard_rows, actual_shared_levels_header, actual_shared_levels_rows = self.actual_data(
213
- response.content.decode("utf-8"), len(students)
214
- )
364
+ (
365
+ actual_scoreboard_header,
366
+ actual_scoreboard_rows,
367
+ actual_shared_levels_header,
368
+ actual_shared_levels_rows,
369
+ ) = self.actual_data(response.content.decode("utf-8"), len(students))
215
370
 
216
371
  # Check the headers and the number or rows match expectations
217
- assert actual_scoreboard_header == self.expected_scoreboard_header(levels)
372
+ assert actual_scoreboard_header == self.expected_scoreboard_header(
373
+ levels
374
+ )
218
375
  assert len(actual_scoreboard_rows) == len(student_rows)
219
- assert actual_shared_levels_header == self.expected_shared_levels_header(shared_levels)
376
+ assert (
377
+ actual_shared_levels_header
378
+ == self.expected_shared_levels_header(shared_levels)
379
+ )
220
380
  assert len(actual_shared_levels_rows) == len(shared_level_rows)
221
381
 
222
382
  # check first scoreboard row
223
- (class_name, name, completed_levels, total_time, total_scores, l1, l2, improvement) = actual_scoreboard_rows[
224
- 0
225
- ].split(",")
383
+ (
384
+ class_name,
385
+ name,
386
+ completed_levels,
387
+ total_time,
388
+ total_scores,
389
+ l1,
390
+ l2,
391
+ improvement,
392
+ ) = actual_scoreboard_rows[0].split(",")
226
393
  assert student_rows[0].class_field.name == class_name
227
394
  assert student_rows[0].name == name
228
395
  assert student_rows[0].level_scores[0]["score"] == int(l1)
@@ -231,9 +398,16 @@ class ScoreboardCsvTestCase(TestCase):
231
398
 
232
399
  # check last scoreboard row
233
400
  last = len(actual_scoreboard_rows) - 1
234
- (class_name, name, completed_levels, total_time, total_scores, l1, l2, improvement) = actual_scoreboard_rows[
235
- last
236
- ].split(",")
401
+ (
402
+ class_name,
403
+ name,
404
+ completed_levels,
405
+ total_time,
406
+ total_scores,
407
+ l1,
408
+ l2,
409
+ improvement,
410
+ ) = actual_scoreboard_rows[last].split(",")
237
411
  assert student_rows[last].class_field.name == class_name
238
412
  assert student_rows[last].name == name
239
413
  assert str(student_rows[last].total_time) == total_time
@@ -279,7 +453,7 @@ class ScoreboardCsvTestCase(TestCase):
279
453
  total_score=total_score,
280
454
  level_scores=level_scores,
281
455
  completed=2,
282
- percentage_complete=45,
456
+ success_rate=45,
283
457
  )
284
458
 
285
459
  return row, student
@@ -310,19 +484,26 @@ class ScoreboardCsvTestCase(TestCase):
310
484
  total_score=0,
311
485
  level_scores=level_scores,
312
486
  completed=0,
313
- percentage_complete=0,
487
+ success_rate=0,
314
488
  )
315
489
 
316
490
  return row
317
491
 
318
492
  def expected_scoreboard_header(self, levels):
319
493
  level_strings = list(map(str, levels))
320
- all_header_strings = CSVHeaders + level_strings + ["Areas for improvement"]
494
+ all_header_strings = (
495
+ CSVHeaders + level_strings + ["Areas for improvement"]
496
+ )
321
497
  joined = ",".join(all_header_strings)
322
498
  return joined
323
499
 
324
500
  def expected_shared_levels_header(self, shared_levels):
325
- level_strings = list([shared_level_to_name(level, level.owner) for level in shared_levels])
501
+ level_strings = list(
502
+ [
503
+ shared_level_to_name(level, level.owner)
504
+ for level in shared_levels
505
+ ]
506
+ )
326
507
  all_header_strings = CSVSharedHeaders + level_strings
327
508
  joined = ",".join(all_header_strings)
328
509
  return joined
@@ -343,14 +524,25 @@ class ScoreboardCsvTestCase(TestCase):
343
524
  scoreboard_header = split[scoreboard_header_row]
344
525
  scoreboard_rows = split[scoreboard_rows_start:scoreboard_rows_end]
345
526
  shared_levels_header = split[shared_levels_header_row]
346
- shared_levels_rows = split[shared_levels_rows_start:shared_levels_rows_end]
347
-
348
- return scoreboard_header, scoreboard_rows, shared_levels_header, shared_levels_rows
527
+ shared_levels_rows = split[
528
+ shared_levels_rows_start:shared_levels_rows_end
529
+ ]
530
+
531
+ return (
532
+ scoreboard_header,
533
+ scoreboard_rows,
534
+ shared_levels_header,
535
+ shared_levels_rows,
536
+ )
349
537
 
350
538
 
351
539
  def create_attempt(student, level, score):
352
540
  attempt = Attempt.objects.create(
353
- finish_time=datetime.fromtimestamp(1435305072), level=level, student=student, score=score, is_best_attempt=True
541
+ finish_time=datetime.fromtimestamp(1435305072),
542
+ level=level,
543
+ student=student,
544
+ score=score,
545
+ is_best_attempt=True,
354
546
  )
355
547
  attempt.start_time = datetime.fromtimestamp(1435305072)
356
548
  attempt.save()
game/tests/utils/level.py CHANGED
@@ -2,19 +2,42 @@ import game.level_management as level_management
2
2
  from game.models import Level
3
3
 
4
4
 
5
+ multiple_house_data = {
6
+ "origin": '{"coordinate":[3,5],"direction":"S"}',
7
+ "python_enabled": False,
8
+ "decor": [],
9
+ "blockly_enabled": True,
10
+ "blocks": [
11
+ {"type": "move_forwards"},
12
+ {"type": "turn_left"},
13
+ {"type": "turn_right"},
14
+ ],
15
+ "max_fuel": "50",
16
+ "python_view_enabled": False,
17
+ "character": "3",
18
+ "name": "2",
19
+ "theme": 1,
20
+ "anonymous": False,
21
+ "cows": "[]",
22
+ "path": '[{"coordinate":[3,5],"connectedNodes":[1]},{"coordinate":[3,4],"connectedNodes":[0,2]}, {"coordinate": [3,3], "connectedNodes":[1]}]',
23
+ "traffic_lights": "[]",
24
+ "destinations": "[[3,4], [3,3]]",
25
+ }
26
+
27
+
5
28
  def create_save_level(teacher_or_student, level_name="1", shared_with=None):
6
29
  data = {
7
30
  "origin": '{"coordinate":[3,5],"direction":"S"}',
8
- "pythonEnabled": False,
31
+ "python_enabled": False,
9
32
  "decor": [],
10
- "blocklyEnabled": True,
33
+ "blockly_enabled": True,
11
34
  "blocks": [
12
35
  {"type": "move_forwards"},
13
36
  {"type": "turn_left"},
14
37
  {"type": "turn_right"},
15
38
  ],
16
39
  "max_fuel": "50",
17
- "pythonViewEnabled": False,
40
+ "python_view_enabled": False,
18
41
  "character": "3",
19
42
  "name": level_name,
20
43
  "theme": 1,
@@ -27,6 +50,23 @@ def create_save_level(teacher_or_student, level_name="1", shared_with=None):
27
50
  level = Level(default=False, anonymous=data["anonymous"])
28
51
  level.owner = teacher_or_student.user
29
52
  level_management.save_level(level, data)
53
+
54
+ if hasattr(level.owner, "teacher"):
55
+ level.needs_approval = False
56
+
57
+ level.save()
58
+
59
+ if shared_with is not None:
60
+ for user in shared_with:
61
+ level.shared_with.add(user)
62
+ level.save()
63
+
64
+ return level
65
+
66
+ def create_save_level_with_multiple_houses(teacher_or_student, level_name="2", shared_with=None):
67
+ level = Level(default=False, anonymous=multiple_house_data["anonymous"])
68
+ level.owner = teacher_or_student.user
69
+ level_management.save_level(level, multiple_house_data)
30
70
  level.save()
31
71
 
32
72
  if shared_with is not None:
@@ -7,13 +7,13 @@ from common.models import School, Teacher
7
7
  def create_school() -> School:
8
8
  school = School()
9
9
  school.name = "".join(random.choice(string.ascii_uppercase) for _ in range(10))
10
- school.postcode = "".join(random.choice(string.ascii_uppercase) for _ in range(7))
11
10
  school.country = "United Kingdom"
12
11
  school.save()
13
12
 
14
13
  return school
15
14
 
16
15
 
17
- def add_teacher_to_school(teacher: Teacher, school: School):
16
+ def add_teacher_to_school(teacher: Teacher, school: School, is_admin=False):
18
17
  teacher.user.teacher.school = school
18
+ teacher.is_admin = is_admin
19
19
  teacher.user.teacher.save()
game/theme.py CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  from builtins import object
6
6
  from rest_framework.reverse import reverse
7
- from django.utils.translation import ugettext
7
+ from django.utils.translation import gettext
8
8
 
9
9
 
10
10
  class Theme(object):
@@ -19,35 +19,35 @@ class Theme(object):
19
19
 
20
20
  THEME_DATA = {
21
21
  "grass": Theme(
22
- name=u"grass",
23
- text=ugettext(u"Grass"),
24
- selected=u"#bce369",
25
- background=u"#a0c53a",
26
- border=u"#70961f",
22
+ name="grass",
23
+ text=gettext("Grass"),
24
+ selected="#bce369",
25
+ background="#a0c53a",
26
+ border="#70961f",
27
27
  pk=1,
28
28
  ),
29
29
  "snow": Theme(
30
- name=u"snow",
31
- text=ugettext(u"Snow"),
32
- selected=u"#b3deff",
33
- background=u"#eef7ff",
34
- border=u"#83c9fe",
30
+ name="snow",
31
+ text=gettext("Snow"),
32
+ selected="#b3deff",
33
+ background="#eef7ff",
34
+ border="#83c9fe",
35
35
  pk=2,
36
36
  ),
37
37
  "farm": Theme(
38
- name=u"farm",
39
- text=ugettext(u"Farm"),
40
- selected=u"#bce369",
41
- background=u"#a0c53a",
42
- border=u"#70961f",
38
+ name="farm",
39
+ text=gettext("Farm"),
40
+ selected="#bce369",
41
+ background="#a0c53a",
42
+ border="#70961f",
43
43
  pk=3,
44
44
  ),
45
45
  "city": Theme(
46
- name=u"city",
47
- text=ugettext(u"City"),
48
- selected=u"#C1C1C1",
49
- background=u"#969696",
50
- border=u"#686868",
46
+ name="city",
47
+ text=gettext("City"),
48
+ selected="#C1C1C1",
49
+ background="#969696",
50
+ border="#686868",
51
51
  pk=4,
52
52
  ),
53
53
  }