Skip to content
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

feat: return publishing information on get component endpoint [FC-0062] #35476

17 changes: 16 additions & 1 deletion openedx/core/djangoapps/content_libraries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,12 @@ class LibraryXBlockMetadata:
modified = attr.ib(type=datetime)
display_name = attr.ib("")
last_published = attr.ib(default=None, type=datetime)
last_draft_created = attr.ib(default=None, type=datetime)
last_draft_created_by = attr.ib(default=None, type=datetime)
rpenido marked this conversation as resolved.
Show resolved Hide resolved
published_by = attr.ib("")
has_unpublished_changes = attr.ib(False)
tags_count = attr.ib(0)
created = attr.ib(default=None, type=datetime)

@classmethod
def from_component(cls, library_key, component):
Expand All @@ -228,6 +232,14 @@ def from_component(cls, library_key, component):
"""
last_publish_log = component.versioning.last_publish_log

published_by = None
if last_publish_log and last_publish_log.published_by:
published_by = last_publish_log.published_by.username

draft = component.versioning.draft
last_draft_created = draft.created if draft else None
last_draft_created_by = draft.publishable_entity_version.created_by if draft else None

return cls(
usage_key=LibraryUsageLocatorV2(
library_key,
Expand All @@ -238,7 +250,10 @@ def from_component(cls, library_key, component):
created=component.created,
modified=component.versioning.draft.created,
last_published=None if last_publish_log is None else last_publish_log.published_at,
has_unpublished_changes=component.versioning.has_unpublished_changes
published_by=published_by,
last_draft_created=last_draft_created,
last_draft_created_by=last_draft_created_by,
has_unpublished_changes=component.versioning.has_unpublished_changes,
)


Expand Down
5 changes: 5 additions & 0 deletions openedx/core/djangoapps/content_libraries/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,12 @@ class LibraryXBlockMetadataSerializer(serializers.Serializer):

block_type = serializers.CharField(source="usage_key.block_type")
display_name = serializers.CharField(read_only=True)
last_published = serializers.DateTimeField(format=DATETIME_FORMAT, read_only=True)
published_by = serializers.CharField(read_only=True)
last_draft_created = serializers.DateTimeField(format=DATETIME_FORMAT, read_only=True)
last_draft_created_by = serializers.CharField(read_only=True)
has_unpublished_changes = serializers.BooleanField(read_only=True)
created = serializers.DateTimeField(format=DATETIME_FORMAT, read_only=True)

# When creating a new XBlock in a library, the slug becomes the ID part of
# the definition key and usage key:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
from unittest import skip

import ddt
from datetime import datetime, timezone
from uuid import uuid4
from django.contrib.auth.models import Group
from django.test.client import Client
from freezegun import freeze_time
from organizations.models import Organization
from rest_framework.test import APITestCase

Expand Down Expand Up @@ -270,12 +272,18 @@ def test_library_blocks(self):
assert self._get_library_blocks(lib_id)['results'] == []

# Add a 'problem' XBlock to the library:
block_data = self._add_block_to_library(lib_id, "problem", "ࠒröblæm1")
create_date = datetime(2024, 6, 6, 6, 6, 6, tzinfo=timezone.utc)
with freeze_time(create_date):
block_data = self._add_block_to_library(lib_id, "problem", "ࠒröblæm1")
self.assertDictContainsEntries(block_data, {
"id": "lb:CL-TEST:téstlꜟط:problem:ࠒröblæm1",
"display_name": "Blank Problem",
"block_type": "problem",
"has_unpublished_changes": True,
"last_published": None,
"published_by": None,
"last_draft_created": create_date.isoformat().replace('+00:00', 'Z'),
"last_draft_created_by": "Bob",
})
block_id = block_data["id"]
# Confirm that the result contains a definition key, but don't check its value,
Expand All @@ -287,10 +295,14 @@ def test_library_blocks(self):
assert self._get_library(lib_id)['has_unpublished_changes'] is True

# Publish the changes:
self._commit_library_changes(lib_id)
publish_date = datetime(2024, 7, 7, 7, 7, 7, tzinfo=timezone.utc)
with freeze_time(publish_date):
self._commit_library_changes(lib_id)
assert self._get_library(lib_id)['has_unpublished_changes'] is False
# And now the block information should also show that block has no unpublished changes:
block_data["has_unpublished_changes"] = False
block_data["last_published"] = publish_date.isoformat().replace('+00:00', 'Z')
block_data["published_by"] = "Bob"
pomegranited marked this conversation as resolved.
Show resolved Hide resolved
self.assertDictContainsEntries(self._get_library_block(block_id), block_data)
assert self._get_library_blocks(lib_id)['results'] == [block_data]

Expand All @@ -311,13 +323,16 @@ def test_library_blocks(self):
</multiplechoiceresponse>
</problem>
""".strip()
self._set_library_block_olx(block_id, new_olx)
update_date = datetime(2024, 8, 8, 8, 8, 8, tzinfo=timezone.utc)
with freeze_time(update_date):
self._set_library_block_olx(block_id, new_olx)
# now reading it back, we should get that exact OLX (no change to whitespace etc.):
assert self._get_library_block_olx(block_id) == new_olx
# And the display name and "unpublished changes" status of the block should be updated:
self.assertDictContainsEntries(self._get_library_block(block_id), {
"display_name": "New Multi Choice Question",
"has_unpublished_changes": True,
"last_draft_created": update_date.isoformat().replace('+00:00', 'Z')
})

# Now view the XBlock's student_view (including draft changes):
Expand Down Expand Up @@ -358,12 +373,18 @@ def test_library_blocks_studio_view(self):
assert self._get_library_blocks(lib_id)['results'] == []

# Add a 'html' XBlock to the library:
block_data = self._add_block_to_library(lib_id, "html", "html1")
create_date = datetime(2024, 6, 6, 6, 6, 6, tzinfo=timezone.utc)
with freeze_time(create_date):
block_data = self._add_block_to_library(lib_id, "html", "html1")
self.assertDictContainsEntries(block_data, {
"id": "lb:CL-TEST:testlib2:html:html1",
"display_name": "Text",
"block_type": "html",
"has_unpublished_changes": True,
"last_published": None,
"published_by": None,
"last_draft_created": create_date.isoformat().replace('+00:00', 'Z'),
"last_draft_created_by": "Bob",
})
block_id = block_data["id"]

Expand All @@ -372,24 +393,32 @@ def test_library_blocks_studio_view(self):
assert self._get_library(lib_id)['has_unpublished_changes'] is True

# Publish the changes:
self._commit_library_changes(lib_id)
publish_date = datetime(2024, 7, 7, 7, 7, 7, tzinfo=timezone.utc)
with freeze_time(publish_date):
self._commit_library_changes(lib_id)
assert self._get_library(lib_id)['has_unpublished_changes'] is False
# And now the block information should also show that block has no unpublished changes:
block_data["has_unpublished_changes"] = False
block_data["last_published"] = publish_date.isoformat().replace('+00:00', 'Z')
block_data["published_by"] = "Bob"
self.assertDictContainsEntries(self._get_library_block(block_id), block_data)
assert self._get_library_blocks(lib_id)['results'] == [block_data]

# Now update the block's OLX:
orig_olx = self._get_library_block_olx(block_id)
assert '<html' in orig_olx
new_olx = "<html><b>Hello world!</b></html>"
self._set_library_block_olx(block_id, new_olx)

update_date = datetime(2024, 8, 8, 8, 8, 8, tzinfo=timezone.utc)
with freeze_time(update_date):
self._set_library_block_olx(block_id, new_olx)
# now reading it back, we should get that exact OLX (no change to whitespace etc.):
assert self._get_library_block_olx(block_id) == new_olx
# And the display name and "unpublished changes" status of the block should be updated:
self.assertDictContainsEntries(self._get_library_block(block_id), {
"display_name": "Text",
"has_unpublished_changes": True,
"last_draft_created": update_date.isoformat().replace('+00:00', 'Z')
})

# Now view the XBlock's studio view (including draft changes):
Expand Down Expand Up @@ -1019,6 +1048,7 @@ def test_library_paste_clipboard(self):
# the the block in the clipboard
self.assertDictContainsEntries(self._get_library_block(paste_data["id"]), {
**block_data,
"last_draft_created_by": None,
"id": f"lb:CL-TEST:test_lib_paste_clipboard:problem:{pasted_block_id}",
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import time
from unittest.mock import patch

from openedx_tagging.core.tagging.models import ObjectTag
from organizations.models import Organization

from openedx.core.djangoapps.content_libraries import api as library_api
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase
from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory

from .. import api
from ..helpers.objecttag_export_helpers import TaggedContent, build_object_tree_with_objecttags, iterate_with_level
from openedx_tagging.core.tagging.models import ObjectTag
from organizations.models import Organization


class TestGetAllObjectTagsMixin:
Expand Down
2 changes: 1 addition & 1 deletion requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ libsass==0.10.0
click==8.1.6

# pinning this version to avoid updates while the library is being developed
openedx-learning==0.11.4
# openedx-learning==0.11.5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rpenido Uncomment this line

Copy link
Contributor Author

@rpenido rpenido Sep 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed: 4dd697a


# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.
openai<=0.28.1
Expand Down
6 changes: 2 additions & 4 deletions requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -824,10 +824,8 @@ openedx-filters==1.9.0
# -r requirements/edx/kernel.in
# lti-consumer-xblock
# ora2
openedx-learning==0.11.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
openedx-learning==0.11.5
# via -r requirements/edx/kernel.in
openedx-mongodbproxy==0.2.1
# via -r requirements/edx/kernel.in
optimizely-sdk==4.1.1
Expand Down
3 changes: 1 addition & 2 deletions requirements/edx/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1373,9 +1373,8 @@ openedx-filters==1.9.0
# -r requirements/edx/testing.txt
# lti-consumer-xblock
# ora2
openedx-learning==0.11.4
openedx-learning==0.11.5
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
openedx-mongodbproxy==0.2.1
Expand Down
6 changes: 2 additions & 4 deletions requirements/edx/doc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -983,10 +983,8 @@ openedx-filters==1.9.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
openedx-learning==0.11.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
openedx-learning==0.11.5
# via -r requirements/edx/base.txt
openedx-mongodbproxy==0.2.1
# via -r requirements/edx/base.txt
optimizely-sdk==4.1.1
Expand Down
6 changes: 2 additions & 4 deletions requirements/edx/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1034,10 +1034,8 @@ openedx-filters==1.9.0
# -r requirements/edx/base.txt
# lti-consumer-xblock
# ora2
openedx-learning==0.11.4
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
openedx-learning==0.11.5
# via -r requirements/edx/base.txt
openedx-mongodbproxy==0.2.1
# via -r requirements/edx/base.txt
optimizely-sdk==4.1.1
Expand Down
Loading