Repository navigation
feat: Tag List on Unit page [FC-0036] #33645
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
Changes from 19 commits
4a95e8d
d8e327c
e36cb84
b383efe
a9f2afc
e19ff55
7305ed7
d79cf97
b423507
3db38e8
b96fd33
a005b34
694d164
6d70b80
91c03c1
76232ba
7aa790d
f19b1e8
aa12f5c
29f3b9c
d3c7603
aade38f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,10 +25,14 @@ | |
| from common.djangoapps.student.auth import has_course_author_access | ||
| from common.djangoapps.xblock_django.api import authorable_xblocks, disabled_xblocks | ||
| from common.djangoapps.xblock_django.models import XBlockStudioConfigurationFlag | ||
| from cms.djangoapps.contentstore.toggles import use_new_problem_editor | ||
| from cms.djangoapps.contentstore.toggles import ( | ||
| use_new_problem_editor, | ||
| use_tagging_taxonomy_list_page, | ||
| ) | ||
| from openedx.core.lib.xblock_utils import get_aside_from_xblock, is_xblock_aside | ||
| from openedx.core.djangoapps.discussions.models import DiscussionsConfiguration | ||
| from openedx.core.djangoapps.content_staging import api as content_staging_api | ||
| from openedx.core.djangoapps.content_tagging.api import get_content_tags | ||
| from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order | ||
| from xmodule.modulestore.exceptions import ItemNotFoundError # lint-amnesty, pylint: disable=wrong-import-order | ||
| from ..toggles import use_new_unit_page | ||
|
|
@@ -61,7 +65,7 @@ | |
| "editor-mode-button", "upload-dialog", | ||
| "add-xblock-component", "add-xblock-component-button", "add-xblock-component-menu", | ||
| "add-xblock-component-support-legend", "add-xblock-component-support-level", "add-xblock-component-menu-problem", | ||
| "xblock-string-field-editor", "xblock-access-editor", "publish-xblock", "publish-history", | ||
| "xblock-string-field-editor", "xblock-access-editor", "publish-xblock", "publish-history", "tag-list", | ||
| "unit-outline", "container-message", "container-access", "license-selector", "copy-clipboard-button", | ||
| "edit-title-button", | ||
| ] | ||
|
|
@@ -178,9 +182,14 @@ def container_handler(request, usage_key_string): | |
| prev_url = quote_plus(prev_url) if prev_url else None | ||
| next_url = quote_plus(next_url) if next_url else None | ||
|
|
||
| show_unit_tags = use_tagging_taxonomy_list_page() | ||
| unit_tags = None | ||
| if show_unit_tags and is_unit_page: | ||
| unit_tags = get_unit_tags(usage_key) | ||
|
|
||
| # Fetch the XBlock info for use by the container page. Note that it includes information | ||
| # about the block's ancestors and siblings for use by the Unit Outline. | ||
| xblock_info = create_xblock_info(xblock, include_ancestor_info=is_unit_page) | ||
| xblock_info = create_xblock_info(xblock, include_ancestor_info=is_unit_page, tags=unit_tags) | ||
|
|
||
| if is_unit_page: | ||
| add_container_page_publishing_info(xblock, xblock_info) | ||
|
|
@@ -216,6 +225,7 @@ def container_handler(request, usage_key_string): | |
| 'draft_preview_link': preview_lms_link, | ||
| 'published_preview_link': lms_link, | ||
| 'templates': CONTAINER_TEMPLATES, | ||
| 'show_unit_tags': show_unit_tags, | ||
| # Status of the user's clipboard, exactly as would be returned from the "GET clipboard" REST API. | ||
| 'user_clipboard': user_clipboard, | ||
| }) | ||
|
|
@@ -598,3 +608,84 @@ def component_handler(request, usage_key_string, handler, suffix=''): | |
| ) | ||
|
|
||
| return webob_to_django_response(resp) | ||
|
|
||
|
|
||
| def get_unit_tags(usage_key): | ||
| """ | ||
| Get the tags of a Unit and build a json to be read by the UI | ||
|
|
||
| Note: When migrating the `TagList` subview from `container_subview.js` to the course-authoring MFE, | ||
| this function can be simplified to use the REST API of openedx-learning, | ||
| which already provides this grouping + sorting logic. | ||
| """ | ||
| # Get content tags from content tagging API | ||
| content_tags = get_content_tags(usage_key) | ||
|
|
||
| # Group content tags by taxonomy | ||
| taxonomy_dict = {} | ||
| for content_tag in content_tags: | ||
| taxonomy_id = content_tag.taxonomy_id | ||
| # When a taxonomy is deleted, the id here is None. | ||
| # In that case the tag is not shown in the UI. | ||
| if taxonomy_id: | ||
| if taxonomy_id not in taxonomy_dict: | ||
| taxonomy_dict[taxonomy_id] = [] | ||
| taxonomy_dict[taxonomy_id].append(content_tag) | ||
|
|
||
| taxonomy_list = [] | ||
| total_count = 0 | ||
|
|
||
| def handle_tag(tags, root_ids, tag, child_tag_id=None): | ||
| """ | ||
| Group each tag by parent to build a tree. | ||
| """ | ||
| tag_processed_before = tag.id in tags | ||
| if not tag_processed_before: | ||
| tags[tag.id] = { | ||
| 'id': tag.id, | ||
| 'value': tag.value, | ||
| 'children': [], | ||
| } | ||
| if child_tag_id: | ||
| # Add a child into the children list | ||
| tags[tag.id].get('children').append(tags[child_tag_id]) | ||
| if tag.parent_id is None: | ||
| if tag.id not in root_ids: | ||
| root_ids.append(tag.id) | ||
| elif not tag_processed_before: | ||
| # Group all the lineage of this tag. | ||
| # | ||
| # Skip this if the tag has been processed before, | ||
| # we don't need to process lineage again to avoid duplicates. | ||
| handle_tag(tags, root_ids, tag.parent, tag.id) | ||
|
|
||
| # Build a tag tree for each taxonomy | ||
| for content_tag_list in taxonomy_dict.values(): | ||
| tags = {} | ||
| root_ids = [] | ||
|
|
||
| for content_tag in content_tag_list: | ||
| # When a tag is deleted from the taxonomy, the `tag` here is None. | ||
| # In that case the tag is not shown in the UI. | ||
| if content_tag.tag: | ||
| handle_tag(tags, root_ids, content_tag.tag) | ||
|
|
||
| taxonomy = content_tag_list[0].taxonomy | ||
|
|
||
| if tags: | ||
| count = len(tags) | ||
| # Add the tree to the taxonomy list | ||
| taxonomy_list.append({ | ||
| 'id': taxonomy.id, | ||
| 'value': taxonomy.name, | ||
| 'tags': [tags[tag_id] for tag_id in root_ids], | ||
| 'count': count, | ||
| }) | ||
| total_count += count | ||
|
Comment on lines
+636
to
+684
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ChrisChV It seems like this functionality does not properly handle multiple tags under the same sub-parent. This is how it is currently showing: for the following tags: When there aren't multiple tags, its seems to be working as expected: There is a recently created PR in the sample generation repo that provides a better distribution of the tags on components/units. (open-craft/taxonomy-sample-data#3). It can better help with testing
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yusuf-musleh Nice catch! I fixed that here 6d70b80 |
||
|
|
||
| unit_tags = { | ||
| 'count': total_count, | ||
| 'taxonomies': taxonomy_list, | ||
| } | ||
|
|
||
| return unit_tags | ||




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.
@ChrisChV I'm wondering if there is a way to make use of @bradenmacdonald 's updated implementation in the
openedx-learningrepo that added the lineage information to the object tags and takes care of the grouping under taxonomies as well, rather than re-implementing it here as well we could potentially utilize that as a single source of truth.Here is a sample of how the data looks like:
{ "block-v1:SampleTaxonomyOrg1+STC1+2023_1+type@vertical+block@aaf8b8eb86b54281aeeab12499d2cb0b": { "taxonomies": [ { "name": "LightCastSkillsTaxonomy", "taxonomy_id": 15, "editable": true, "tags": [ { "value": "Administrative Functions", "lineage": [ "Administration", "Administrative Support", "Administrative Functions" ] } ] }, { "name": "OpenCanadaTaxonomy", "taxonomy_id": 14, "editable": true, "tags": [ { "value": "Verbal Ability", "lineage": [ "Abilities", "Cognitive Abilities", "Communication Abilities", "Verbal Ability" ] } ] }, ... ] } }Though I think to make use of that, it seems there needs to be some changes made to the
openedx-learningrepo, namely extracting the logic out of theObjectTagsByTaxonomySerializerand exposing a python API that can be used in this repo. Since currently It is only available through the rest api.Any thoughts on this?
Uh oh!
There was an error while loading. Please reload this page.
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.
My thinking was that it's OK to have this duplicated here, because eventually this will be re-implemented in the course-authoring MFE and fetch data using REST (that's out of scope for us now, but at some point ALL of these Studio pages will be using the new MFE only). When that happens, it can use the logic in openedx-learning.
We should definitely put in a comment stating that when this is moved into an MFE it should be simplified to use the REST API which already provides this grouping + sorting logic. But for now since it's already implemented this way, I think it's fine to leave it like this and get it done sooner.
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.
Ok @bradenmacdonald, that makes sense 👍
@yusuf-musleh Adding another comment, In addition to grouping by taxonomy, it is also necessary to group by parents, to be able to assemble each level of the component.
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.
@bradenmacdonald I see, yup, that make sense to me as well!
@ChrisChV Sounds good!