Skip to content

Commit

Permalink
🪲 fix quiz/puzzles tabs not showed to students (#5276)
Browse files Browse the repository at this point in the history
**Problem**
Related to #5156

![image](https://github.com/hedyorg/hedy/assets/20051470/ff41ff88-14e7-456f-838e-553b00cc12a2)

The feature actually works. However, it only works if the teacher has logged in and at least visited the customize-class page. This is because there's a migration step that I added which basically adds the quizzes/puzzles to the list of adventures if they were not hidden before. The problem that this PR solves is related to the scenario in which a student is logged in and interacts with a class while their teacher has not refreshed the customize-class page: Thus the migration was not done yet at this point, hence the students then would not see the quizzes/puzzles.

_Why didn't we figure it out earlier not during testing nor reviewing?_ This is mainly because **we tested it while relying on** the _previewing class_ feature. This feature, supposidely, would allow a teacher to exactly simulate what students see and how they interact with the related class. But apparently that's not the case, since the preview feature is simply coplying the customization class and "allowing the user to merely see that class". 

**How to test?** 
- in your `dev_database.json` file, remove the quiz/puzzle manually from at least the first level of the `sorted_adventures` of your class (e.g., CLASS1)
- then log in as a student and the quiz/puzzle should be readded and shown to you, iff. the quiz/puzzle was not disabled
  • Loading branch information
hasan-sh authored Mar 19, 2024
1 parent 5f191df commit ad52b2b
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 39 deletions.
31 changes: 18 additions & 13 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1417,6 +1417,8 @@ def hour_of_code(level, program_id=None):


# routing to index.html


@app.route('/ontrack', methods=['GET'], defaults={'level': '1', 'program_id': None})
@app.route('/onlinemasters', methods=['GET'], defaults={'level': '1', 'program_id': None})
@app.route('/onlinemasters/<int:level>', methods=['GET'], defaults={'program_id': None})
Expand Down Expand Up @@ -1469,15 +1471,6 @@ def index(level, program_id):
# At this point we can have the following scenario:
# - The level is allowed and available
# - But, if there is a quiz threshold we have to check again if the user has reached it

parsons_in_level = True
quiz_in_level = True
if customizations.get("sorted_adventures") and len(customizations["sorted_adventures"]) > 2:
parsons_in_level = [adv for adv in customizations["sorted_adventures"][str(level)][-2:]
if adv.get("name") == "parsons"]
quiz_in_level = [adv for adv in customizations["sorted_adventures"][str(level)][-2:]
if adv.get("name") == "quiz"]

if 'level_thresholds' in customizations:
# If quiz in level and in some of the previous levels, then we check the threshold level.
check_threshold = 'other_settings' in customizations and 'hide_quiz' not in customizations['other_settings']
Expand Down Expand Up @@ -1592,11 +1585,23 @@ def index(level, program_id):
if parsons:
parson_exercises = len(PARSONS[g.lang].get_parsons_data_for_level(level))

if not parsons_in_level or 'other_settings' in customizations and \
'hide_parsons' in customizations['other_settings']:
parsons_hidden = 'other_settings' in customizations and 'hide_parsons' in customizations['other_settings']
quizzes_hidden = 'other_settings' in customizations and 'hide_quiz' in customizations['other_settings']

if customizations:
for_teachers.ForTeachersModule.migrate_quizzes_parsons_tabs(customizations, parsons_hidden, quizzes_hidden)

parsons_in_level = True
quiz_in_level = True
if customizations.get("sorted_adventures") and\
len(customizations.get("sorted_adventures", {str(level): []})[str(level)]) > 2:
last_two_adv_names = [adv["name"] for adv in customizations["sorted_adventures"][str(level)][-2:]]
parsons_in_level = "parsons" in last_two_adv_names
quiz_in_level = "quiz" in last_two_adv_names

if not parsons_in_level or parsons_hidden:
parsons = False
if not quiz_in_level or 'other_settings' in customizations and \
'hide_quiz' in customizations['other_settings']:
if not quiz_in_level or quizzes_hidden:
quiz = False

max_level = hedy.HEDY_MAX_LEVEL
Expand Down
60 changes: 34 additions & 26 deletions website/for_teachers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import hedyweb
import utils
from safe_format import safe_format
from datetime import date

from website.server_types import SortedAdventure
from website.flask_helpers import render_template
from website.auth import (
Expand All @@ -26,7 +28,6 @@
from .achievements import Achievements
from .database import Database
from .website_module import WebsiteModule, route
from datetime import date

SLIDES = collections.defaultdict(hedy_content.NoSuchSlides)
for lang in hedy_content.ALL_LANGUAGES.keys():
Expand Down Expand Up @@ -541,6 +542,33 @@ def sort_adventures_in_class(self, user):
available_adventures=available_adventures,
class_id=session['class_id'])

@staticmethod
def migrate_quizzes_parsons_tabs(customizations, parsons_hidden, quizzes_hidden):
"""If the puzzles/quizzes were not migrated yet which is possible if the teacher didn't tweak
the class customizations, if this is the case, we need to add them if possible."""
migrated = customizations.get("quiz_parsons_tabs_migrated")
if not migrated and customizations.get("sorted_adventures"):
for level, sorted_adventures in customizations["sorted_adventures"].items():
last_two_adv_names = [adv["name"] for adv in sorted_adventures[-2:]]
parson_in_level = "parsons" in last_two_adv_names
quiz_in_level = "quiz" in last_two_adv_names
# In some levels, we don't need quiz/parsons
level_accepts_parsons = "parsons" in hedy_content.ADVENTURE_ORDER_PER_LEVEL[int(level)]
level_accepts_quiz = "quiz" in hedy_content.ADVENTURE_ORDER_PER_LEVEL[int(level)]
if not parson_in_level and not parsons_hidden and level_accepts_parsons:
sorted_adventures.append(
{"name": "parsons", "from_teacher": False})

if not quiz_in_level and not quizzes_hidden and level_accepts_quiz:
sorted_adventures.append(
{"name": "quiz", "from_teacher": False})
# Need to reorder, for instance, in case parsons was hidden and the other was not.
ForTeachersModule.reorder_adventures(sorted_adventures)

# Mark current customization as being migrated so that we don't do this step next time.
customizations["quiz_parsons_tabs_migrated"] = 1
Database.update_class_customizations(Database, customizations)

def get_class_info(self, user, class_id, get_customizations=False):
if hedy_content.Adventures(g.lang).has_adventures():
default_adventures = hedy_content.Adventures(g.lang).get_adventure_keyname_name_levels()
Expand Down Expand Up @@ -572,11 +600,11 @@ def get_class_info(self, user, class_id, get_customizations=False):
default_adventures["parsons"] = {}

parsons_hidden = False
quizes_hidden = False
quizzes_hidden = False
if customizations:
parsons_hidden = 'other_settings' in customizations and 'hide_parsons' in customizations['other_settings']
quizes_hidden = 'other_settings' in customizations and 'hide_quiz' in customizations['other_settings']
if quizes_hidden:
quizzes_hidden = 'other_settings' in customizations and 'hide_quiz' in customizations['other_settings']
if quizzes_hidden:
del default_adventures["quiz"]
if parsons_hidden:
del default_adventures["parsons"]
Expand Down Expand Up @@ -620,28 +648,8 @@ def get_class_info(self, user, class_id, get_customizations=False):
}
self.db.update_class_customizations(customizations)

migrated = customizations.get("quiz_parsons_tabs_migrated")
if not migrated:
for level, sorted_adventures in customizations['sorted_adventures'].items():
last_two_adv_names = [adv["name"] for adv in sorted_adventures[-2:]]
parson_in_level = "parsons" in last_two_adv_names
quiz_in_level = "quiz" in last_two_adv_names
# In some levels, we don't need quiz/parsons
level_accepts_parsons = "parsons" in hedy_content.ADVENTURE_ORDER_PER_LEVEL[int(level)]
level_accepts_quiz = "quiz" in hedy_content.ADVENTURE_ORDER_PER_LEVEL[int(level)]
if not parsons_hidden and not parson_in_level and level_accepts_parsons and get_customizations:
sorted_adventures.append(
{"name": "parsons", "from_teacher": False})

if not quizes_hidden and not quiz_in_level and level_accepts_quiz and get_customizations:
sorted_adventures.append(
{"name": "quiz", "from_teacher": False})
# Need to reorder, for instance, in case parsons was hidden and the other was not.
self.reorder_adventures(sorted_adventures)

# Mark current customization as being migrated so that we don't do this step next time.
customizations["quiz_parsons_tabs_migrated"] = True
self.db.update_class_customizations(customizations)
if get_customizations:
self.migrate_quizzes_parsons_tabs(customizations, parsons_hidden, quizzes_hidden)

for level, sorted_adventures in customizations['sorted_adventures'].items():
for adventure in sorted_adventures:
Expand Down

0 comments on commit ad52b2b

Please sign in to comment.