feat: New history log api functions [FC-0123]#501
feat: New history log api functions [FC-0123]#501ChrisChV wants to merge 11 commits intoopenedx:mainfrom
history log api functions [FC-0123]#501Conversation
|
Thanks for the pull request, @ChrisChV! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
history log api functionshistory log api functions [FC-0123]
| Return DraftChangeLogRecords for a PublishableEntity since its last publication, | ||
| ordered from most recent to oldest. | ||
|
|
||
| If the entity has never been published, all DraftChangeLogRecords are returned. |
There was a problem hiding this comment.
What is the intended behavior of this function when the last "publish" was a deletion?
There was a problem hiding this comment.
I think the current code does work in this case, to be clear. And I realize that it's not something that's likely to come up in the UX. I just want to make sure that the behavior is well defined and tested for in that edge case.
There was a problem hiding this comment.
Thanks! Yes, I'll add tests for that case.
|
High level comment: please note that it's possible to reset a draft to the published state (i.e. discard changes). When that happens, the Draft version is reset to point to the same version as Published, but all the versions created still exist and are reflected in the draft change log. |
| .values_list("draft_change_log__changed_by", flat=True) | ||
| .distinct() | ||
| ) | ||
| return get_user_model().objects.filter(pk__in=contributor_ids) |
There was a problem hiding this comment.
Do you think it is worth adding an order_by here to assert consistent results?
|
Per this slack thread, I think we're going to want to extend the Does this sound correct to you @sdaitzman, @marcotuts? |
|
I made a separate issue to capture user publish intent (my comment above), so that we can discuss in more detail: http://github.com/openedx/openedx-core/issues/533 |
| # If reset_drafts_to_published() was called within this publish window, there | ||
| # will be a DraftChangeLogRecord where new_version == baseline. Use the most | ||
| # recent such record as the new lower bound so discarded entries are excluded. |
There was a problem hiding this comment.
I know I was the one who flagged this because I wanted to make sure the code noted it, but I actually don't think we should filter out discarded changes. It's a log, and "discard changes" is a legit thing that someone in that chain of edits did. I think we should be explicit that it happened, especially if people are going here because they're thinking, "I know I was working on this, but what happened to my edits!?!" Then they could see "Dave discarded changes" and know who to strangle talk to.
|
|
||
| def get_descendant_component_entity_ids(container: Container) -> list[int]: | ||
| """ | ||
| Return the entity IDs of all leaf (non-Container) descendants of ``container``. |
There was a problem hiding this comment.
Code in the containers applet shouldn't be querying Draft models directly, or ideally even be aware of components.
Where is this going to get called from?
There was a problem hiding this comment.
Code in the containers applet shouldn't be querying Draft models directly, or ideally even be aware of components.
I've refactored the function to not use Draft
Where is this going to get called from?
This function is called in edx-platform, in get_library_container_draft_history and in get_library_container_publish_history. This is what I have implemented, as I mentioned in Slack. It might change later after implement the direct field.
ormsbee
left a comment
There was a problem hiding this comment.
Partial review. Will add more in the morning. Thank you.
| reset_filter = { | ||
| "entity_id": entity_id, | ||
| "new_version_id": baseline_version_id, | ||
| "draft_change_log__changed_at__lte": published_at, | ||
| } | ||
| if prev_published_at: | ||
| reset_filter["draft_change_log__changed_at__gt"] = prev_published_at | ||
|
|
||
| last_reset_at = ( | ||
| DraftChangeLogRecord.objects | ||
| .filter(**reset_filter) | ||
| .order_by("-draft_change_log__changed_at") | ||
| .values_list("draft_change_log__changed_at", flat=True) | ||
| .first() | ||
| ) |
There was a problem hiding this comment.
Per @sdaitzman, we should represent the Discard Changes action as another kind of draft update, so we don't need to filter them out here.
| Intermediate containers (e.g. Subsections, Units) are never included in the | ||
| result; only leaf component entities are returned. |
There was a problem hiding this comment.
[Question] Why do we only focus on Components and exclude intermediate containers? Don't we need to include changes to intermediate Containers to build a historical view of the parent Container?
There was a problem hiding this comment.
This implementation follows the logic discussed in openedx/frontend-app-authoring#2805 (comment)
For Containers, both lists follow the same structure and contain:
- All direct edits made to the container itself.
- All edits included in each publish group, for all descendant components.
E.g. for a section, an edit to a component grouped under a prior publish shows one edit, to that component (not to any in-between levels, or to the direct child subsection that is an ancestor of the component)
There was a problem hiding this comment.
So does a rename of a Unit never show up at all?
There was a problem hiding this comment.
It is shown in the unit's history log:
All direct edits made to the container itself.
The logic I understood here is to show the important changes, which are the components, in the log. If we show only intermediate changes, for example, in a section's history log, it can display so many changes that it might overwhelm the user.
The current implementation keeps it compact and with relevant information.
There was a problem hiding this comment.
I guess I would expect it to show non-side-effect changes, i.e. ones where the actual version_num of the record changes. Because otherwise, we'd miss things like "Deleted this Unit", or "Removed this Subsectino" which are really big changes.
| ) | ||
|
|
||
|
|
||
| def get_descendant_component_entity_ids(container: Container) -> list[int]: |
There was a problem hiding this comment.
I know you're framing this as descendants using container traversal, but publishing follows the (very closely related) concept of dependencies. Unless you absolutely need to frame this in terms of containers, it might make more sense to do dependency traversal similar to how we traverse dependencies for publishing. It will likely be faster as well.
One of the reasons I mention this is that we will eventually have dependencies that live outside of the container->child relationship, like media assets, and eventually course-related things like grading policies.
There was a problem hiding this comment.
Please mark the new API calls as "[ 🛑 UNSTABLE ]" in their docstrings, like the containers APIs are.
Otherwise, two very small change requests to keep component knowledge out of the publishing applet. I realize that we may have to revise this pending further UX refinement, but this is really excellent work—very clearly written and well commented. Great job!
| .filter(entity_id=entity_id) | ||
| .select_related( | ||
| "draft_change_log__changed_by", | ||
| "entity__component__component_type", |
There was a problem hiding this comment.
Please get rid of this line here, and add it on as a .select_related() when this gets called from get_library_component_draft_history(), like:
history_qs = content_api.get_entity_draft_history(component.publishable_entity)
records = list(history_qs.select_related("entity__component__component_type")Since get_library_component_draft_history() is aware of components anyway and is actively traversing them, I think it's better to keep that knowledge out of the publishing applet as much as possible.
| .filter(entity_id=entity_id) | ||
| .select_related( | ||
| "publish_log__published_by", | ||
| "entity__component__component_type", |
There was a problem hiding this comment.
Likewise, please remove this line and add it back in as a select_related call from get_library_component_publish_history()
Description
get_entity_draft_history: ReturnDraftChangeLogRecordsfor aPublishableEntitysince its last publication.get_entity_version_contributors: returns the users who authored changes between two published versions of an entityget_entity_publish_history: Return allPublishLogRecordsfor aPublishableEntityget_entity_publish_history_entries: Return theDraftChangeLogRecordsassociated with a specificPublishLogget_descendant_component_entity_ids: BFS traversal of a container hierarchy, returning the IDs of all descendant component entities (used to scope queries when fetching history forcontainer children)
Supporting information
Testing instructions
Follow the testing instructions in openedx/frontend-app-authoring#2948
Deadline
Before the Verawood cut.
Other information
N/A