-
Notifications
You must be signed in to change notification settings - Fork 60
Refactor MentoringBlock to use StudioContainerWithNestedXBlocksMixin #121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
mtyaka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking pretty good! I tested it and it looks and works great in the Studio, but it seems that we are missing buttons to add "Message"-type children.
You will also need to rebase and resolve a couple of conflicts.
problem_builder/mentoring.py
Outdated
| fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', { | ||
| "url_name": self.url_name | ||
| })) | ||
| local_context = dict(context) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I prefer explicit context.copy() over dict(context) (although I see you copied this from step.py, so if you decide to change it here, it would make sense to change it in step.py, too).
| self.render_children(context, fragment, can_reorder=True, can_add=False) | ||
| fragment.add_content(u'</div>') | ||
| fragment.add_content(loader.render_template('templates/html/mentoring_add_buttons.html', {})) | ||
| fragment.add_content(loader.render_template('templates/html/mentoring_url_name.html', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
problem_builder/mentoring.py
Outdated
| fragment = Fragment(u'<div class="mentoring">') # This DIV is needed for CSS to apply to the previews | ||
| self.render_children(context, fragment, can_reorder=True, can_add=False) | ||
| fragment.add_content(u'</div>') | ||
| fragment.add_content(loader.render_template('templates/html/mentoring_add_buttons.html', {})) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should remove the mentoring_add_buttons.html template if we're not using it anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that were' using StudioContainerWithNestedXBlocksMixin, do we still need the code in this file: https://github.com/open-craft/problem-builder/blob/4ffa21c36b534f9b86f50b128815800a33b69899/problem_builder/public/js/mentoring_edit.js or is that handled by the mixin now? It might be worth checking and remove that file if it's not needed anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we're still using it in the questionnaire block; I haven't taken a long look to determine what it's being used for, though.
| return [ | ||
| NestedXBlockSpec(AnswerBlock, boilerplate='studio_default'), | ||
| MCQBlock, RatingBlock, MRQBlock, | ||
| NestedXBlockSpec(None, category="html", label=self._("HTML")), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there are three types of children missing from this list: "Message (Complete)", "Message (Incomplete)", and "Message (Max # Attempts)" (see screenshot). It's not visible in the screenshot, but I think there is also "Message (Assessment Review)" when using the deprecated assessment mode.
| # of questionnaire.[css/js] into the DOM. So we use the mentoring block here if possible | ||
| block_with_resources = self.get_parent() | ||
| if not isinstance(block_with_resources, MentoringBlock): | ||
| block_with_resources = self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We normally work around circular dependencies by deferring imports until they are needed, for example:
problem-builder/problem_builder/mentoring.py
Line 418 in 4ffa21c
| from .questionnaire import QuestionnaireAbstractBlock # Import here to avoid circular dependency |
Would that work here? If it works, it would be great if we could apply the same optimization to Step Builder.
9f83f22 to
d1fae47
Compare
|
@mtyaka, I'm updating the sandbox now - care to take another look? |
mtyaka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found some issues related to the MentoringMessageBlock component.
Besides the issues outlined in inline comments, I also noticed that when you add a message type block, it renders the student view initially, and only renders the author view if you refresh the page in the Studio.
It looks like in order to get it to render author_view immediately, you have to set the has_author_view flag to True.
| # If we use local_resource_url(self, ...) the runtime may insert many identical copies | ||
| # of questionnaire.[css/js] into the DOM. So we use the mentoring block here if possible | ||
| block_with_resources = self.get_parent() | ||
| from .mentoring import MentoringBlock |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can you add a comment to explain we're importing the class here to work around a circular dependency?
problem_builder/message.py
Outdated
|
|
||
|
|
||
| def get_message_label(type): | ||
| return MESSAGE_TYPES.get(type, 'Message') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to default to 'Message' here? I think I would prefer to let it fail if the label is missing, since we expect the label to always be present.
problem_builder/message.py
Outdated
| 'incomplete': _(u'Message (Incomplete)'), | ||
| 'max_attempts_reached': _(u'Message (Max # Attempts)'), | ||
| 'on-assessment-review': _(u'Message (Assessment Review)'), | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there's already a MESSAGE_TYPES dict with specific message type data defined inside the MentoringMessageBlock class, would it make sense to move these studio labels into that dict, to keep message type data in a single place?
In the get_message_label function, you would then do something like:
return MentoringMessageBlock.MESSAGE_TYPES[type]['studio_label']
problem_builder/mentoring.py
Outdated
| fragment.add_css_url(self.runtime.local_resource_url(self, 'public/css/problem-builder-tinymce-content.css')) | ||
| fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/util.js')) | ||
| fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/mentoring_edit.js')) | ||
| fragment.initialize_js('MentoringEditComponents') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MentoringEditComponents function is defined inside mentoring_edit.js, which we removed, so we should also remove this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not catching this before, but we have to import and initialize the container_edit.js file to properly utilize nested xblock JS helpers from the xblock-utils repository, just like we do for the MentoringWithExplicitStepsBlock.
problem_builder/mentoring.py
Outdated
| 'completed', | ||
| 'incomplete', | ||
| 'max_attempts_reached', | ||
| 'on-assessment-review', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of #122, The on-assessment-review message type button should only be displayed when using the deprecated "assessment" mode, so we should only add on-assessment-review to this list when the self.is_assessment flag is True.
| MentoringMessageBlock, | ||
| category='pb-message', | ||
| boilerplate=message_type, | ||
| label=get_message_label(message_type), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Message block specs should have single_instance=True, because we only allow a single instance of each message type inside a Mentoring block, BUT I tried that and it looks like single_instance functionality is currently implemented in a rather hackish way and it can only limit blocks by category. Here we still want to allow multiple pb-message blocks, as long as they are of different message_type, but it doesn't look like we can do that currently.
If you set single_instance=True, the button that you click gets correctly disabled when adding a Message type block to a mentoring container, but if you refresh the page, all pb-message buttons are disabled rather than just the one type that you added to the container.
One way to fix this would be to introduce subclasses of MentoringMessageBlock, so that each message type would be a separate class.
Another way would be to expand the single_instance feature of nested xblocks in xblock-utils so that it would be able to toggle the button on parameters other than just the block category.
I think I prefer the second option, but neither of these options are simple, so in order to not block this PR, I think I am fine with leaving single_instance=True out for now, and create a task to handle this in next sprint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mtyaka Given the larger work that both options would represent, which we don't have time to address in the scope here, could we try to find a third option?
What would happen if two messages of the same type were added? Would there be an explicit validation error appearing for the bloc? If so, would it make sense to simply let the validation error inform the author that he needs to fix it - we can't prevent every editing issue proactively anyway, so this is already a pattern the author should be familiar with. And improve this case when Studio and xblock-utils support the use case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@antoviaque If two messages of the same type are added, there is an explicit error message on the parent (mentoring) block, although it's only visible after you update the page. It's not a big problem, so I think it would be fine to leave it for when Studio/xblock-utils support the use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, sounds like I should just go ahead and merge? @antoviaque, @mtyaka?
|
@mtyaka, those changes have been made. |
|
Thanks @haikuginger! Once the build is green: 👍
|


Since we initially created ProblemBuilder, the StudioContainerWithNestedXBlocksMixin class has been added to the XBlock runtime. Since this is now the standardized way of dealing with nested XBlocks, we want to move MentoringBlock to use that structure and gain any existing benefits, as well as any benefits for the future.
Dependencies: None
Screenshots:

Sandbox URL:
Partner information: Hosted on edx.org (Davidson College)
Testing instructions:
Author notes and concerns:
Reviewers