Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents

Legend

opaque_key { < settings-scoped field defaults > } { < settings-scoped fields > } { < content-scoped fields > }

Scenario

We have a library at version 6 with blocks W and X. Version 5 had an additional blocks Y and Z.

We have a course with a library_content block pointing at version 5 of the library.

  • It overrides the titles (display_name) for X and Y, but uses the default titles of W and Z.

  • It edits the capa content (data) for Y and Z, but uses the upstream content of W and X.

Here’s what the lib and course trees look like:

lib:O:L

Version 5

...

lb:O:L:problem:libBlockW { display_name: “title W” } { data: “www” }

...

lb:O:L:problem:libBlockX { display_name: “title X” } { data: “xxx” }

Table of Contents

Legend

  • opaque_key { < settings-scoped field defaults > } { < settings-scoped fields > } { < content-scoped fields > }

    • child_1_opaque_key

    • etc

Scenario

  • We have a course with a library_content block pointing at version 5 of the library, where version 6 is the latest

  • The course overrides the titles (display_name) for X and Y, but uses the default titles of W and Z.

  • It edits the capa content (data) for Y and Z, but uses the upstream content of W and X.

Here’s what the lib looks like:

  • lib:O:L

    • Version 5

      • lb:O:L:problem:libBlockW { display_name: “title W” } { data: “www” }

      • lb:O:L:problem:libBlockX { display_name: “title X” } { data: “xxx” }

      • lb:O:L:problem:libBlockY { display_name: “title Y” } { data: “yyy” }

      • lb:O:L:problem:libBlockZ { display_name: “title Z” } { data: “zzz” }

    • Version 6 {latest}

      • (contents not important)

And here’s what the course tree looks like:

  • course-v1:O+C+R

    • block-v1:O+C+R+type@chapter+block@...

      • block-v1:O+C+R+type@sequence+block@...

        • block-v1:O+C+R+type@vertical+block@...

          • block-v1:O+C+R+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

What does the user see for the blocks under myLCB? Unsurprisingly:

title W

www

override title X

xxx

override title Y

yyy_edit

title Z

zzz_edit

Curveball 1: Import/Export

We export course-v1:O+C+R and then import it into a new course run, course-v1-O+C+R2. Unfortunately, the library’s default settings don’t go into in the export!

Here’s what R2 looks like:

  • course-v1:O+C+R2

    • block-v1:O+C+R2+type@chapter+block@...

      • block-v1:O+C+R2+type@sequence+block@...

        • block-v1:O+C+R2+type@vertical+block@...

          • block-v1:O+C+R2+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ data: “www” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")} { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")} { display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ data: “zzz_edit” }

What does the user see now? Well, wherever a title override isn’t set, they see the the ProblemBlock’s provided default title:

Problem

www

override title X

xxx

override title Y

yyy_edit

Problem

zzz_edit

How do we work around this in V1 libs?

We re-load the blocks from the library at the proper version (5). This gives us our defaults back, but it does blow away any content edits :(

R2 would then look like this:

  • course-v1:O+C+R2

    • block-v1:O+C+R2+type@chapter+block@...

      • block-v1:O+C+R2+type@sequence+block@...

        • block-v1:O+C+R2+type@vertical+block@...

          • block-v1:O+C+R2+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")} { display_name: “title Y” } { display_name: “override title Y” } { data: “yyy” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz” }

And the user would see the right titles, but no content edits:

title W

www

override title X

xxx

override title Y

yyy

title Z

zzz

How do we want to work around this in V2 libs?

Well, first of all the current V2 content_libraries API does not support loading blocks for old library versions. We could add support for that, but we’re not sure it’s the right approach.

Instead, we’d rather add defaults to the OLX export, so that the export comes back with defaults intact, and removes the need to re-load library blocks, thus preserving library edits 🎉 The structure is now:

  • course-v1:O+C+R2

    • block-v1:O+C+R2+type@chapter+block@...

      • lb:O:L:problem:libBlockX { display_name: “title W” } { data: “www” }

      • block-v1:O+C+R2+type@sequence+block@...

        • block-v1:O+C+R2+type@vertical+block@...

          • block-v1:O+C+R2+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y”

        } { data: “yyy”
            • }

        lb:O:L:problem:libBlockZ
            • { display_name:

        “title Z”
            • “override title Y” } { data:

        “zzz”
            • “yyy_edit” }

      Version 6 {latest}

            • block-v1:O+C+R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:

        libBlockY
            • libBlockZ")}{ display_name: “title

        X”
            • Z” }{ data:

        “xxx” }
      • (Y was removed)

      • (Z was removed)
            • “zzz_edit” }

Yielding:

title W

www

override title X

xxx

override title Y

yyy_edit

title Z

zzz_edit

OPEN QUESTION… how will this work for courses which were exported before the v1/v2 transition?

Curveball 2: Duplication

Back to the original course, let’s say the user duplicates LCB. The standard handling of “duplicate a block” would randomly generating id strings for its children:

  • course-v1:O+C+R

    • block-v1:O+C+R+type@chapter+block@...

      • block-v1:O+C+R+type@sequence+block@...

        • block-v1:O+C+R+type@vertical+block@...

          • block-v1:O+C+R+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

What does the user see for the blocks under myLCB? Unsurprisingly:

title W

www

override title X

xxx

override title Y

yyy_edit

title Z

zzz_edit

Curveball 1: Import/Export

...

            • “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R

...

Here’s what R2 looks like:

  • course-v1:O+C+R2
            • +type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

          • block-v1:O+C+

      R2
          • R+

      type@chapter+block@...
          • type@library_content+block@dupeLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+

        R2
            • R+

        type@sequence+block@...
            • type@problem+block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead1{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+

          R2
            • R+

          type@vertical+block@...
            • type@problem+block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead2{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+

            R2
            • R+

            type@library_content
            • type@problem+

            block@myLCB
            • block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead3{

            source_library_id: “lib:O:L”, source_library_version: “5”
            • display_name: “title Y” } { display_name: “override title Y” } { data: “yyy }

            • block-v1:O+C+R2R+type@problem+block@block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead4{ DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ data: “www” }display_name: “title Z” }{ data: “zzz” }

That would be fine, until the course author updates the library’s source version, at which point the children with randomly-generated IDs would be thrown away in favor of new children with library-block-id-derived IDs. Title overrides and any student state would be lost:

  • course-v1:O+C+R

    course-v1:O+C+R2
    • block-v1:O+C+

      R2

      R+

      type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")} { display_name: “override title X” } { data: “xxx” }

      type@chapter+block@...

      • block-v1:O+C+

        R2+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")} { display_name: “override title Y” } { data: “yyy_edit” }

        R+type@sequence+block@...

        • block-v1:O+C+

          R2

          R+

          type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ data: “zzz_edit” }

What does the user see now? Well, wherever a title override isn’t set, they see the the ProblemBlock’s provided default title:

Problem

www

override title X

xxx

override title Y

yyy_edit

Problem

zzz_edit

How do we work around this in V1 libs?

We re-load the blocks from the library at the proper version (5). This gives us our defaults back, but it does blow away any content edits :(

R2 would then look like this:

        • type@vertical+block@...

          • block-v1:O+C+R+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+

      R2
            • R+

      type@chapter+block@...
      • block-v1:O+C+R2+type@sequence+block@...
            • type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+

          R2
            • R+

          type@vertical+block@...
          • block-v1:O+C+R2+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5”
            • type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R2R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockWlibBlockZ")}{ display_name: “title W” Z” }{ data: “www” : “zzz_edit” }

          • block-v1:O+C+R+type@library_content+block@dupeLCB { source_library_id: “lib:O:L”, source_library_version: “6” }

            • block-v1:O+C+R2R+type@problem+block@{DERIVE_ID("myLCBdupeLCB", "lb:O:L:problem:libBlockXlibBlockW")}{ display_name: “title X” W” } { display_name: “override title X” } { data: “xxx” }data: “www” } # R.I.P. STUDENT STATE

            • block-v1:O+C+R2R+type@problem+block@{DERIVE_ID("myLCBdupeLCB", "lb:O:L:problem:libBlockYlibBlockX")}{ display_name: “title Y” X” }{ display_name: “override title Y” } { data: “yyy” }“xxx” } # R.I.P. STUDENT STATE

            • block-v1:O+C+R2R+type@problem+block@{DERIVE_ID("myLCBdupeLCB", "lb:O:L:problem:libBlockZlibBlockY")}{ display_name: “title Z” Y” }{ data: “zzz” }

And the user would see the right titles, but no content edits:

title W

www

override title X

xxx

override title Y

yyy

title Z

zzz

How do we want to work around this in V2 libs?

Well, first of all the current V2 content_libraries API does not support loading blocks for old library versions. We could add support for that, but we’re not sure it’s the right approach.

...

            • “yyy” } # R.I.P. STUDENT STATE

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("dupeLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz” } # R.I.P. STUDENT STATE

How does V1 handle this? Well, it tells the duplication code to not duplicate the library_content block’s children:

  • course-v1:O+C+R2R

    course-v1:O+C+R
    • block-v1:O+C+R2R+type@chapter+block@...

      block-v1:O+C+R2+type@sequence

      +block@...

      block-v1:O+C+R2+type@vertical+block@...

      block-v1:O+C+R2+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

      • block-v1:O+C+

        R2

        R+

        type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

        type@sequence+block@...

        • block-v1:O+C+R+type@vertical+block@...

          • block-v1:O+C+

            R2

            R+

            type@problem

            type@library_content+

            block@

            block@myLCB{

            DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx”

            source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R2R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockYlibBlockW")}{ display_name: “title Y” W” } { display_name: “override title Y” } { data: “yyy_edit” “www” }

            • block-v1:O+C+R2R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZlibBlockX")}{ display_name: “title Z” X” } { data: “zzz_edit” }

Yielding:

title W

www

override title X

xxx

override title Y

yyy_edit

title Z

zzz_edit

Todo

Consider: how will this work for courses which were exported before the v1/v2 transition?

Curveball 2: Duplication

TODO

bad

            • display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R+

      type@chapter+block@...
            • type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R+

        type@sequence+block@...
        • block-v1:O+C+R+type@vertical+block@...
            • type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

          • block-v1:O+C+R+type@library_content+block@myLCBblock@dupeLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }(no children yet)

Then, it reloads the blocks from the library. This avoids the student-state-loss risk described above, but it does, annoyingly, blow away any content edits. This behavior is essentially the same as what happens when you import a V1 library content block.

  • course-v1:O+C+R

    • block-v1:O+C+R+

      type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

      type@chapter+block@...

      • block-v1:O+C+R+

        type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }

        type@sequence+block@...

    course-v1:O+C+R

    • block-v1:O+C+R+type@chapter+block@...

      • block-v1:O+C+R+type@sequence+block@...
        • block-v1:O+C+R+

          type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

          type@vertical+block@...

          • block-v1:O+C+R+type@library_content+block@dupeLCBblock@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead1block@{ display_name: “title W” } { data: “www” }block-v1:O+C+R+type@problem+block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead2{ display_name: “title X” DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “override title X” “title W” } { data: “xxx” “www” }

            • block-v1:O+C+R+type@problem+block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead3block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title Y” X” } { display_name: “override title Y” X” } { data: “yyy “xxx” }

            • block-v1:O+C+R+type@problem+block@deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead4block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Z” Y” } { data: “zzz” }

v1

            • display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R+

          type@vertical+block@...
            • type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

          • block-v1:O+C+R+type@library_content+block@myLCBblock@dupeLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCBdupeLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCBdupeLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCBdupeLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy _edit” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCBdupeLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

          • block-v1:O+C+R+type@library_content+block@dupeLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • (no children yet)

v1 post refresh

            • “zzz” }

For V2, it’d be really nice if we could duplicate settings and content and correctly derive the content IDs:

  • course-v1:O+C+R

    • block-v1:O+C+R+type@chapter+block@...

      • block-v1:O+C+R+type@sequence+block@...

        • block-v1:O+C+R+type@vertical+block@...

          • block-v1:O+C+R+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz_edit” }

          • block-v1:O+C+R+type@library_content+block@dupeLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

    course-v1:O+C+R

    • block-v1:O+C+R+type@chapter+block@...

      • block-v1:O+C+R+type@sequence+block@...

        • block-v1:O+C+R+type@vertical+block@...

          • block-v1:O+C+R+type@library_content+block@myLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("dupeLCB", "lb:O:L:problem:libBlockW")}{ display_name: “title W” } { data: “www” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("dupeLCB", "lb:O:L:problem:libBlockX")}{ display_name: “title X” } { display_name: “override title X” } { data: “xxx” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("dupeLCB", "lb:O:L:problem:libBlockY")}{ display_name: “title Y” } { display_name: “override title Y” } { data: “yyy_edit” }

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("dupeLCB", "lb:O:L:problem:libBlockZ")}{ display_name: “title Z” }{ data: “zzz” }

desired:

            • block-v1:O+C+R+type@problem+block@{DERIVE_ID("myLCB", )}{ display_name: “title Z” }{ data: “zzz_edit” }

OPEN QUESTION… how do we do this? Well, if we knew the right derived keys, we could set them ourselves! But that requires knowing what the original source library block usage keys were.

Two different ideas for figuring out the old usage keys

  1. Add a field to LibraryContentBlock that maps the DERIVE_ID(…) output back to the original library id. Essentially:

    • Code Block
      languagepy
      class LibraryContentBlock(XBlock):
          ...
          # Values are: {
          #     DERIVE_ID("dupeLCB", "lb:O:L:problem:libBlockW"): "lb:O:L:problem:libBlockW",
          #     DERIVE_ID("dupeLCB", "lb:O:L:problem:libBlockX"): "lb:O:L:problem:libBlockW"

...

    • ,
          #     DERIVE_ID("

...

    • dupeLCB", "lb:O:L:problem:

...

    • libBlockY")

...

    • : "lb:O:L:problem:libBlockW",
          #     DERIVE_ID("

...

    • dupeLCB", "lb:O:L:problem:

...

    • libBlockZ")

...

block-v1:O+C+R+type@library_content+block@dupeLCB { source_library_id: “lib:O:L”, source_library_version: “5” }

...

problem: DERIVE_ID is a one-way function.

do we need a mapping back to the original usage keys?

...

    • : "lb:O:L:problem:libBlockW",
          # }
          child_usage_keys_to_library_usage_keys = Dict(...)
          ...
    • We would need to gracefully handle old blocks which don’t have this field set.

  1. Upon duplication of a library_content block, load up the blocks from the old version of the library, run DERIVE_KEY on each of those, and compare the results to the library content block’s children:

    • Code Block
      languagepy
      child_usage_keys_to_library_usage_keys: dict[UsageKey, UsageKey] = {}
      for source_lib_block_key in get_library_block_usage_keys(library=lcb.source_library_id, version=lcb.source_library_version):
          child_usage_keys_to_library_usage_keys = DERIVE_KEY(lcb.usage_key.block_id, source_lib_block_key)

TODO