diff --git a/workbench/static/workbench/js/runtime/1.js b/workbench/static/workbench/js/runtime/1.js index 9ce83d7a..7a526718 100644 --- a/workbench/static/workbench/js/runtime/1.js +++ b/workbench/static/workbench/js/runtime/1.js @@ -40,6 +40,9 @@ var RuntimeProvider = (function() { return child } } + }, + notify: function(name, data) { + console.log(`Runtime event ${JSON.stringify(name)} with data:`, data) } } }; diff --git a/workbench/templates/workbench/block.html b/workbench/templates/workbench/block.html index dcb328bf..bcaf1c1a 100644 --- a/workbench/templates/workbench/block.html +++ b/workbench/templates/workbench/block.html @@ -30,6 +30,22 @@

XBlock: {{scenario.description}}

+
+ View +
+ Current view: {{ view_name }} +
+ {% if other_views %} +
+ Other views: + +
+ {% endif %} +
Database
diff --git a/workbench/test/test_scenarios.py b/workbench/test/test_scenarios.py index 0bcdb96f..f6a52daf 100644 --- a/workbench/test/test_scenarios.py +++ b/workbench/test/test_scenarios.py @@ -62,6 +62,7 @@ def test_scenario(self): it forces database access, which pytest_django doesn't like. """ scenario_ids = list(scenarios.get_scenarios().keys()) + views_seen = set() for scenario_id in scenario_ids: url = reverse('workbench_show_scenario', kwargs={'scenario_id': scenario_id}) @@ -77,3 +78,9 @@ def test_scenario(self): for vertical_tag in html.xpath('//div[@class="vertical"]'): # No vertical tag should be empty. assert list(vertical_tag), f"Scenario {scenario_id}: Empty shouldn't happen!" + # Default view for scenario should be the student view. + indicator = html.xpath('//div[@class="current-view-indicator"]')[0] + assert "Current view: student_view" in indicator.text + views_seen |= set(el.text.strip() for el in html.xpath('//a[@class="block-view-link"]')) + # At least one of our test blocks should have the studio view available, or else we need to add one which does. + assert "studio_view" in views_seen diff --git a/workbench/views.py b/workbench/views.py index 57c99073..15292fa3 100644 --- a/workbench/views.py +++ b/workbench/views.py @@ -26,6 +26,7 @@ log = logging.getLogger(__name__) +KNOWN_BLOCK_VIEWS = ['student_view', 'author_view', 'studio_view'] # We don't really have authentication and multiple students, just accept their # id on the URL. @@ -35,6 +36,24 @@ def get_student_id(request): return student_id +def can_render_view(block, view_name, root=False) -> bool: + """ + Verifies that a view can be rendered by a block. + """ + if (children := getattr(block, 'children', [])) and not root: + child_blocks = (block.runtime.get_block(child_id) for child_id in children) + return ( + can_render_view(block, view_name, root=True) and + all(can_render_view(child, view_name) for child in child_blocks) + ) + return getattr(block, view_name, getattr(block, "fallback_view", None)) is not None + + +def get_block_views(block: XBlock) -> set[str]: + """Get the available views for a block.""" + return {view_name for view_name in KNOWN_BLOCK_VIEWS if can_render_view(block, view_name)} + + # ---- Views ----- def index(_request): @@ -74,15 +93,20 @@ def show_scenario(request, scenario_id, view_name='student_view', template='work 'activate_block_id': request.GET.get('activate_block_id', None) } + other_views = sorted(get_block_views(block) - {view_name}) + frag = block.render(view_name, render_context) log.info("End show_scenario %s", scenario_id) return render(request, template, { + 'scenario_id': scenario_id, 'scenario': scenario, 'block': block, 'body': frag.body_html(), 'head_html': frag.head_html(), 'foot_html': frag.foot_html(), 'student_id': student_id, + 'view_name': view_name, + 'other_views': other_views, })