Skip to content

Commit 443c3d6

Browse files
authored
feat: Support for grouped changesets of draft modifications (#290)
This introduces `DraftChangeLog` and `DraftChangeLogRecord`, which are mostly draft equivalents of the `PublishLog` and `PublishLogRecord`. A `DraftChangeLog` entry is created for every group of changes (e.g. an import or a reset), and `DraftChangeLogRecord` has a record for every individual publishable entity that was changed. The motivation for these models: 1. Batch changes into logical groupings, e.g. "discard changes in library" or "import a course's content into this library". 2. Accurate history reconstruction: We don't currently track reset-to-published operations anywhere, so we can't completely faithfully reconstruct historical draft information based purely off of the timestamps of when `PublishableEntityVersions` are created. It introduces the `bulk_draft_changes_for` context manager, a convenient API for associating multiple edits into an atomic changeset with one DraftChangeLog. It also introduces a new model `DraftSideEffect`. This is to capture the idea that sometimes a change in one publishable entity will affect another one, even we don't explicitly create a new version of the affected entity. For now, the only planned side-effects are that changes in child elements affect their parent containers. The `DraftSideEffect` model could be used more broadly than this in the future, although we would need to be extremely thoughtful about it use to avoid write explosions and user confusion. For more details, see: #290 In a future commit, we expect to introduce an analogous `PublishSideEffect` record, tied to `PublishLog`. Bumps package version from 0.22.0 to 0.23.0
1 parent 9eadbc1 commit 443c3d6

File tree

12 files changed

+1741
-152
lines changed

12 files changed

+1741
-152
lines changed

openedx_learning/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Open edX Learning ("Learning Core").
33
"""
44

5-
__version__ = "0.22.0"
5+
__version__ = "0.23.0"

openedx_learning/apps/authoring/publishing/admin.py

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@
44
from __future__ import annotations
55

66
from django.contrib import admin
7+
from django.db.models import Count
78

89
from openedx_learning.lib.admin_utils import ReadOnlyModelAdmin, one_to_one_related_model_html
910

10-
from .models import LearningPackage, PublishableEntity, Published, PublishLog, PublishLogRecord
11+
from .models import (
12+
DraftChangeLog,
13+
DraftChangeLogRecord,
14+
LearningPackage,
15+
PublishableEntity,
16+
PublishLog,
17+
PublishLogRecord,
18+
)
19+
from .models.publish_log import Published
1120

1221

1322
@admin.register(LearningPackage)
@@ -171,3 +180,69 @@ def published_at(self, published_obj):
171180

172181
def message(self, published_obj):
173182
return published_obj.publish_log_record.publish_log.message
183+
184+
185+
class DraftChangeLogRecordTabularInline(admin.TabularInline):
186+
"""
187+
Tabular inline for a single Draft change.
188+
"""
189+
model = DraftChangeLogRecord
190+
191+
fields = (
192+
"entity",
193+
"title",
194+
"old_version_num",
195+
"new_version_num",
196+
)
197+
readonly_fields = fields
198+
199+
def get_queryset(self, request):
200+
queryset = super().get_queryset(request)
201+
return queryset.select_related("entity", "old_version", "new_version") \
202+
.order_by("entity__key")
203+
204+
def old_version_num(self, draft_change: DraftChangeLogRecord):
205+
if draft_change.old_version is None:
206+
return "-"
207+
return draft_change.old_version.version_num
208+
209+
def new_version_num(self, draft_change: DraftChangeLogRecord):
210+
if draft_change.new_version is None:
211+
return "-"
212+
return draft_change.new_version.version_num
213+
214+
def title(self, draft_change: DraftChangeLogRecord):
215+
"""
216+
Get the title to display for the DraftChange
217+
"""
218+
if draft_change.new_version:
219+
return draft_change.new_version.title
220+
if draft_change.old_version:
221+
return draft_change.old_version.title
222+
return ""
223+
224+
225+
@admin.register(DraftChangeLog)
226+
class DraftChangeSetAdmin(ReadOnlyModelAdmin):
227+
"""
228+
Read-only admin to view Draft changes (via inline tables)
229+
"""
230+
inlines = [DraftChangeLogRecordTabularInline]
231+
fields = (
232+
"uuid",
233+
"learning_package",
234+
"num_changes",
235+
"changed_at",
236+
"changed_by",
237+
)
238+
readonly_fields = fields
239+
list_display = fields
240+
list_filter = ["learning_package"]
241+
242+
def num_changes(self, draft_change_set):
243+
return draft_change_set.num_changes
244+
245+
def get_queryset(self, request):
246+
queryset = super().get_queryset(request)
247+
return queryset.select_related("learning_package", "changed_by") \
248+
.annotate(num_changes=Count("records"))

0 commit comments

Comments
 (0)