Add existing sidebar information to the course outline page - #14828
Conversation
3d2f9f8 to
f98c34e
Compare
54b8cb3 to
b08e6b9
Compare
6bba802 to
c72a613
Compare
andy-armstrong
left a comment
There was a problem hiding this comment.
@dianakhuang @bjacobel this is looking great. I would like us to clean up the terminology in both the back end and front end, and then I think this looks good to go from my perspective.
@bjacobel A very minor UX nit is that the Resume button goes too low when resized to a very narrow width:
| """ | ||
| # create ordered list of transformers, adding BlocksAPITransformer at end. | ||
| transformers = BlockStructureTransformers() | ||
| can_view_special_exam = False |
There was a problem hiding this comment.
Nit: this isn't an access control check, so can_view seems confusing. How about include_special_exam?
|
|
||
| class MilestonesTransformer(BlockStructureTransformer): | ||
| """ | ||
| Excludes all special exams (timed, proctored, practice proctored) from the student view. |
There was a problem hiding this comment.
Is this docstring still correct?
| unicode(block_key.course_key), | ||
| unicode(block_key) | ||
| ) | ||
| except ProctoredExamNotFoundException as ex: |
There was a problem hiding this comment.
It seems odd to only handle proctored exam failures. Does this class apply to all special exams (and hence needs renaming), or is there a more generic class you could use here?
There was a problem hiding this comment.
This does apply to all special exams, but it is defined and used in the edx-proctoring repo, and I don't believe this story should warrant an edx-proctoring release.
| if self.is_special_exam(block_key, block_structure): | ||
|
|
||
| # | ||
| # call into edx_proctoring subsystem |
There was a problem hiding this comment.
Nit: I'd prefer that this comment not refer to proctoring since this is intended to apply to all special exams. It is a strange implementation detail that it is implemented in edx-proctoring that I hope one day will be cleaned up.
There was a problem hiding this comment.
I left this in place just because it's referring to edx_proctoring the package/repo.
| log.exception(ex) | ||
|
|
||
| if timed_exam_attempt_context: | ||
| # yes, user has proctoring context about |
There was a problem hiding this comment.
Nit: again I would generalize this to special exams and not call out proctoring specifically.
| self.setup_gated_section(self.blocks[gated_block_ref], self.blocks[gating_block_ref]) | ||
|
|
||
| with self.assertNumQueries(6): | ||
| with self.assertNumQueries(8): |
There was a problem hiding this comment.
How do we feel about having added two queries to this? Is there any way to make these changes without introducing extra overhead? @nasthagiri can you advise us on this?
There was a problem hiding this comment.
@dianakhuang do you know what these two additional queries are?
There was a problem hiding this comment.
I believe they're the required content and user_can_skip_entrance_exam queries.
| self.setup_gated_section(self.blocks['H'], self.blocks['A']) | ||
| self.get_blocks_and_check_against_expected(self.staff, expected_blocks) | ||
|
|
||
| def test_can_view_special(self): |
There was a problem hiding this comment.
Nit: I'd rename this to test_special_exams.
| ## There are behavior differences between rendering of subsections which have | ||
| ## special_exam/timed examinations and those that do not. | ||
| ## | ||
| ## Proctoring exposes a exam status message field as well as a status icon |
| <span class="subsection-title">${ subsection['display_name'] }</span> | ||
|
|
||
| <div class="details"> | ||
| ## There are behavior differences between rendering of subsections which have |
There was a problem hiding this comment.
It would be good to not mention proctoring in the comments in this file, because there are no special proctoring-related features. I would prefer that this only use one term, but I'm not sure if that is special exams or timed exams. The latter seems better to me, but I don't think I understand the distinction. Maybe it is possible to just call these exams, or is there different behavior for 'non-special' exams?
A lot of this confusion is also in the block API response, so it would be good for us to all get on the same page as to what we're handling here.
| # 'student_view_multi_device' | ||
| SupportedFieldType(StudentViewTransformer.STUDENT_VIEW_MULTI_DEVICE, StudentViewTransformer), | ||
|
|
||
| SupportedFieldType('special_exam', MilestonesTransformer), |
There was a problem hiding this comment.
Can we just call this exam_info and have all exam information in one place? The distinction between regular exams, special exams, timed exams, and proctored exams is not obvious to me (and I imagine to others). It would be good to keep this API as simple as possible so that clients don't have to understand these distinctions either. I understand if this isn't possible due to the complicated way special exams have been implemented.
If we do want to keep this only to special exams, I'd still rename it to special_exam_info.
|
@andy-armstrong I'll fix the alignment issue in the branch for minor UI fixes, I'm working on it over there. |
|
jenkins run a11y |
robrap
left a comment
There was a problem hiding this comment.
Just adding a couple of thoughts. I like @andy-armstrong's questions about the different types of exams. Anything we can do to understand the differences, and document or clean up code to clarify those differences would be great.
Also, is the plan to get Nimisha to review this after team review is complete? (At least the Transformer part?)
| """ | ||
| if self.is_special_exam(block_key, block_structure): | ||
|
|
||
| # |
There was a problem hiding this comment.
Nit: formatting on this multi-line comment seems odd. The blank lines above and below seem unnecessary, and the line length seems shorter than necessary.
| log.exception(ex) | ||
|
|
||
| if timed_exam_attempt_context: | ||
| # yes, user has proctoring context about |
There was a problem hiding this comment.
Nit: Again, line length on this comment seems shorter than necessary.
| return [block_structure.create_universal_filter()] | ||
| root_key = block_structure.root_block_usage_key | ||
| course_key = root_key.course_key | ||
| user_can_skip = EntranceExamConfiguration.user_can_skip_entrance_exam(usage_info.user, course_key) |
There was a problem hiding this comment.
@andy-armstrong I'm wondering 2 related things:
- As I think @dianakhuang had intended, should some of this be in a separate EntranceExamTransformer?
- If the transformer could be supplied from elsewhere, would that solve your issue of it not living in the api?
I'm not sure how much work it would be to do Fix js that prevented single step OE problems (ie only self assessment) ... #2, but possible doing Put back in the blocks for Advanced Settings. #1 would better enable that?
|
@dianakhuang: The sandbox has various issues, and it is unclear whether or not that they are relevant to our work.
I recommend you configure certain users with the new view and certain with the old view, so we can compare against the old courseware and left-hand navigation. FYI: @marcotuts |
|
@robrap I just reconfigured the sandbox to have the feature on for staff@ but off for non-staff users. |
|
Thanks @bjacobel. Seems the same issues are happening in the old view. I'm hoping/assuming we have not broken the old view, but it would be nice to have a valid enough course to test, including the proper gating. This would include some gating and an entrance exam. |
|
jenkins run js |
|
jenkins run bokchoy |
|
@andy-armstrong i believe i've addressed all your comments. |
There was a problem hiding this comment.
Should this be special_exam_info?
There was a problem hiding this comment.
Nit: I was thinking of this as a separate top-level method so that it reduces the complexity of the transform method. It also makes it easier to move the method to a different file.
andy-armstrong
left a comment
There was a problem hiding this comment.
👍 two nits from me, so address them or not as you see fit.
@nasthagiri I think this is so close to landing that we won't ask you for a formal review. If you are able to look at it and see issues, we can address them in a follow-on PR if this has already merged. Thanks!
|
@robrap i would definitely love a review once you get a chance to do so. |
54985eb to
76d68c1
Compare
|
jenkins run js |
| if user_gated_from_block(block_key): | ||
| block_structure.remove_block(block_key, False) | ||
| else: | ||
| self.add_special_exam_info(block_key, block_structure, usage_info) |
There was a problem hiding this comment.
@dianakhuang @nasthagiri: My very imperfect understanding was that the collect phase added data and the transform phase mostly pruned. However, it is called "transform" and not "prune". Can one of you help me understand what should happen where, and why and if add_special_exam_info makes sense during transform? I admit I did not dig into code, I am just going by names and previous understanding.
: Is this the right place to add this info based on your intentions of collect vs transform?
There was a problem hiding this comment.
Talked to @robrap in person, but I think we are in agreement that while currently, the transform phase only prunes right now, it is set up such that this is a reasonable use case because the special_exam_info field contains user-specific data that is pulled from the RDS.
There was a problem hiding this comment.
The transform phase is not meant for only pruning. If you see the implementations of the transform methods in the course_api/blocks/transformers directory, you'll find that many of them don't prune, but instead add necessary data for the requester.
| @@ -35,22 +45,39 @@ def collect(cls, block_structure): | |||
| block_structure.request_xblock_fields('is_proctored_enabled') | |||
There was a problem hiding this comment.
@nasthagiri: @dianakhuang and I agree that it would seem cleaner to have special exams (which aren't milestones), to be in its own transformer. @dianakhuang is under the impression that you don't think that is a good idea. Could you clarify why? Thank you!
811de72 to
fbb08d4
Compare
| # create ordered list of transformers, adding BlocksAPITransformer at end. | ||
| transformers = BlockStructureTransformers() | ||
| include_special_exams = False | ||
| if requested_fields is not None and 'special_exam_info' in requested_fields: |
There was a problem hiding this comment.
Nit: The "info" part is redundant; the name can be simplified to just "special_exams".
| block_structure.request_xblock_fields('is_proctored_enabled') | ||
| block_structure.request_xblock_fields('is_practice_exam') | ||
| block_structure.request_xblock_fields('is_timed_exam') | ||
| block_structure.request_xblock_fields('entrance_exam_id') |
There was a problem hiding this comment.
Since you are adding a collected field here, we'll need to coordinate the release of this transformer and update its version number accordingly. Please see the docstring at https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/content/block_structure/transformer.py#L17.
| return False | ||
| elif self.has_pending_milestones_for_user(block_key, usage_info): | ||
| return True | ||
| elif self.gated_by_required_content(block_key, block_structure, user_can_skip, required_content): |
There was a problem hiding this comment.
Why is this call needed? Isn't this already covered by has_pending_milestones_for_user?
There was a problem hiding this comment.
Unfortunately, no. I'm not sure exactly where the behavior is not not overlapping, but tests started to fail once one of these was used but not the other.
| if user_gated_from_block(block_key): | ||
| block_structure.remove_block(block_key, False) | ||
| else: | ||
| self.add_special_exam_info(block_key, block_structure, usage_info) |
There was a problem hiding this comment.
The transform phase is not meant for only pruning. If you see the implementations of the transform methods in the course_api/blocks/transformers directory, you'll find that many of them don't prune, but instead add necessary data for the requester.
| except ProctoredExamNotFoundException as ex: | ||
| log.exception(ex) | ||
|
|
||
| if special_exam_attempt_context: |
There was a problem hiding this comment.
Nit: For readability and simpler code, put this in an else clause of the try-except. And you can also eliminate the special_exam_attempt_context boolean,
There was a problem hiding this comment.
The special_exam_attempt_context isn't a boolean. It's a dictionary with the special exam data contained within it. If there isn't any for this user for this exam, I believe it returns an empty dictionary, so this additional check is necessary.
| return False | ||
| elif self.has_pending_milestones_for_user(block_key, usage_info): | ||
| return True | ||
| elif self.gated_by_required_content(block_key, block_structure, user_can_skip, required_content): |
There was a problem hiding this comment.
@dianakhuang This part around entrance exams is new and seems to affect the api overall, so will it affect mobile as well? What would have happened, what is happening, and is this change ok without another special parameter?
There was a problem hiding this comment.
As discussed in person, this is probably a miss on the original mobile for entrance exams. So, hopefully it is a good thing to have this in both cases.
| else: | ||
| data_string = _("{subsection_format} due {{date}}").format(subsection_format=subsection.get('format')) | ||
| %> | ||
| % if subsection.get('format') or 'special_exam_info' in subsection: |
There was a problem hiding this comment.
@bjacobel Do we need an empty <div class="details"> if there are no details, or is it ok to use this if to gate the entire div?
c5beaea to
5959e1c
Compare
Includes refactor of course home/course outline smoke test to its own file.
Add comment explaining why we can't have a separate transformer.
8e6c118 to
196d379
Compare
|
EdX Release Notice: This PR has been deployed to the staging environment in preparation for a release to production on Monday, April 17, 2017. |
|
EdX Release Notice: This PR has been deployed to the production environment. |

dianakhuang.sandbox.edx.org - outline is enabled for all users