rapid-router 5.4.1__py2.py3-none-any.whl → 7.6.8__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. example_project/rapid_router_test_settings.py +164 -0
  2. example_project/settings.py +152 -0
  3. example_project/urls.py +15 -0
  4. example_project/{example_project/wsgi.py → wsgi.py} +2 -1
  5. game/__init__.py +1 -1
  6. game/admin.py +43 -4
  7. game/app_settings.py +3 -7
  8. game/character.py +26 -18
  9. game/decor.py +172 -97
  10. game/end_to_end_tests/base_game_test.py +44 -33
  11. game/end_to_end_tests/editor_page.py +17 -2
  12. game/end_to_end_tests/game_page.py +127 -45
  13. game/end_to_end_tests/selenium_test_case.py +1 -20
  14. game/end_to_end_tests/test_cow_crashes.py +3 -5
  15. game/end_to_end_tests/test_language_dropdown.py +14 -0
  16. game/end_to_end_tests/test_level_editor.py +290 -0
  17. game/end_to_end_tests/test_level_failures.py +1 -1
  18. game/end_to_end_tests/test_level_selection.py +79 -0
  19. game/end_to_end_tests/test_play_through.py +240 -102
  20. game/end_to_end_tests/test_python_levels.py +44 -13
  21. game/end_to_end_tests/test_saving_workspace.py +22 -0
  22. game/forms.py +9 -2
  23. game/level_management.py +38 -29
  24. game/messages.py +1218 -203
  25. game/migrations/0001_squashed_0025_levels_ordering_pt1.py +19 -1
  26. game/migrations/0026_levels_pt2.py +13 -2
  27. game/migrations/0032_cannot_turn_left_level.py +13 -2
  28. game/migrations/0033_recursion_level.py +13 -2
  29. game/migrations/0034_joes_level.py +13 -2
  30. game/migrations/0035_disable_route_score_level_70.py +0 -2
  31. game/migrations/0036_level_score_73.py +0 -2
  32. game/migrations/0037_level_score_79.py +0 -2
  33. game/migrations/0038_level_score_40.py +0 -1
  34. game/migrations/0042_level_score_73.py +0 -2
  35. game/migrations/0048_add_cow_field_and_blocks.py +0 -2
  36. game/migrations/0049_level_score_34.py +0 -2
  37. game/migrations/0050_level_score_40.py +0 -2
  38. game/migrations/0051_level_score_49.py +0 -1
  39. game/migrations/0076_level_locked_for_class.py +19 -0
  40. game/migrations/0077_alter_level_next_level.py +52 -0
  41. game/migrations/0078_add_block_types.py +23 -0
  42. game/migrations/0079_populate_block_type_add_cow_blocks.py +60 -0
  43. game/migrations/0080_level_disable_algorithm_score.py +18 -0
  44. game/migrations/0081_first_12_levels_no_algo_score.py +29 -0
  45. game/migrations/0082_level_43_solution.py +16 -0
  46. game/migrations/0083_add_cows_to_existing_levels.py +195 -0
  47. game/migrations/0084_alter_block_block_type.py +18 -0
  48. game/migrations/0085_add_new_blocks.py +53 -0
  49. game/migrations/0086_loop_levels.py +482 -0
  50. game/migrations/0087_workspace_python_view_enabled.py +18 -0
  51. game/migrations/0088_rename_episodes.py +35 -0
  52. game/migrations/0089_episodes_in_development.py +30 -0
  53. game/migrations/0090_add_missing_model_solutions.py +144 -0
  54. game/migrations/0091_disable_algo_score_if_no_model_solution.py +46 -0
  55. game/migrations/0092_disable_algo_score_in_custom_levels.py +28 -0
  56. game/migrations/0093_alter_level_character_name.py +18 -0
  57. game/migrations/0094_add_hint_lesson_subtitle_to_levels.py +28 -0
  58. game/migrations/0095_level_commands.py +18 -0
  59. game/migrations/0096_alter_level_commands.py +18 -0
  60. game/migrations/0097_add_python_den_levels.py +1515 -0
  61. game/migrations/0098_add_episode_link_fields.py +44 -0
  62. game/migrations/0099_python_episodes_links.py +103 -0
  63. game/migrations/0100_reorder_python_levels.py +179 -0
  64. game/migrations/0101_rename_episodes.py +45 -0
  65. game/migrations/0102_reoder_episodes_13_14.py +136 -0
  66. game/migrations/0103_level_1015_solution.py +26 -0
  67. game/migrations/0104_remove_level_direct_drive.py +17 -0
  68. game/migrations/0105_delete_invalid_attempts.py +18 -0
  69. game/migrations/0106_fields_to_snake_case.py +48 -0
  70. game/migrations/0107_rename_worksheet_link_episode_student_worksheet_link.py +18 -0
  71. game/migrations/0108_episode_indy_worksheet_link.py +18 -0
  72. game/migrations/0109_create_episodes_23_and_24.py +99 -0
  73. game/migrations/0110_remove_episode_indy_worksheet_link_and_more.py +100 -0
  74. game/migrations/0111_create_worksheets.py +149 -0
  75. game/migrations/0112_worksheet_locked_classes.py +21 -0
  76. game/migrations/0113_level_needs_approval.py +18 -0
  77. game/migrations/0114_default_and_non_student_levels_no_approval.py +31 -0
  78. game/migrations/0115_level_level__default_does_not_need_approval.py +22 -0
  79. game/migrations/0116_update_worksheet_video_links.py +68 -0
  80. game/migrations/0117_update_solutions_to_if_else.py +61 -0
  81. game/models.py +157 -16
  82. game/permissions.py +34 -19
  83. game/python_den_urls.py +26 -0
  84. game/random_road.py +43 -127
  85. game/serializers.py +12 -17
  86. game/static/django_reverse_js/js/reverse.js +171 -0
  87. game/static/game/css/LilitaOne-Regular.ttf +0 -0
  88. game/static/game/css/backgrounds.css +14 -10
  89. game/static/game/css/dataTables.custom.css +4 -2
  90. game/static/game/css/dataTables.jqueryui.css +561 -320
  91. game/static/game/css/editor.css +47 -0
  92. game/static/game/css/game.css +43 -49
  93. game/static/game/css/game_screen.css +116 -53
  94. game/static/game/css/jquery.dataTables.css +455 -251
  95. game/static/game/css/level_editor.css +10 -1
  96. game/static/game/css/level_selection.css +32 -3
  97. game/static/game/css/level_share.css +6 -5
  98. game/static/game/css/skulpt/codemirror.css +1 -0
  99. game/static/game/image/Python_Den_hero_student.png +0 -0
  100. game/static/game/image/Python_levels_page.svg +1954 -0
  101. game/static/game/image/characters/front_view/Electric_van.svg +448 -0
  102. game/static/game/image/characters/top_view/Electric_van.svg +448 -0
  103. game/static/game/image/characters/top_view/Sleigh.svg +436 -0
  104. game/static/game/image/decor/city/solar_panel.svg +1200 -0
  105. game/static/game/image/decor/farm/solar_panel.svg +86 -0
  106. game/static/game/image/decor/grass/solar_panel.svg +86 -0
  107. game/static/game/image/decor/snow/barn.svg +1788 -0
  108. game/static/game/image/decor/snow/cfc.svg +1050 -147
  109. game/static/game/image/decor/snow/crops.svg +7370 -0
  110. game/static/game/image/decor/snow/hospital.svg +1220 -0
  111. game/static/game/image/decor/snow/house1.svg +971 -0
  112. game/static/game/image/decor/snow/house2.svg +1574 -0
  113. game/static/game/image/decor/snow/school.svg +1071 -0
  114. game/static/game/image/decor/snow/shop.svg +3211 -0
  115. game/static/game/image/decor/snow/solar_panel.svg +173 -0
  116. game/static/game/image/electric_van.svg +448 -0
  117. game/static/game/image/icons/add_house.svg +26 -0
  118. game/static/game/image/icons/delete_house.svg +26 -0
  119. game/static/game/image/icons/description.svg +1 -0
  120. game/static/game/image/icons/hint.svg +1 -0
  121. game/static/game/image/icons/if_else.svg +3 -0
  122. game/static/game/image/icons/python.svg +1 -1
  123. game/static/game/image/if_else_example.png +0 -0
  124. game/static/game/image/pigeon.svg +684 -0
  125. game/static/game/image/python_den_header.svg +19 -0
  126. game/static/game/js/animation.js +84 -28
  127. game/static/game/js/blockly/msg/js/bg.js +52 -1
  128. game/static/game/js/blockly/msg/js/ca.js +52 -1
  129. game/static/game/js/blockly/msg/js/en-gb.js +52 -1
  130. game/static/game/js/blockly/msg/js/en.js +52 -1
  131. game/static/game/js/blockly/msg/js/es.js +52 -1
  132. game/static/game/js/blockly/msg/js/fr.js +52 -1
  133. game/static/game/js/blockly/msg/js/hi.js +52 -1
  134. game/static/game/js/blockly/msg/js/it.js +52 -1
  135. game/static/game/js/blockly/msg/js/pl.js +52 -1
  136. game/static/game/js/blockly/msg/js/pt-br.js +52 -1
  137. game/static/game/js/blockly/msg/js/ru.js +52 -1
  138. game/static/game/js/blockly/msg/js/ur.js +52 -1
  139. game/static/game/js/blocklyCompiler.js +550 -392
  140. game/static/game/js/blocklyControl.js +335 -302
  141. game/static/game/js/blocklyCustomBlocks.js +691 -458
  142. game/static/game/js/blocklyCustomisations.js +3 -1
  143. game/static/game/js/button.js +12 -0
  144. game/static/game/js/cow.js +15 -130
  145. game/static/game/js/drawing.js +313 -201
  146. game/static/game/js/editor.js +23 -0
  147. game/static/game/js/game.js +148 -139
  148. game/static/game/js/jquery.dataTables.min.js +3 -159
  149. game/static/game/js/level_editor.js +823 -448
  150. game/static/game/js/level_moderation.js +33 -2
  151. game/static/game/js/level_selection.js +62 -25
  152. game/static/game/js/loadLanguages.js +21 -0
  153. game/static/game/js/map.js +106 -36
  154. game/static/game/js/model.js +55 -107
  155. game/static/game/js/pathFinder.js +73 -72
  156. game/static/game/js/program.js +184 -193
  157. game/static/game/js/pythonControl.js +14 -1
  158. game/static/game/js/scoreboard.js +0 -37
  159. game/static/game/js/scoreboardSharedLevels.js +48 -0
  160. game/static/game/js/sharing.js +22 -10
  161. game/static/game/js/skulpt/codemirror.js +5 -4
  162. game/static/game/js/skulpt/skulpt-stdlib.js +1 -1
  163. game/static/game/js/sound.js +52 -5
  164. game/static/game/js/van.js +0 -7
  165. game/static/game/raphael_image/characters/top_view/Electric_van.svg +448 -0
  166. game/static/game/raphael_image/characters/top_view/Sleigh.svg +436 -0
  167. game/static/game/raphael_image/decor/city/solar_panel.svg +1200 -0
  168. game/static/game/raphael_image/decor/farm/solar_panel.svg +86 -0
  169. game/static/game/raphael_image/decor/grass/solar_panel.svg +86 -0
  170. game/static/game/raphael_image/decor/snow/barn.svg +1788 -0
  171. game/static/game/raphael_image/decor/snow/cfc.svg +1050 -147
  172. game/static/game/raphael_image/decor/snow/crops.svg +7370 -0
  173. game/static/game/raphael_image/decor/snow/hospital.svg +1220 -0
  174. game/static/game/raphael_image/decor/snow/house1.svg +971 -0
  175. game/static/game/raphael_image/decor/snow/house2.svg +1574 -0
  176. game/static/game/raphael_image/decor/snow/school.svg +1071 -0
  177. game/static/game/raphael_image/decor/snow/shop.svg +3211 -0
  178. game/static/game/raphael_image/decor/snow/solar_panel.svg +173 -0
  179. game/static/game/raphael_image/pigeon.svg +685 -0
  180. game/static/game/raphael_image/sleigh_wreckage.svg +430 -0
  181. game/static/game/sass/game.scss +22 -6
  182. game/static/game/sound/clown_horn.mp3 +0 -0
  183. game/static/game/sound/clown_horn.ogg +0 -0
  184. game/static/game/sound/electric_van_starting.mp3 +0 -0
  185. game/static/game/sound/electric_van_starting.ogg +0 -0
  186. game/static/game/sound/pigeon.mp3 +0 -0
  187. game/static/game/sound/pigeon.ogg +0 -0
  188. game/static/game/sound/sleigh_bells.mp3 +0 -0
  189. game/static/game/sound/sleigh_bells.ogg +0 -0
  190. game/static/game/sound/sleigh_crash.mp3 +0 -0
  191. game/static/game/sound/sleigh_crash.ogg +0 -0
  192. game/templates/game/base.html +35 -15
  193. game/templates/game/basenonav.html +23 -17
  194. game/templates/game/game.html +236 -111
  195. game/templates/game/level_editor.html +353 -275
  196. game/templates/game/level_moderation.html +19 -6
  197. game/templates/game/level_selection.html +75 -62
  198. game/templates/game/python_den_level_selection.html +291 -0
  199. game/templates/game/python_den_worksheet.html +101 -0
  200. game/templates/game/scoreboard.html +88 -65
  201. game/tests/test_level_editor.py +210 -35
  202. game/tests/test_level_moderation.py +6 -20
  203. game/tests/test_level_selection.py +332 -11
  204. game/tests/test_python_den_worksheet.py +85 -0
  205. game/tests/test_scoreboard.py +258 -66
  206. game/tests/utils/level.py +43 -3
  207. game/tests/utils/teacher.py +2 -2
  208. game/theme.py +21 -21
  209. game/urls.py +125 -78
  210. game/views/language_code_conversions.py +90 -0
  211. game/views/level.py +201 -63
  212. game/views/level_editor.py +109 -48
  213. game/views/level_moderation.py +29 -6
  214. game/views/level_selection.py +179 -56
  215. game/views/level_solutions.py +600 -106
  216. game/views/scoreboard.py +181 -66
  217. game/views/worksheet.py +25 -0
  218. rapid_router-7.6.8.dist-info/METADATA +174 -0
  219. {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/RECORD +222 -242
  220. {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/WHEEL +1 -1
  221. rapid_router-7.6.8.dist-info/licenses/LICENSE.md +3 -0
  222. example_project/example_project/__init__.py +0 -1
  223. example_project/example_project/settings.py +0 -54
  224. example_project/example_project/urls.py +0 -16
  225. example_project/manage.py +0 -10
  226. game/autoconfig.py +0 -59
  227. game/csp_config.py +0 -23
  228. game/locale/ar_SA/LC_MESSAGES/django.mo +0 -0
  229. game/locale/ar_SA/LC_MESSAGES/django.po +0 -405
  230. game/locale/ar_SA/LC_MESSAGES/djangojs.mo +0 -0
  231. game/locale/ar_SA/LC_MESSAGES/djangojs.po +0 -743
  232. game/locale/bg_BG/LC_MESSAGES/django.mo +0 -0
  233. game/locale/bg_BG/LC_MESSAGES/django.po +0 -405
  234. game/locale/bg_BG/LC_MESSAGES/djangojs.mo +0 -0
  235. game/locale/bg_BG/LC_MESSAGES/djangojs.po +0 -739
  236. game/locale/ca_ES/LC_MESSAGES/django.mo +0 -0
  237. game/locale/ca_ES/LC_MESSAGES/django.po +0 -405
  238. game/locale/ca_ES/LC_MESSAGES/djangojs.mo +0 -0
  239. game/locale/ca_ES/LC_MESSAGES/djangojs.po +0 -740
  240. game/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  241. game/locale/cs_CZ/LC_MESSAGES/django.po +0 -405
  242. game/locale/cs_CZ/LC_MESSAGES/djangojs.mo +0 -0
  243. game/locale/cs_CZ/LC_MESSAGES/djangojs.po +0 -741
  244. game/locale/cy_GB/LC_MESSAGES/django.mo +0 -0
  245. game/locale/cy_GB/LC_MESSAGES/django.po +0 -405
  246. game/locale/cy_GB/LC_MESSAGES/djangojs.mo +0 -0
  247. game/locale/cy_GB/LC_MESSAGES/djangojs.po +0 -743
  248. game/locale/de_DE/LC_MESSAGES/django.mo +0 -0
  249. game/locale/de_DE/LC_MESSAGES/django.po +0 -405
  250. game/locale/de_DE/LC_MESSAGES/djangojs.mo +0 -0
  251. game/locale/de_DE/LC_MESSAGES/djangojs.po +0 -739
  252. game/locale/el_GR/LC_MESSAGES/django.mo +0 -0
  253. game/locale/el_GR/LC_MESSAGES/django.po +0 -405
  254. game/locale/el_GR/LC_MESSAGES/djangojs.mo +0 -0
  255. game/locale/el_GR/LC_MESSAGES/djangojs.po +0 -739
  256. game/locale/en_GB/LC_MESSAGES/django.mo +0 -0
  257. game/locale/en_GB/LC_MESSAGES/django.po +0 -405
  258. game/locale/en_GB/LC_MESSAGES/djangojs.mo +0 -0
  259. game/locale/en_GB/LC_MESSAGES/djangojs.po +0 -739
  260. game/locale/es_ES/LC_MESSAGES/django.mo +0 -0
  261. game/locale/es_ES/LC_MESSAGES/django.po +0 -405
  262. game/locale/es_ES/LC_MESSAGES/djangojs.mo +0 -0
  263. game/locale/es_ES/LC_MESSAGES/djangojs.po +0 -739
  264. game/locale/fi_FI/LC_MESSAGES/django.mo +0 -0
  265. game/locale/fi_FI/LC_MESSAGES/django.po +0 -405
  266. game/locale/fi_FI/LC_MESSAGES/djangojs.mo +0 -0
  267. game/locale/fi_FI/LC_MESSAGES/djangojs.po +0 -739
  268. game/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  269. game/locale/fr_FR/LC_MESSAGES/django.po +0 -405
  270. game/locale/fr_FR/LC_MESSAGES/djangojs.mo +0 -0
  271. game/locale/fr_FR/LC_MESSAGES/djangojs.po +0 -739
  272. game/locale/gu_IN/LC_MESSAGES/django.mo +0 -0
  273. game/locale/gu_IN/LC_MESSAGES/django.po +0 -405
  274. game/locale/gu_IN/LC_MESSAGES/djangojs.mo +0 -0
  275. game/locale/gu_IN/LC_MESSAGES/djangojs.po +0 -739
  276. game/locale/hi_IN/LC_MESSAGES/django.mo +0 -0
  277. game/locale/hi_IN/LC_MESSAGES/django.po +0 -405
  278. game/locale/hi_IN/LC_MESSAGES/djangojs.mo +0 -0
  279. game/locale/hi_IN/LC_MESSAGES/djangojs.po +0 -739
  280. game/locale/id_ID/LC_MESSAGES/django.mo +0 -0
  281. game/locale/id_ID/LC_MESSAGES/django.po +0 -405
  282. game/locale/id_ID/LC_MESSAGES/djangojs.mo +0 -0
  283. game/locale/id_ID/LC_MESSAGES/djangojs.po +0 -738
  284. game/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  285. game/locale/it_IT/LC_MESSAGES/django.po +0 -405
  286. game/locale/it_IT/LC_MESSAGES/djangojs.mo +0 -0
  287. game/locale/it_IT/LC_MESSAGES/djangojs.po +0 -739
  288. game/locale/ja_JP/LC_MESSAGES/django.mo +0 -0
  289. game/locale/ja_JP/LC_MESSAGES/django.po +0 -405
  290. game/locale/ja_JP/LC_MESSAGES/djangojs.mo +0 -0
  291. game/locale/ja_JP/LC_MESSAGES/djangojs.po +0 -738
  292. game/locale/lol_US/LC_MESSAGES/django.mo +0 -0
  293. game/locale/lol_US/LC_MESSAGES/django.po +0 -405
  294. game/locale/lol_US/LC_MESSAGES/djangojs.mo +0 -0
  295. game/locale/lol_US/LC_MESSAGES/djangojs.po +0 -739
  296. game/locale/nb_NO/LC_MESSAGES/django.mo +0 -0
  297. game/locale/nb_NO/LC_MESSAGES/django.po +0 -405
  298. game/locale/nb_NO/LC_MESSAGES/djangojs.mo +0 -0
  299. game/locale/nb_NO/LC_MESSAGES/djangojs.po +0 -739
  300. game/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  301. game/locale/nl_NL/LC_MESSAGES/django.po +0 -405
  302. game/locale/nl_NL/LC_MESSAGES/djangojs.mo +0 -0
  303. game/locale/nl_NL/LC_MESSAGES/djangojs.po +0 -739
  304. game/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  305. game/locale/pl_PL/LC_MESSAGES/django.po +0 -405
  306. game/locale/pl_PL/LC_MESSAGES/djangojs.mo +0 -0
  307. game/locale/pl_PL/LC_MESSAGES/djangojs.po +0 -741
  308. game/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  309. game/locale/pt_BR/LC_MESSAGES/django.po +0 -405
  310. game/locale/pt_BR/LC_MESSAGES/djangojs.mo +0 -0
  311. game/locale/pt_BR/LC_MESSAGES/djangojs.po +0 -739
  312. game/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  313. game/locale/pt_PT/LC_MESSAGES/django.po +0 -405
  314. game/locale/pt_PT/LC_MESSAGES/djangojs.mo +0 -0
  315. game/locale/pt_PT/LC_MESSAGES/djangojs.po +0 -739
  316. game/locale/ro_RO/LC_MESSAGES/django.mo +0 -0
  317. game/locale/ro_RO/LC_MESSAGES/django.po +0 -405
  318. game/locale/ro_RO/LC_MESSAGES/djangojs.mo +0 -0
  319. game/locale/ro_RO/LC_MESSAGES/djangojs.po +0 -740
  320. game/locale/ru_RU/LC_MESSAGES/django.mo +0 -0
  321. game/locale/ru_RU/LC_MESSAGES/django.po +0 -405
  322. game/locale/ru_RU/LC_MESSAGES/djangojs.mo +0 -0
  323. game/locale/ru_RU/LC_MESSAGES/djangojs.po +0 -741
  324. game/locale/sv_SE/LC_MESSAGES/django.mo +0 -0
  325. game/locale/sv_SE/LC_MESSAGES/django.po +0 -405
  326. game/locale/sv_SE/LC_MESSAGES/djangojs.mo +0 -0
  327. game/locale/sv_SE/LC_MESSAGES/djangojs.po +0 -739
  328. game/locale/tl_PH/LC_MESSAGES/django.mo +0 -0
  329. game/locale/tl_PH/LC_MESSAGES/django.po +0 -405
  330. game/locale/tl_PH/LC_MESSAGES/djangojs.mo +0 -0
  331. game/locale/tl_PH/LC_MESSAGES/djangojs.po +0 -739
  332. game/locale/tlh_AA/LC_MESSAGES/django.mo +0 -0
  333. game/locale/tlh_AA/LC_MESSAGES/django.po +0 -405
  334. game/locale/tlh_AA/LC_MESSAGES/djangojs.mo +0 -0
  335. game/locale/tlh_AA/LC_MESSAGES/djangojs.po +0 -739
  336. game/locale/tr_TR/LC_MESSAGES/django.mo +0 -0
  337. game/locale/tr_TR/LC_MESSAGES/django.po +0 -405
  338. game/locale/tr_TR/LC_MESSAGES/djangojs.mo +0 -0
  339. game/locale/tr_TR/LC_MESSAGES/djangojs.po +0 -740
  340. game/locale/ur_IN/LC_MESSAGES/django.mo +0 -0
  341. game/locale/ur_IN/LC_MESSAGES/django.po +0 -405
  342. game/locale/ur_IN/LC_MESSAGES/djangojs.mo +0 -0
  343. game/locale/ur_IN/LC_MESSAGES/djangojs.po +0 -739
  344. game/locale/ur_PK/LC_MESSAGES/django.mo +0 -0
  345. game/locale/ur_PK/LC_MESSAGES/django.po +0 -405
  346. game/locale/ur_PK/LC_MESSAGES/djangojs.mo +0 -0
  347. game/locale/ur_PK/LC_MESSAGES/djangojs.po +0 -739
  348. game/static/game/image/actions/go.svg +0 -18
  349. game/static/game/image/icons/destination.svg +0 -9
  350. game/static/game/js/pqselect.min.js +0 -9
  351. game/static/game/js/widget-scroller.js +0 -906
  352. rapid_router-5.4.1.dist-info/LICENSE.md +0 -577
  353. rapid_router-5.4.1.dist-info/METADATA +0 -24
  354. {rapid_router-5.4.1.dist-info → rapid_router-7.6.8.dist-info}/top_level.txt +0 -0
game/views/level.py CHANGED
@@ -1,15 +1,13 @@
1
- from __future__ import division
2
- from __future__ import absolute_import
1
+ from __future__ import absolute_import, division
3
2
 
4
- from builtins import str
5
- from builtins import object
6
3
  import json
4
+ from builtins import object, str
5
+ from datetime import datetime
7
6
 
8
- from django.urls import reverse
9
7
  from django.http import Http404, HttpResponse
10
- from django.shortcuts import render, get_object_or_404
8
+ from django.shortcuts import get_object_or_404, redirect, render
9
+ from django.urls import reverse
11
10
  from django.utils import timezone
12
- from django.utils.safestring import mark_safe
13
11
  from django.views.decorators.http import require_POST
14
12
  from rest_framework import serializers
15
13
 
@@ -18,15 +16,19 @@ import game.messages as messages
18
16
  import game.permissions as permissions
19
17
  from game import app_settings
20
18
  from game.cache import (
19
+ cached_custom_level,
21
20
  cached_default_level,
22
21
  cached_episode,
23
- cached_custom_level,
24
- cached_level_decor,
25
22
  cached_level_blocks,
23
+ cached_level_decor,
26
24
  )
25
+ from game.character import get_character
27
26
  from game.decor import get_decor_element
28
- from game.models import Level, Attempt, Workspace
27
+ from game.models import Attempt, Level, Workspace
28
+ from game.theme import get_theme
29
+ from game.views.language_code_conversions import language_code_dict
29
30
  from game.views.level_solutions import solutions
31
+
30
32
  from .helper import renderError
31
33
 
32
34
 
@@ -42,16 +44,94 @@ def play_custom_level(request, levelId, from_editor=False):
42
44
  return play_level(request, level, from_editor)
43
45
 
44
46
 
45
- def play_default_level(request, levelName):
46
- level = cached_default_level(levelName)
47
+ def play_default_level(request, level_name):
48
+ level_index = int(level_name)
49
+ if level_index > 79:
50
+ raise Http404
51
+ if (
52
+ level_index > 19
53
+ and not level_index in [29, 33, 44, 51, 61, 68]
54
+ and not request.user.is_authenticated
55
+ ):
56
+ return redirect(reverse("levels"))
57
+
58
+ level = cached_default_level(level_name)
47
59
  return play_level(request, level)
48
60
 
49
61
 
50
- def _next_level_url(level, night_mode):
62
+ def play_default_python_level(request, level_name):
63
+ level_index = int(level_name)
64
+ if level_index > 49:
65
+ raise Http404
66
+ if (
67
+ level_index > 26
68
+ and not level_index in [41]
69
+ and not request.user.is_authenticated
70
+ ):
71
+ return redirect(reverse("python_levels"))
72
+
73
+ levelId = int(level_name) + 1000
74
+
75
+ level = cached_default_level(levelId)
76
+ return play_level(request, level, from_python_den=True)
77
+
78
+
79
+ def _prev_level_url(level, user, night_mode, from_python_den):
80
+ """
81
+ Find the previous available level. Check if level is available if so, go to
82
+ it.
83
+ """
84
+
85
+ if not level.prev_level.all():
86
+ return ""
87
+
88
+ prev_level = level.prev_level.all()[0]
89
+ if not user.is_anonymous and hasattr(user.userprofile, "student"):
90
+ student = user.userprofile.student
91
+ klass = student.class_field
92
+
93
+ is_prev_level_locked = klass in prev_level.locked_for_class.all()
94
+ if is_prev_level_locked:
95
+ while is_prev_level_locked and int(prev_level.name) > 1:
96
+ prev_level = prev_level.prev_level.all()[0]
97
+ is_prev_level_locked = klass in prev_level.locked_for_class.all()
98
+
99
+ return _level_url(prev_level, night_mode, from_python_den)
100
+
101
+
102
+ def _next_level_url(level, user, night_mode, from_python_den):
103
+ """
104
+ Find the next available level. By default, this will be the `next_level`
105
+ field in the Level model, but in the case where the user is a student and
106
+ the teacher has locked certain levels, then loop until we find the next
107
+ unlocked level (or we run out of levels).
108
+ """
109
+
51
110
  if not level.next_level:
111
+ if (
112
+ level.episode
113
+ and level.episode.next_episode
114
+ and len(level.episode.next_episode.levels) == 0
115
+ ):
116
+ return reverse("python_levels")
52
117
  return ""
53
118
 
54
- return _level_url(level.next_level, night_mode)
119
+ next_level = level.next_level
120
+
121
+ if not user.is_anonymous and hasattr(user.userprofile, "student"):
122
+ student = user.userprofile.student
123
+ klass = student.class_field
124
+
125
+ is_next_level_locked = klass in next_level.locked_for_class.all()
126
+
127
+ if is_next_level_locked:
128
+ while is_next_level_locked and (
129
+ int(next_level.name) < 1050 if from_python_den else 80
130
+ ):
131
+ next_level = next_level.next_level
132
+ is_next_level_locked = klass in next_level.locked_for_class.all()
133
+
134
+ return _level_url(next_level, night_mode, from_python_den)
55
135
 
56
136
 
57
137
  def add_night(url, night_mode):
@@ -60,24 +140,28 @@ def add_night(url, night_mode):
60
140
  return url
61
141
 
62
142
 
63
- def _level_url(level, night_mode):
143
+ def _level_url(level, night_mode, from_python_den):
64
144
  if level.default:
65
- result = _default_level_url(level)
145
+ result = _default_level_url(level, from_python_den)
66
146
  else:
67
147
  result = _custom_level_url(level)
68
148
 
69
149
  return add_night(result, night_mode)
70
150
 
71
151
 
72
- def _default_level_url(level):
73
- return reverse("play_default_level", args=[level.name])
152
+ def _default_level_url(level, from_python_den):
153
+ viewname = "play_python_default_level" if from_python_den else "play_default_level"
154
+
155
+ level_name = int(level.name) - 1000 if from_python_den else level.name
156
+
157
+ return reverse(viewname, args=[level_name])
74
158
 
75
159
 
76
160
  def _custom_level_url(level):
77
161
  return reverse("play_custom_level", args=[level.id])
78
162
 
79
163
 
80
- def play_level(request, level, from_editor=False):
164
+ def play_level(request, level, from_editor=False, from_python_den=False):
81
165
  """Loads a level for rendering in the game.
82
166
 
83
167
  **Context**
@@ -105,34 +189,43 @@ def play_level(request, level, from_editor=False):
105
189
  request.user, level, app_settings.EARLY_ACCESS_FUNCTION(request)
106
190
  ):
107
191
  return renderError(
108
- request, messages.noPermissionTitle(), messages.notSharedLevel()
192
+ request, messages.no_permission_title(), messages.not_shared_level()
109
193
  )
110
194
 
111
- # Set default level description/hint lookups
112
- lesson = "description_level_default"
113
- hint = "hint_level_default"
114
-
115
- # If it's one of our levels, set level description/hint lookups
116
- # to point to what they should be
117
- if level.default:
118
- lesson = "description_level" + str(level.name)
119
- hint = "hint_level" + str(level.name)
120
-
121
- # Try to get the relevant message, and fall back on defaults
122
- try:
123
- lessonCall = getattr(messages, lesson)
124
- hintCall = getattr(messages, hint)
125
- except AttributeError:
126
- lessonCall = messages.description_level_default
127
- hintCall = messages.hint_level_default
128
-
129
- lesson = mark_safe(lessonCall())
130
- hint = mark_safe(hintCall())
131
-
132
- house = get_decor_element("house", level.theme).url
133
- cfc = get_decor_element("cfc", level.theme).url
134
- background = get_decor_element("tile1", level.theme).url
195
+ subtitle = level.subtitle
196
+ lesson = (
197
+ getattr(messages, "description_level" + str(level.name))
198
+ if level.default
199
+ else level.lesson
200
+ )
201
+ hint = (
202
+ getattr(messages, "hint_level" + str(level.name))
203
+ if level.default
204
+ else level.hint
205
+ )
206
+ commands_attr = "commands_level" + str(level.name)
207
+ commands = (
208
+ getattr(messages, commands_attr, None) if level.default else level.commands
209
+ )
135
210
  character = level.character
211
+ character_url = character.top_down
212
+ wreckage_url = "van_wreckage.svg"
213
+
214
+ if datetime.now().month == 12:
215
+ house = get_decor_element("house", get_theme("snow")).url
216
+ cfc = get_decor_element("cfc", get_theme("snow")).url
217
+ background = get_decor_element("tile1", get_theme("snow")).url
218
+
219
+ if character == get_character("Van"):
220
+ character_url = "characters/top_view/Sleigh.svg"
221
+ wreckage_url = "sleigh_wreckage.svg"
222
+ else:
223
+ house = get_decor_element("house", level.theme).url
224
+ cfc = get_decor_element("cfc", level.theme).url
225
+ background = get_decor_element("tile1", level.theme).url
226
+
227
+ character_width = character.width
228
+ character_height = character.height
136
229
 
137
230
  workspace = None
138
231
  python_workspace = None
@@ -162,11 +255,6 @@ def play_level(request, level, from_editor=False):
162
255
 
163
256
  decor_data = cached_level_decor(level)
164
257
 
165
- character_url = character.top_down
166
- character_width = character.width
167
- character_height = character.height
168
- wreckage_url = "van_wreckage.svg"
169
-
170
258
  if night_mode:
171
259
  block_data = level_management.get_night_blocks(level)
172
260
  night_mode_javascript = "true"
@@ -177,21 +265,36 @@ def play_level(request, level, from_editor=False):
177
265
  night_mode_javascript = "false"
178
266
  model_solution = level.model_solution
179
267
 
180
- return_view = "level_editor" if from_editor else "levels"
268
+ return_view = (
269
+ "level_editor"
270
+ if from_editor
271
+ else "python_levels" if from_python_den else "levels"
272
+ )
273
+
274
+ temp_block_data = []
275
+ [
276
+ temp_block_data.append(block)
277
+ for block in block_data
278
+ if block not in temp_block_data
279
+ ]
280
+
281
+ block_data = temp_block_data
181
282
 
182
283
  return render(
183
284
  request,
184
285
  "game/game.html",
185
286
  context={
186
287
  "level": level,
288
+ "subtitle": subtitle,
187
289
  "lesson": lesson,
290
+ "hint": hint,
291
+ "commands": commands if commands is not None else level.commands,
188
292
  "blocks": block_data,
189
293
  "decor": decor_data,
190
294
  "character": character,
191
295
  "background": background,
192
296
  "house": house,
193
297
  "cfc": cfc,
194
- "hint": hint,
195
298
  "workspace": workspace,
196
299
  "python_workspace": python_workspace,
197
300
  "return_url": reverse(return_view),
@@ -204,8 +307,14 @@ def play_level(request, level, from_editor=False):
204
307
  app_settings.NIGHT_MODE_FEATURE_ENABLED
205
308
  ).lower(),
206
309
  "model_solution": model_solution,
207
- "next_level_url": _next_level_url(level, night_mode),
208
- "flip_night_mode_url": _level_url(level, not night_mode),
310
+ "prev_level_url": _prev_level_url(
311
+ level, request.user, night_mode, from_python_den
312
+ ),
313
+ "next_level_url": _next_level_url(
314
+ level, request.user, night_mode, from_python_den
315
+ ),
316
+ "flip_night_mode_url": _level_url(level, not night_mode, from_python_den),
317
+ "available_language_dict": language_code_dict,
209
318
  },
210
319
  )
211
320
 
@@ -213,7 +322,9 @@ def play_level(request, level, from_editor=False):
213
322
  def fetch_workspace_from_last_attempt(attempt):
214
323
  latest_attempt = (
215
324
  Attempt.objects.filter(
216
- level=attempt.level, student=attempt.student, night_mode=attempt.night_mode
325
+ level=attempt.level,
326
+ student=attempt.student,
327
+ night_mode=attempt.night_mode,
217
328
  )
218
329
  .order_by("-start_time")
219
330
  .first()
@@ -323,11 +434,25 @@ def load_workspace(request, workspaceID):
323
434
 
324
435
 
325
436
  def save_workspace(request, workspaceID=None):
437
+ request_params = [
438
+ "name",
439
+ "contents",
440
+ "python_contents",
441
+ "blockly_enabled",
442
+ "python_enabled",
443
+ "python_view_enabled",
444
+ ]
445
+ missing_params = [param for param in request_params if param not in request.POST]
446
+ if missing_params != []:
447
+ raise Exception(
448
+ "Request missing the following required parameters", missing_params
449
+ )
326
450
  name = request.POST.get("name")
327
451
  contents = request.POST.get("contents")
328
452
  python_contents = request.POST.get("python_contents")
329
453
  blockly_enabled = json.loads(request.POST.get("blockly_enabled"))
330
454
  python_enabled = json.loads(request.POST.get("python_enabled"))
455
+ python_view_enabled = json.loads(request.POST.get("python_view_enabled"))
331
456
 
332
457
  workspace = None
333
458
  if workspaceID:
@@ -341,26 +466,28 @@ def save_workspace(request, workspaceID=None):
341
466
  workspace.python_contents = python_contents
342
467
  workspace.blockly_enabled = blockly_enabled
343
468
  workspace.python_enabled = python_enabled
469
+ workspace.python_view_enabled = python_view_enabled
344
470
  workspace.save()
345
471
 
346
472
  return load_list_of_workspaces(request)
347
473
 
348
474
 
349
- def load_workspace_solution(request, levelName):
350
-
351
- if levelName in solutions:
475
+ def load_workspace_solution(request, level_name):
476
+ if level_name in solutions:
352
477
  workspace = Workspace(owner=request.user.userprofile)
353
- workspace.id = levelName
354
- workspace.name = levelName
478
+ workspace.id = level_name
479
+ workspace.name = level_name
355
480
  workspace.contents = solutions["blockly_default"]
356
481
  workspace.python_contents = solutions["python_default"]
357
482
 
358
- if int(levelName) <= 91:
359
- workspace.contents = solutions[levelName]
483
+ level = Level.objects.get(name=level_name, default=True)
484
+
485
+ if level.blockly_enabled:
486
+ workspace.contents = solutions[level_name]
360
487
  workspace.blockly_enabled = True
361
488
  workspace.python_enabled = False
362
489
  else:
363
- workspace.python_contents = solutions[levelName]
490
+ workspace.python_contents = solutions[level_name]
364
491
  workspace.blockly_enabled = False
365
492
  workspace.python_enabled = True
366
493
 
@@ -378,8 +505,19 @@ def load_workspace_solution(request, levelName):
378
505
 
379
506
 
380
507
  def start_episode(request, episodeId):
508
+ if int(episodeId) > 9:
509
+ raise Http404
510
+
511
+ episode = cached_episode(episodeId)
512
+ return play_level(request, episode.first_level)
513
+
514
+
515
+ def start_python_episode(request, episodeId):
516
+ if int(episodeId) < 12:
517
+ raise Http404
518
+
381
519
  episode = cached_episode(episodeId)
382
- return play_level(request, episode.first_level, False)
520
+ return play_level(request, episode.first_level, from_python_den=True)
383
521
 
384
522
 
385
523
  @require_POST
@@ -2,16 +2,14 @@ from __future__ import division
2
2
 
3
3
  import json
4
4
  import re
5
- from builtins import map
6
- from builtins import str
5
+ from builtins import map, str
7
6
 
8
- from common.models import Student, Class, Teacher
7
+ from common.models import Student, Teacher
9
8
  from django.contrib.auth.models import User
10
- from django.urls import reverse
11
9
  from django.db import transaction
12
10
  from django.http import HttpResponse
13
11
  from django.shortcuts import get_object_or_404, render, redirect
14
- from django.utils.safestring import mark_safe
12
+ from django.urls import reverse
15
13
  from django.views.decorators.http import require_POST
16
14
  from portal.templatetags import app_tags
17
15
  from rest_framework.authentication import SessionAuthentication
@@ -20,8 +18,7 @@ from rest_framework.views import APIView
20
18
  import game.level_management as level_management
21
19
  import game.messages as messages
22
20
  import game.permissions as permissions
23
- from game import app_settings
24
- from game import random_road
21
+ from game import app_settings, random_road
25
22
  from game.cache import cached_level_decor, cached_level_blocks
26
23
  from game.character import get_all_character
27
24
  from game.decor import get_all_decor, get_decor_element
@@ -69,9 +66,7 @@ def available_blocks():
69
66
  if app_settings.COW_FEATURE_ENABLED:
70
67
  return Block.objects.all()
71
68
  else:
72
- return Block.objects.all().exclude(
73
- type__in=["declare_event", "puff_up", "sound_horn"]
74
- )
69
+ return Block.objects.all().exclude(type__in=["cow_crossing", "sound_horn"])
75
70
 
76
71
 
77
72
  def play_anonymous_level(request, levelId, from_level_editor=True, random_level=False):
@@ -88,8 +83,9 @@ def play_anonymous_level(request, levelId, from_level_editor=True, random_level=
88
83
  if not level.anonymous:
89
84
  return redirect(reverse("level_editor"), permanent=True)
90
85
 
91
- lesson = mark_safe(messages.description_level_default())
92
- hint = mark_safe(messages.hint_level_default())
86
+ subtitle = level.subtitle
87
+ lesson = level.lesson
88
+ hint = level.hint
93
89
 
94
90
  attempt = None
95
91
  house = get_decor_element("house", level.theme).url
@@ -125,12 +121,13 @@ def play_anonymous_level(request, levelId, from_level_editor=True, random_level=
125
121
  "level": level,
126
122
  "decor": decor_data,
127
123
  "blocks": block_data,
124
+ "subtitle": subtitle,
128
125
  "lesson": lesson,
126
+ "hint": hint,
129
127
  "character": character,
130
128
  "background": background,
131
129
  "house": house,
132
130
  "cfc": cfc,
133
- "hint": hint,
134
131
  "attempt": attempt,
135
132
  "random_level": random_level,
136
133
  "return_url": reverse(return_view_name),
@@ -197,12 +194,12 @@ def load_level_for_editor(request, levelID):
197
194
 
198
195
  level_dict = LevelSerializer(level).data
199
196
  level_dict["theme"] = level.theme.id
200
- level_dict["decor"] = cached_level_decor(level)
197
+ level_dict["decor"] = level_management.get_decor(level)
201
198
  level_dict["blocks"] = cached_level_blocks(level)
202
199
 
203
200
  response = {"owned": level.owner == request.user.userprofile, "level": level_dict}
204
201
 
205
- return HttpResponse(json.dumps(response), content_type="application/javascript")
202
+ return HttpResponse(json.dumps(response), content_type="application/json")
206
203
 
207
204
 
208
205
  @transaction.atomic
@@ -210,6 +207,8 @@ def load_level_for_editor(request, levelID):
210
207
  def save_level_for_editor(request, levelId=None):
211
208
  """Processes a request on creation of the map in the level editor"""
212
209
  data = json.loads(request.POST["data"])
210
+ data["disable_algorithm_score"] = True
211
+
213
212
  if ("character" not in data) or (not data["character"]):
214
213
  # Set a default, to deal with issue #1158 "Cannot save custom level"
215
214
  data["character"] = 1
@@ -222,19 +221,74 @@ def save_level_for_editor(request, levelId=None):
222
221
  if not permissions.can_save_level(request.user, level):
223
222
  return HttpResponseUnauthorized()
224
223
 
225
- pattern = re.compile("^(\w?[ ]?)*$")
226
-
227
- if pattern.match(data["name"]):
228
- level_management.save_level(level, data)
229
- # Add the teacher automatically if it is a new level and the student is not
230
- # independent
231
- if (
232
- (levelId is None)
233
- and hasattr(level.owner, "student")
234
- and not level.owner.student.is_independent()
235
- ):
236
- level.shared_with.add(level.owner.student.class_field.teacher.user.user)
237
- level.save()
224
+ name_pattern = re.compile("^(\w?[ ]?)*$")
225
+ fields_pattern = re.compile("^[\w.?!', ]*$")
226
+
227
+ name_is_safe = name_pattern.match(data["name"])
228
+ fields_are_safe = all(
229
+ [
230
+ field not in data or fields_pattern.match(data[field])
231
+ for field in ["subtitle", "lesson", "hint"]
232
+ ]
233
+ )
234
+
235
+ if not (name_is_safe and fields_are_safe):
236
+ return HttpResponseUnauthorized()
237
+
238
+ level_management.save_level(level, data)
239
+
240
+ is_user_school_student = (
241
+ hasattr(level.owner, "student") and not level.owner.student.is_independent()
242
+ )
243
+ is_user_independent = (
244
+ hasattr(level.owner, "student") and level.owner.student.is_independent()
245
+ )
246
+ is_user_teacher = hasattr(level.owner, "teacher")
247
+
248
+ # when level is created
249
+ if levelId is None:
250
+ teacher = None
251
+
252
+ # if level owner is a school student, share with teacher automatically if they aren't an admin
253
+ if is_user_school_student:
254
+ teacher = level.owner.student.class_field.teacher
255
+ if not teacher.is_admin:
256
+ level.shared_with.add(teacher.new_user)
257
+
258
+ if not data["anonymous"]:
259
+ level_management.email_new_custom_level(
260
+ teacher.new_user.email,
261
+ request.build_absolute_uri(reverse("level_moderation")),
262
+ request.build_absolute_uri(
263
+ reverse("play_custom_level", kwargs={"levelId": level.id})
264
+ ),
265
+ str(level.owner.student),
266
+ level.owner.student.class_field.name,
267
+ )
268
+
269
+ elif is_user_teacher:
270
+ teacher = level.owner.teacher
271
+
272
+ # if level owner is a teacher or an indy user, approval isn't needed
273
+ if not is_user_school_student:
274
+ level.needs_approval = False
275
+
276
+ # share with all admins of the school if user is in a school
277
+ if not is_user_independent:
278
+ if not teacher.school is None:
279
+ school_admins = teacher.school.admins()
280
+
281
+ [
282
+ level.shared_with.add(school_admin.new_user)
283
+ for school_admin in school_admins
284
+ if school_admin.new_user != request.user
285
+ ]
286
+
287
+ # anytime a student edits their level
288
+ if is_user_school_student:
289
+ if not level.needs_approval:
290
+ level.needs_approval = True
291
+
238
292
  if not data["anonymous"]:
239
293
  level_management.email_new_custom_level(
240
294
  level.owner.student.class_field.teacher.new_user.email,
@@ -242,14 +296,13 @@ def save_level_for_editor(request, levelId=None):
242
296
  request.build_absolute_uri(
243
297
  reverse("play_custom_level", kwargs={"levelId": level.id})
244
298
  ),
245
- request.build_absolute_uri(reverse("home")),
246
299
  str(level.owner.student),
247
300
  level.owner.student.class_field.name,
248
301
  )
249
- response = {"id": level.id}
250
- return HttpResponse(json.dumps(response), content_type="application/javascript")
251
- else:
252
- return HttpResponseUnauthorized()
302
+
303
+ level.save()
304
+ response = {"id": level.id}
305
+ return HttpResponse(json.dumps(response), content_type="application/json")
253
306
 
254
307
 
255
308
  @transaction.atomic
@@ -286,7 +339,7 @@ def generate_random_map_for_editor(request):
286
339
 
287
340
 
288
341
  class SharingInformationForEditor(APIView):
289
- """Returns a information about who the level can be and is shared with. This uses
342
+ """Returns information about who the level can be and is shared with. This uses
290
343
  the CanShareLevel permission."""
291
344
 
292
345
  authentication_classes = (SessionAuthentication,)
@@ -320,10 +373,10 @@ class SharingInformationForEditor(APIView):
320
373
  ).exclude(id=student.id)
321
374
  valid_recipients["classmates"] = [
322
375
  {
323
- "id": classmate.user.user.id,
324
- "name": app_tags.make_into_username(classmate.user.user),
376
+ "id": classmate.new_user.id,
377
+ "name": app_tags.make_into_username(classmate.new_user),
325
378
  "shared": level.owner == classmate.user
326
- or level.shared_with.filter(id=classmate.user.user.id).exists(),
379
+ or level.shared_with.filter(id=classmate.new_user.id).exists(),
327
380
  }
328
381
  for classmate in classmates
329
382
  ]
@@ -331,10 +384,10 @@ class SharingInformationForEditor(APIView):
331
384
  # Then add their teacher as well
332
385
  teacher = class_.teacher
333
386
  valid_recipients["teacher"] = {
334
- "id": teacher.user.user.id,
335
- "name": app_tags.make_into_username(teacher.user.user),
387
+ "id": teacher.new_user.id,
388
+ "name": app_tags.make_into_username(teacher.new_user),
336
389
  "shared": level.owner == teacher.user
337
- or level.shared_with.filter(id=teacher.user.user.id).exists(),
390
+ or level.shared_with.filter(id=teacher.new_user.id).exists(),
338
391
  }
339
392
 
340
393
  elif hasattr(userprofile, "teacher"):
@@ -342,22 +395,29 @@ class SharingInformationForEditor(APIView):
342
395
 
343
396
  # First get all the students they teach
344
397
  valid_recipients["classes"] = []
345
- classes_taught = Class.objects.filter(teacher=teacher)
398
+ if teacher.is_admin:
399
+ classes_taught = teacher.school.classes()
400
+ else:
401
+ classes_taught = teacher.class_teacher.all()
346
402
  for class_ in classes_taught:
347
403
  students = Student.objects.filter(
348
404
  class_field=class_, new_user__is_active=True
349
405
  )
350
406
  valid_recipients["classes"].append(
351
407
  {
352
- "name": class_.name,
408
+ "name": (
409
+ f"{class_.name} ({app_tags.make_into_username(class_.teacher.new_user)})"
410
+ if teacher.is_admin
411
+ else class_.name
412
+ ),
353
413
  "id": class_.id,
354
414
  "students": [
355
415
  {
356
- "id": student.user.user.id,
357
- "name": app_tags.make_into_username(student.user.user),
416
+ "id": student.new_user.id,
417
+ "name": app_tags.make_into_username(student.new_user),
358
418
  "shared": level.owner == student.user
359
419
  or level.shared_with.filter(
360
- id=student.user.user.id
420
+ id=student.new_user.id
361
421
  ).exists(),
362
422
  }
363
423
  for student in students
@@ -371,11 +431,12 @@ class SharingInformationForEditor(APIView):
371
431
  fellow_teachers = Teacher.objects.filter(school=teacher.school)
372
432
  valid_recipients["teachers"] = [
373
433
  {
374
- "id": fellow_teacher.user.user.id,
375
- "name": app_tags.make_into_username(fellow_teacher.user.user),
434
+ "id": fellow_teacher.new_user.id,
435
+ "name": app_tags.make_into_username(fellow_teacher.new_user),
436
+ "admin": fellow_teacher.is_admin,
376
437
  "shared": level.owner == fellow_teacher.user
377
438
  or level.shared_with.filter(
378
- id=fellow_teacher.user.user.id
439
+ id=fellow_teacher.new_user.id
379
440
  ).exists(),
380
441
  }
381
442
  for fellow_teacher in fellow_teachers