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

docs: ADRs for modeling containers capability #251

Open
wants to merge 39 commits into
base: main
Choose a base branch
from

Conversation

mariajgrimaldi
Copy link
Member

@mariajgrimaldi mariajgrimaldi commented Oct 28, 2024

Description

ADRs with high-level decisions regarding the modeling of containers as a generalized capability of holding content, selectors as a strategy for selecting content that builds on the container idea, and units as a concrete implementation of containers that can contain static and dynamic content through selectors.

These decisions resulted from discussions in #38 and #219, which were consolidated into issue #240.

In order to validate the proposal, we created a POC implementation #254 of what has been discussed so far. These ADRs and the POC are still a work in progress, but early reviews are welcome to validate the proposal.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Oct 28, 2024
@openedx-webhooks
Copy link

openedx-webhooks commented Oct 28, 2024

Thanks for the pull request, @mariajgrimaldi!

What's next?

Please work through the following steps to get your changes ready for engineering review:

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.

🔘 Provide context

To 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:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads

🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

🔘 Let us know that your PR is ready for review:

Who will review my changes?

This repository is currently maintained by @axim-engineering. Tag them in a comment and let them know that your changes are ready for review.

Where 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:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

1. Unit Members and Relationships
==================================

- The members of a unit can only be components.
Copy link
Member Author

Choose a reason for hiding this comment

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

Should these decisions be part of this ADR as well?

  • Unit members can exist as standalone items outside the unit.
  • Members should indicate which unit they belong to.
  • Members can be duplicated from units but keeping original references.

- Containers can be published, allowing their content to be accessible from where the container is referenced.
- When a draft container is published, all its draft members are also published.
- Members of a container can be published independently of the container itself.
- If a member of a container is published independently, then it'd be published in the context of the container where it is referenced.
Copy link
Member Author

Choose a reason for hiding this comment

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

Should these decisions be part of this ADR as well?

  • All members within the container should be published before the container can be reused.

- When using version-agnostic references to members, no new version is created when members change since the latest draft or published state is always used.
- If a member is soft-deleted, the container will create a new version with the member removed.

5. Publishing
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 8, 2024

Choose a reason for hiding this comment

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

Question:
What I'm still missing here is the behavior of publishing containers when they're being reused somewhere and modified in some way. I understand we discussed this in our last meeting, but it's still not clear to me what our approach should be from the modeling point of view.

Copy link
Contributor

@bradenmacdonald bradenmacdonald left a comment

Choose a reason for hiding this comment

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

Thanks, I'm finding this to be a nice clear explanation in many places, but there are some things I'm confused by and I think we need to clarify them in the ADR.


- A container is designed as a generalized capability to hold different types of content.
- A container is a publishable content type that holds other content types through a parent-child relationship.
- A container application will offer shared definitions for use by other container types.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not really clear on what this third line about shared definitions means. Maybe an example would help?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think I explained it better in this comment: #251 (comment). I was referring to a Django application where the APIs and mixins that other container types would be built on.

Comment on lines 37 to 39
- Each container holds different states of its members (user-defined, initial, and frozen final state) to support rollback operations.
- Members can be added or removed from a container, and the container will maintain the state of the content for the previous version (frozen final state).
- The initial state of a container is immutable.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Each container holds different states of its members (user-defined, initial, and frozen final state) to support rollback operations.
- Members can be added or removed from a container, and the container will maintain the state of the content for the previous version (frozen final state).
- The initial state of a container is immutable.
- Each container holds different states of its members (user-defined, initial, and frozen final state) to support rollback operations.
- The initial state of a container is immutable.
- Members can be added or removed from a container, and the container will maintain the state of the content for the previous version (frozen final state).

I was confused what "the initial state is immutable" meant until I realized that "initial state" is one of three "states" mentioned earlier, and doesn't mean "the first version of a container"

Copy link
Member Author

Choose a reason for hiding this comment

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

Now, it mentions the initial list of members instead of "state".

- Containers represent their content hierarchy through a structure that defines parent-child relationships between the container and its members.
- The structure defining these relationships is anonymous, so it can only be referenced through the container.
- Containers can hold both static and dynamically generated content, including user-specific variations.
- Each container holds different states of its members (user-defined, initial, and frozen final state) to support rollback operations.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think an expanded description of these three states would be helpful. From this I'm not totally clear on what's the difference between initial and user-defined. And why "initial" is immutable but "frozen" is not (?) even though it sounds like it should be from the name.

Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 12, 2024

Choose a reason for hiding this comment

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

For the expanded descriptions, I created a separate section for "Container States," grouping all the state management decisions instead: af7dce4. I think now each definition is clear according to what was proposed in the model sketches.

EDIT: it's now called container history and it'd probably keep changing.

Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 12, 2024

Choose a reason for hiding this comment

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

why "initial" is immutable but "frozen" is not (?) even though it sounds like it should be from the name

I understand frozen lists are "this is how the container looked (a screenshot?) when a new version was created". So, if there's a unit U0 with components defined_list = P0latest, P1latest, P2latest all pointing to their latest versions at the time of the next container creation where frozen_list would be None and initial_list = P0, P1, P2 where P0, P1, and P2 are the pinned versions of P0latest and so on at moment of creation. When U1 is created, defined_list looks like defined_list = P0latest, P1latest, P2latest so frozen_list would be frozen_list = P0', P1'', P2' (the pinned versions of P0latest and so on). New versions for P0,...,1 are created when U1 defined_list would point to those, so we'd need a way of knowing what versions were in U1 in case we want to go back to it.

Frozen lists are useful when there are unpinned versions of members, since when they're all pinned, then defined_list = initial_list = frozen_list.

These in-line comments Dave left in #240 might explain this better: https://github.com/openedx/openedx-learning/pull/254/files#diff-6f2c589dc4ba5960e91d39f6488eb5e2e2e63ddaff63a75909091c760b877802R112-R154

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for expanding the descriptions, but I'm still confused :/

The code seems to call these "lists" not "states" - it would be good to pick a consistent term.

The user-defined state of a container is the state that the author has defined for the version of the container

I don't think we should call this "user-defined" when it's actually "author-defined." To me, "user-defined" sounds more like "the version of this container that a specific user will see (after we've accounted for groups, A/B testing, randomized content, exam permissions, etc.). But "author-defined" is clear.

  • The initial state of a container is the state of the container when it was first created.

# inital_list is an EntityList where all the versions are pinned, to show
# what the exact versions of the children were at the time that the
# Container was created.

Is initial list really the list when the container was first created? (which I assume would usually be an empty list). I think this should say "the initial list is a copy of the author-defined list that has all versions pinned as they were at the time the ContainerEntityVersion was created." Because if this is really something immutable from when the container was created, not something for when each version is created, then it should be on the container model, not redundantly specified for every ContainerEntityVersion.

The frozen final state of a container is the state of the container at the time when a new version is created.

I think this needs an example: "While this ContainerEntityVersion is the current draft, this will be None which means that unpinned entities in the list should be showing their latest version. However, once this is published or an even newer draft is created, this frozen list should be saved to reflect the history of the list at that exact point in time when the version was finalized."


I also don't really understand the need for an initial_list at all. Isn't it the same as "the frozen_list of the previous version"? And whether that's the case or not, when/why do we need to use it (initial_list)? I don't see any explanation in #240 .

Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 12, 2024

Choose a reason for hiding this comment

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

Oh, now I see that in the ADR itself I called it container states in some places and in others lists. I updated this also considering the author-defined suggestion. Thanks for the suggestions!


Is initial list really the list when the container was first created? (which I assume would usually be an empty list).

No. It's when the next container version is created, not the container entity itself.

I think this should say "the initial list is a copy of the author-defined list that has all versions pinned as they were at the time the ContainerEntityVersion was created."

Yes, that's a better description of what's happening here. I wanted to keep this as high-level as possible, so I avoided mentioning container entities or other model-specific definitions. Do you think it is necessary to reference each model for clarity?

Because if this is really something immutable from when the container was created, not something for when each version is created, then it should be on the container model, not redundantly specified for every ContainerEntityVersion.

Exactly, these lists are actually related to each container version, not the container entity. I'll make that clear.


I think this needs an example: "While this ContainerEntityVersion is the current draft, this will be None which means that unpinned entities in the list should be showing their latest version. However, once this is published or an even newer draft is created, this frozen list should be saved to reflect the history of the list at that exact point in time when the version was finalized."

In "...unpinned entities in the list should be showing their lates version" do you refer to the author-defined list, right?


I also don't really understand the need for an initial_list at all. Isn't it the same as "the frozen_list of the previous version"? And whether that's the case or not, when/why do we need to use it (initial_list)? I don't see any explanation in #240 .

I think the idea of an initial state came from this comment: #38 (comment) -- still, it doesn't say the end purpose. But as far as I understand, it's included for convenience:

# inital_list is an EntityList where all the versions are pinned, to show
# what the exact versions of the children were at the time that the
# Container was created. We could technically derive this, but it would be
# awkward to query.
#
# If the Container was defined so that all references were pinned, then this
# can point to the exact same EntityList as defined_list.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I saw that, but I still don't understand the use case. I also can't tell if it's any different than "the frozen_list of the previous version", or if those are the same thing.

@ormsbee maybe you can clarify?

- The initial state of a container is immutable.
- When a container's structure changes, e.g., when a new member is added, the user-defined state of the container is updated with the new members list.
- Containers support both fixed and version-agnostic references for members, allowing members to be pinned to a specific version or set to reference the latest draft or published state.
- The latest draft or published states can be referenced by setting the version to ``None``, avoiding the need for new instances on each update.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a difference between specifying "latest draft" and specifying "latest published" or is there just a way to say "latest" (version=None), and whether that is draft or published depends on _______ ?

EDIT: OK, from looking at the code it appears that each container (via EntityListRow) can specify both a draft_version and a published_version at the same time, separately. And each can be either pinned or unpinned. I guess I'm very unclear on how this works when the ContainerEntityVersion itself is versioned and has a draft + published version.

If I have a draft ContainerEntityVersion, what do its EntityListRows' published_versions mean? And when I publish it, so it's now a published ContainerEntityVersion, what do its EntityListRows' draft_versions mean?

Copy link
Contributor

@ormsbee ormsbee Nov 17, 2024

Choose a reason for hiding this comment

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

If I have a draft ContainerEntityVersion, what do its EntityListRows' published_versions mean? And when I publish it, so it's now a published ContainerEntityVersion, what do its EntityListRows' draft_versions mean?

I originally had it with just version, but I vaguely remember thinking that I had to add draft_version separately from published_version to address some edge case... which I have totally forgotten the specifics of. And maybe was wrong-headed to begin with. 😛 Let's take it back out for now and just keep version–if said weird edge case was real, I'm sure we'll run into it in the not-too-distant future, and deal with it then.

Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 19, 2024

Choose a reason for hiding this comment

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

@ormsbee: during our latest discussion, you mentioned that having both, mainly the draft version, covered the CCX use cases where we don't necessarily control the latest draft version, so we'd want to pin it for the author's context. Do you think we should cover that case later on?

We also discussed that having both would help us know which published/draft versions were in the frozen list when creating the next version, but I think that the use case could be covered by having a single reference to the current version draft or published. Would you agree?

Copy link
Contributor

Choose a reason for hiding this comment

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

@ormsbee: during our latest discussion, you mentioned that having both, mainly the draft version, covered the CCX use cases where we don't necessarily control the latest draft version, so we'd want to pin it for the author's context. Do you think we should cover that case later on?

I think I was mistaken there, because if the author wanted to pin to a specific version, they could do so, and then when they decided to undo that pin to get something later, they could do so with a new version of the container–no need to keep the publish versioned separate.

We also discussed that having both would help us know which published/draft versions were in the frozen list when creating the next version, but I think that the use case could be covered by having a single reference to the current version draft or published. Would you agree?

Yeah, I agree.

I think where my head might have been going is what happens in the following scenario:

  1. UnitVersion UV1 has an unpinned reference to Component C1.
  2. C1 has ComponentVersions CV1 and CV2. CV1 is the current published version, while CV2 is the draft version.
  3. C1 is soft-deleted, forcing UV1 to pin down its references to the last version. But does it pin to CV1 or CV2?

So I'm still kicking it around in my head, but I'm trying to see how things line up with Unit/Container modeling if we don't force new container versions to be created when things get deleted, and instead filter out the deleted stuff at read time. I think it could get rid of a lot of the bookkeeping needs for the model. I'm trying to sketch this out this evening to see if it fits together in a reasonable way...

- A new version is created if and only if the container itself changes (e.g., title, ordering of contents, adding or removing content) and not when its content changes (e.g., a component in a Unit is updated with new text).
- Changes to the order of members within a container require creating a new version of the container with the new ordering.
- Each time a new version is created because of metadata changed, its members are copied from the previous version to preserve the state of the content at that time.
- Changes in pinned published or draft states require creating a new version of the container to maintain the state of the content for the previous version.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you "pin" to a specific draft version or published version? I would assume you can only pin to a specific version number, which isn't really considered "draft" or "published" - it's just a specific version (that may have been the latest published version and/or latest draft version at some point, but may or may not be now).

e.g.

1 2 3 4 5 6 7 8
    ^   ^     ^
    |   |     v8 is draft
    |   v5 is published
    Pinned to v3

In this scenario, v3 is just a specific version, neither published nor draft.

Edit: OK, I see now you can pin "draft" and "published" separately for each EntityRow, but I'm confused on the implications of that.


- A unit is a concrete type of container that holds components.
- A unit is a container, making it also a publishable entity.
- A unit application will offer shared definitions for use by other unit subtypes.
Copy link
Contributor

Choose a reason for hiding this comment

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

What is a "unit application"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this explains it better: 46999f8

I'm specifically referring to:

  • Generalized containers (containers app–lowest level of these)
  • Selectors for dynamically selecting 0-N PublishableEntities, i.e. how we're going to do things like SplitTest and Randomized (selectors app, builds on containers).
  • Units (units app, builds on containers and selectors)–basically empty shells at the moment.

From #240. Let me know if there's a better way of saying this.

Copy link
Contributor

Choose a reason for hiding this comment

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

  • Generalized containers (containers app–lowest level of these)
  • Selectors for dynamically selecting 0-N PublishableEntities, i.e. how we're going to do things like SplitTest and Randomized (selectors app, builds on containers).
  • Units (units app, builds on containers and selectors)–basically empty shells at the moment.

Can you include this whole example? That really makes it more clear.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, sure!

- Members within a container are maintained in a specific order as an ordered list.
- Containers represent their content hierarchy through a structure that defines parent-child relationships between the container and its members.
- The structure defining these relationships is anonymous, so it can only be referenced through the container.
- Containers can hold both static and dynamically generated content, including user-specific variations.
Copy link
Contributor

Choose a reason for hiding this comment

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

How will "dynamically generated content" work?

Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 14, 2024

Choose a reason for hiding this comment

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

According to #240, dynamically generated content will be built on top of what the issue called "selectors". I thought we could write a different ADR for it. I'm going to create an empty ADR with some basic info at the moment, so it's clear we will address it in this PR.

Although the issue calls it "dynamically selected" i.e SplitTest or Randomized content, so I believe it'd be better to change this from "static and dynamically generated" to "static and dynamic content" removing "generated".

Copy link
Member Author

Choose a reason for hiding this comment

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

I removed this from the generalized container capability into the unit ADR, since it makes more sense to be there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I see now:

    # These entities
    # could be Selectors, in which case we'd need to do more work to find the right
    # variant.

The part that I'm struggling with is that we seem to be using a lot of complexity and work (frozen_list, initial_list) to support both pinned and unpinned versions at this base layer, and I was kind of assuming that by paying the price of that complexity, we would also be able to handle "selectors" and dynamic content. But it seems like that's going to be yet another layer on top of this.

The way that "selectors" will have to deal with pinned/unpinned references and children seems very similar and I am kind of hoping that we'll be able to find some commonality and simplify this. It may be too early to know that though.

Note that we could implement containers and units first, and add selectors later, but I would like to validate selectors because it's the riskiest part of this design.

I agree with this statement. It's hard to know how good this high level design is until we see how it works with selectors.

Copy link
Contributor

Choose a reason for hiding this comment

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

The way that "selectors" will have to deal with pinned/unpinned references and children seems very similar and I am kind of hoping that we'll be able to find some commonality and simplify this. It may be too early to know that though.

I think that when I first sketched this part of the data model, my thinking was that we'd want to always pin all the entities referenced from the Variant, because we might be dynamically generating a different Variant per user–something that would make it really expensive to amend things if someone deleted an entity that was referenced in an unpinned way. I also suspect that I sketched the Selector/Variant piece before I started making the distinction between defined/initial/frozen lists, since a lot of that was added to deal with deleting members.

But I agree with your point that there's more overlap here than this model is representing now. Author-defined Variants (like A/B tests) are much more like their own kind of ContainerEntityVersions. Dynamically generated Variants (like randomized subset), could be pointers to those containers + a pinned EntityList.

I feel like all the fundamental pieces are there, if we just nudge things around a bit. I'll chew on it.

Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 20, 2024

Choose a reason for hiding this comment

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

But I agree with your point that there's more overlap here than this model is representing now. Author-defined Variants (like A/B tests) are much more like their own kind of ContainerEntityVersions. Dynamically generated Variants (like randomized subset), could be pointers to those containers + a pinned EntityList.

Am I right in thinking that variants would be a container type with special rules (author-defined or per-user) for their author-defined list shown to students? I wonder how this would work with other container types like units.

Comment on lines 90 to 97
This section defines the rules for pruning container versions, explaining when a container version can be pruned and the effects of pruning on the container and its members.

- A container version can be pruned if:
#. It's not being used by any other container.
#. It's not a published version.
#. It's not the latest version of the container.
- In a top-down approach, start the deletion process with the parent container and work your way down to its members. E.g., when pruning Section V2 > Subsection V1 > Unit V3, the deletion process starts in the greater container working its way down to the smaller.
- Pruning a container version will not affect the container's history or the members of other container versions, so containers will not be deleted if they are shared by other containers.
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 20, 2024

Choose a reason for hiding this comment

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

Questions:
This is my first approach to pruning, based on #240 (comment) and #154.

However, I still need some clarification about the implications of removing an entity version that's used somewhere. I'm guessing if another container is using this container entity version, then I cannot delete it (prune). Is that a correct assumption? Or I could delete it, but as with soft-deletion, I'd need to create a new container version for all containers referencing the deleted object, but still, the history for that container would be somewhat invalid after pruning.

Now, as for the history of a single container: let's say I have 3 versions for a unit: UV1, UV2 and UV3. What would happen if I prune UV1 and UV2, but there are publishable entities from those referenced by rows in UV3? Would I be able to prune all but the entities referenced by UV3?


This section defines the rules for version control in containers, explaining when new versions are created based on changes to container structure or metadata.

- A new version is created if and only if the container itself changes (e.g., title, ordering of members, adding or removing members) and not when its members change (e.g., a component in a Unit is updated with new text). For instance, a new version of a unit is created when a component is removed, not when a new version of a component is created.
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 20, 2024

Choose a reason for hiding this comment

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

Question
Does unpinning a member version from a container merit creating a new version for the container? Or is this an entirely different container?

@@ -0,0 +1,78 @@
18. Modeling Units as a Concrete Implementation of the Container Capability
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Nov 20, 2024

Choose a reason for hiding this comment

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

I'm not sure if this requires its own ADR, but it helps illustrate the decisions using a more familiar concept like units.

- Components are referenced as an ordered list in a unit.
- Units can hold both static and dynamic content, such as user-specific variations.
- Units can reference pinned and unpinned versions of its components.
- The latest draft or publish version of a component can be set by using `None` in thr parent-child relationship between units-components.
Copy link
Member Author

Choose a reason for hiding this comment

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

Suggested change
- The latest draft or publish version of a component can be set by using `None` in thr parent-child relationship between units-components.
- The latest draft or publish version of a component can be set by using `None` in the parent-child relationship between units-components.

Comment on lines 44 to 52
- Each unit version holds different list of components to support rollback operations and history tracking.
- The author-defined list is the list of components defined by the author for a specific unit version.
- The author-defined list of components won't change for a specific version.
- The initial list is a copy of the author-defined list that has all components pinned to the versions at the time of the unit version creation.
- The initial list is immutable for a unit version.
- The frozen list refers to the list of components at the time when the next version of the unit is created.
- When creating the author-defined list of a new version with pinned references, then the author-defined list is the same as the initial and frozen list. When creating a new version with unpinned references, then the frozen list starts as `None` and should be updated with the author-defined components pinned when a new version is created.
- The author-defined list is used to show the content of a unit version as the author specified it, the frozen list can be used for discard operations on a draft version and the initial-list is part of the history of evolution of the unit.
- These lists allow history tracking of a unit version and revert operations.
Copy link
Member Author

Choose a reason for hiding this comment

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

note to self: This needs to be updated according the latest changes in the container ADR.

@mariajgrimaldi mariajgrimaldi changed the title [WIP] docs: ADRs for modeling containers capability docs: ADRs for modeling containers capability Nov 22, 2024
@mariajgrimaldi mariajgrimaldi marked this pull request as ready for review November 22, 2024 12:34
@mariajgrimaldi
Copy link
Member Author

I'm opening this PR for review as we continue to discuss the proposal, although it doesn't mean we've reached a consensus yet. Thank you!

@mariajgrimaldi
Copy link
Member Author

You can find the latest discussion about unit/container modeling in this slack thread: https://openedx.slack.com/archives/C05NT2YN820/p1732135462177269

I'm hoping we'll write down the outcome of the discussion here soon.

Comment on lines 48 to 65
4. Container Version History
============================

This section defines the various lists of container's versions (author-defined, initial, and frozen) used to track the history of changes made to a container, allowing to view past versions and changes over time.

- Each container version holds different lists of members (author-defined, initial, and frozen) to support rollback operations and history tracking for the container.
- The author-defined list is the list of members that the author has defined for the version of the container.
- The author-defined list won't change for a specific container version even if its references get soft-deleted.
- The initial list is a copy of the author-defined list that has all versions pinned as they were at the time the container version was created.
- The initial list is immutable for a container version.
- The frozen list refers to the list of members at the time when the next version of the container is created.
- The author-defined list is used to show the content of a container version as the author specified it, the frozen list can be used for discard operations on a draft version and the initial-list is part of the history of evolution of the container.

Let's say a course author creates a unit version with three components, all using floating versions. Each component's latest version is V1. The author-defined list would include these three components, ordered as the author decided. The initial list would have the components pinned to V1 since that's the components' versions for this moment in time, while the frozen list would be empty until we create the next version for the unit.

Now, when the author creates a new version of the unit, for example, V2, we need to store the latest state of the container in the frozen list. This means pinning the latest versions of the components at that time, let's say V1, V2, and V3, respectively.

Next, imagine the course author creates a another unit version but uses pinned references for the components instead of floating versions, as they don't want to use the latest updates. In this case, the author-defined list, initial list, and frozen list would all be the same, as the component versions remain fixed. If we were to use different pinned versions, then a new unit version would be created instead.
Copy link
Member Author

@mariajgrimaldi mariajgrimaldi Dec 5, 2024

Choose a reason for hiding this comment

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

According to our latest discussion had here this must be changed to address:

  1. References to defined, initial and frozen must be dropped in favor of relaxing the requirement of "container versions should not reference soft-deleted children"
  2. Emphasize "only changes of the container itself generate a new version of the container," so NO external change (changes in container children) should create a new version of a container. If a container has invalid references to children, then it should be filtered out as needed.
  3. The only list remaining is the author defined list to keep references to the container children.

- Containers can reference a specific version of their members or be set to point to their latest versions. For instance, component V1 might be used in a unit instead of its latest version. The latest version of a member can be referenced by setting its version to ``None`` which consists of the chosen standard for this representation.
- A single member (publishable entity) can be shared by multiple containers, allowing for reuse of content across different containers. For instance, a component can be shared by multiple units.

4. Container Version History
Copy link
Member Author

Choose a reason for hiding this comment

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

During discussions, container members were usually referenced as "children", so I will change all references to members to children.

This section defines the rules for version control in containers, explaining when new versions are created based on changes to container structure or metadata.

- A new version is created if and only if the container itself changes (e.g., title, ordering of members, adding or removing members) and not when its members change (e.g., a component in a Unit is updated with new text). For instance, a new version of a unit is created when a component is removed, not when a new version of a component is created.
- When a shared member is soft-deleted in a another container, all containers referencing it should create a new version without the member. This new version will be the new draft version of the container. For example, suppose a component is shared between two units, if the component is soft-deleted independently, then we'd need to create a new version for both units sharing the component.
Copy link
Member Author

Choose a reason for hiding this comment

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

* References to defined, initial and frozen must be dropped in favor of relaxing the requirement of "container versions should not reference soft-deleted children"
* Emphasize "only changes of the container itself generate a new version of the container," so NO external change (changes in container children) should create a new version of a container. If a container has invalid references to children, then it should be filtered out as needed.
* The only list remaining is the author defined list to keep references to the container children.
* Container members were usually referenced as "children", so change all references to members to children.
- Children within a container are maintained in a specific order as an ordered list. E.g., components within a unit, or units within a subsection, are presented in a specific order.
- Containers represent their content hierarchy through a structure, like Course > Section > Subsection > Unit > Component, which defines parent-child relationships at each level.
- Containers can reference a specific version of their children or be set to point to their latest versions. For instance, component V1 might be used in a unit instead of its latest version. The latest version of a child can be referenced by setting its version to ``None`` which consists of the chosen standard for this representation.
- A single child (publishable entity) can be shared by multiple containers, allowing for reuse of content across different containers. For instance, a component can be shared by multiple units.
Copy link
Member Author

Choose a reason for hiding this comment

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

I think we need to be more specific about the unit, is the unit in a content library o within the context of a course?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
open-source-contribution PR author is not from Axim or 2U
Projects
Status: In Eng Review
Development

Successfully merging this pull request may close these issues.

4 participants