WBS for Course Content Outline and Course Unit pages

WBS for Course Content Outline and Course Unit pages

Low-level WBS for Course Content Outline and Course Unit pages

 

 

High-level analysis of the page Course Content Outline

From legacy to MFE + API (w/o functional changes).

 

Following the example we’ll use frontend-driven-development here.

 

Steps

  1. Understand (list) the feature set.

    1. extract features - split legacy UI to functional pieces

    2. discover optional implicit functions

  2. Define additional configuration

    1. waffle course flag(s) for feature(s) - rollout period

  3. Extend MFE

    1. feature/sub-features placement (codebase hierarchy, naming, etc.)

    2. routing (we want to stick to legacy paths as close as possible - to keep user experience not affected: bookmarks, etc.)

    3. state management (shape, UI, data, etc.)

    4. data required (shape, potential API endpoints, etc.) - API contract

    5. main UI building blocks (basic presentational components)

    6. unit tests

  4. Compose final API contract

    1. endpoints list with shaped data set

  5. Implement API (BFF)

    1. create backend DRF wrappers around legacy utils to satisfy requested API contract

    2. unit tests

  6. Internatioanalization (I18N)

  7. Update documentation

    1. MFE README

    2. CMS (Platform) documentation


STEP 1: Feature Set

 

Legacy context:

Feature context: course

Navigation: CMS > Content > Outline

Page path: <STUDIO-HOST>/course/course-v1:edX+DemoX+Demo_Course

Page decomposition

Page route

Dedicated route is already present.

Page layout

  • top bar (full width)

    • heading (left)

    • tool bar - page actions (right)

      • action-button1

      • … …

      • action-buttonN

  • content (full width)

    • main section (left)

      • status bar

        • attributes

          • attr1

          • … …

          • attrN

        • highlights

      • outline

        • empty course

        • section1

        • … …

        • sectionN

    • aside section (right)

      • info-item1

      • … …

      • info-itemN

 


Functions

  1. Additional functions:

    1. Heading (A)

    2. Tool Bar (B)

      1. New section creation (“New Section” button) - duplicated action (see: 2.a.i.1)

      2. Course reindex initiation (“Reindex“ button)

      3. Expand/collapse all sections (corresponding wording button)

      4. Learning MFE initial unit link (“View Live“ button)

    3. Status Bar (C)

      1. Course Schedule link (“Start Date“ clickable value)

      2. Pacing Type (passive course type)

      3. Checklists (out of scope?)

      4. Course Highlight Emails

        1. Activation “Enable Now“ button

        2. External documentation link (“Learn more“ link) - configurable?

    4. Side Bar (D)

      1. “Creating your course organisation” static section

      2. “Reorganising your course” static section

        1. External documentation link (“Learn more about the course outline“ button) - configurable?

      3. “Setting release dates and grading policies” static section

        1. External documentation link (“Learn more about grading policy settings”) - configurable?

      4. “Changing the content learners see” static section

        1. External documentation link (“Learn more about content visibility settings“) - configurable?

    5. Feedback Messages

      1. “Sav“

  2. Course structure manipulation:

    1. Outline (E)

      1. List sections

        1. Empty course (initial state)

          1. New Section - creation

        2. Sections list

        3. Loader

      2. Section actions (red)

        1. New Section - creation (j)

        2. Delete Section - removal (f)

        3. Duplicate Section - cloning (e)

        4. Configure Section - configuration (d)

        5. Reorder Section - movement (g)

        6. Expand/Collapse Section - appearance (a)

        7. Rename Section - edit (b, c)

        8. Section Highlights - description (i)

        9. Publish Section (h)

      3. Subsection actions (yellow)

        1. New Subsection - creation (h)

        2. Delete Subsection - removal (f)

        3. Duplicate Subsection - cloning (e)

        4. Configure Subsection - configuration (d)

        5. Reorder Subsection - movement (g)

        6. Expand/Collapse Subsection - appearance (a)

        7. Rename Subsection - edit (b, c)

        8. Publish Subsection (h)

      4. Unit actions (green)

        1. New Unit - creation (h)

        2. Delete Unit - removal (e)

        3. Duplicate Unit - cloning (d)

        4. Configure Unit - configuration (c)

        5. Reorder Unit - movement (f)

        6. Rename Unit - edit (a, b)

        7. Publish Unit (g)

    2. Feedback (F)

      1. “Saving…“ | “Adding…“ | … (during any action processing)

      2. “Error“ (API errors, etc.)

      3. Page alerts (example: Course has been successfully reindexed.)

  3. Implicit functions

 

 

Features hierarchy (see ADR #2 for details)

Features implementation description

Feature

Responsibility

State

Data

Presentational components

Feature

Responsibility

State

Data

Presentational components

OUTLINE

(top page level)

  • hosts sub-features

  • implements page skeleton

  • displays alerts? (example: course index succeeded)

State:

// "courseOutline" state slice // courseOutlineReducer "subroot" reducer ...

Actions:

Selectors:

Template context (legacy) - 'course_outline.html'

cms/djangoapps/contentstore/views/course.py#course_handler cms/djangoapps/contentstore/views/course.py#course_index --- { 'language_code': request.LANGUAGE_CODE, 'context_course': course_module, 'lms_link': lms_link, 'sections': sections, 'course_structure': course_structure, 'initial_state': course_outline_initial_state(locator_to_show, course_structure) if locator_to_show else None, 'rerun_notification_id': current_action.id if current_action else None, 'course_release_date': course_release_date, 'settings_url': settings_url, 'reindex_link': reindex_link, 'deprecated_blocks_info': deprecated_blocks_info, 'notification_dismiss_url': reverse_course_url( 'course_notifications_handler', current_action.course_key, kwargs={ 'action_state_id': current_action.id, }, ) if current_action else None, 'frontend_app_publisher_url': frontend_app_publisher_url, 'mfe_proctored_exam_settings_url': get_proctored_exam_settings_url(course_module.id), 'advance_settings_url': reverse_course_url('advanced_settings_handler', course_module.id), 'proctoring_errors': proctoring_errors, }

New API

Is there any global non-feature-specific data?

  • main layout container (outline page)

course-details

  • displays course start date (if set)

  • allows navigation to course start date configuration

  • displays course pacing type

Shape:

{ "start_date": <course-start-date>, "pacing_type": <course-pacing-type>, }

Actions:

 

 

course-checklists

out-of-scope?

Shape:

Actions:

 

 

course-highlights

  • allows section(s) highlights creation/editing

  • displays section highlights number

  • allows highlights emails scheduled sending

  • allows external documentation link configuration

 

See OeX documentation.

Shape:

{ "editor_is_open": false, "documentation_link": <web-link>, "emails_activated": false, "highlights": { "section_id": [ { "text": <highlight1-content> }, { "text": <highlight2-content> } ], "section_id": [], } }

Actions:

course-highlights.editor.opened { "section": <section_id> } course-highlights.editor.closed {} course-highlights.emails.activated {} course-highlights.save.started { "section": <section_id>, "highlights": [ { "text": <highlight1-content> }, { "text": <highlight2-content> } ] }

Selectors:

TBD

Enable “Course Highlight Emails“ (legacy)

POST <CMS>/xblock/block-v1:RG+02+101+type@course+block@course { publish: "republish", metadata: { highlights_enabled_for_messaging: true } } --- JSON { "id": "block-v1:RG+02+101+type@course+block@course", ... "data": null, "metadata": { ... "highlights_enabled_for_messaging": true, ... } }

Fetch block outline data (legacy)

GET <CMS>/xblock/outline/<course-locator> --- JSON { "id": "block-v1:RG+02+101+type@course+block@course", "display_name": "CMS MFE", "category": "course", "has_children": true, ... "highlights_enabled_for_messaging": true, "highlights_enabled": true, "highlights_preview_only": false, "highlights_doc_url": "http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/open-release-olive.master/developing_course/course_sections.html#set-section-highlights-for-weekly-course-highlight-messages", ... }

New API

GET <CMS-API>/highlights/<course_id> --- JSON { "highlights_enabled_for_messaging": <bool>, "highlights_enabled": <bool>, "highlights_preview_only": <bool>, "highlights_doc_url": <URI>, ... } POST <CMS-API>/highlights/ { "course_id": <course_id>, "section_id": <section_id>, "highlights": [ { "text": <highlight1-content> }, { "text": <highlight2-content> } ] } --- JSON { ... }
  • section card widget/button

    • displays highlights number

    • opens modal editor

  • section editor (modal)

    • edit 5 text fields

    • save data to API

  • status-bar widget

    • activates emails

    • holds docs link

section-create

  • allows new section creation

  • provides:

    • toolbar action button

    • empty content button

    • content button

  • dispatches footer feedback

  • dispatches global error

 

Shape:

{ in_progress: false, }

Actions:

// notation is abstract: course.section.create { "after": "section_id", (optional) } course.section.creation.started {} course.section.creation.succeed {} course.section.creation.failed {} dispatch: alert.show { "type": "error" "message": <text> } dispatch: feedback.show { "text": "Adding", }

 

Selectors:

TBD

Create new block (legacy)

POST <CMS>/xblock/ { "parent_locator":"block-v1:RG+02+101+type@course+block@course", "category":"chapter", "display_name":"Section" } --- JSON { "locator": "block-v1:RG+02+101+type@chapter+block@9cf00a10306e45e48e3bcce89bccd818", "courseKey": "course-v1:RG+02+101" }

Fetch course outline data (legacy)

GET <CMS>/xblock/outline/block-v1:RG+02+101+type@course+block@course --- JSON { "id": "block-v1:RG+02+101+type@course+block@course", "display_name": "CMS MFE", "category": "course", "has_children": true, ... "child_info": { "category": "chapter", "display_name": "Section", "children": [ { "id": "block-v1:RG+02+101+type@chapter+block@9cf00a10306e45e48e3bcce89bccd818", "display_name": "Section", "category": "chapter", "has_children": true, "edited_on": "Jun 29, 2023 at 10:19 UTC", ... other data } ] }, ... }

New API

NOTE: even if there is already a new API for course parts creation we implement a dedicated Outline BFF API (which should reuse available funcs) POST <CMS-API>/course/section/create { "parent_id": <course_id>, "include_after_id": <section_id>, (optional, default: last) "display_name":<section-name"> (optional, default: "Section") } --- JSON { "locator": "block-v1:RG+02+101+type@chapter+block@9cf00a10306e45e48e3bcce89bccd818", "display_name": "Section", }

 

  • new section button (toolbar)

  • new section button (empty outline)

  • new section button (outline bottom)

  • footer alert (“Adding“)

  • global error alert (“<text>“)

course-index

  • allows course search reindexing

  • provides:

    • action button (conditionally? permissions)

      • hidden/deactivated?

      • must respect FEATURES['ENABLE_COURSEWARE_INDEX']

  • dispatches footer feedback

  • dispatches success alert

  • dispatches global error

Shape:

{ indexing: false, enabled: false, }

Actions:

// abstract notation: <course-indexing-started> { "course": <course-id>, } <course-indexing-finished> { "course": <course-id>, } [on <course-indexing-finished>] <display-alert-success> { "icon": "fa-bullhorn", "title": "Course Index", "message": <user_message> }

Recreate course search index (legacy)

GET <CMS>/course/course-v1:RG+02+101/search_reindex --- JSON { "user_message": "Course has been successfully reindexed." }

New API

// Initial state: GET <CMS-API>/search-index/ --- JSON { "enabled": true, } POST <CMS-API>/search-index/reindex { "course_id": <course_id>, } --- JSON { "error": <bool>, "feedback": "Course has been successfully reindexed.", ... }
  • toolbar action button

learning-link

(in progress)

  • allows navigation to LMS (new tab) in different contexts:

    • course

    • specific section

    • specific subsection

    • specific unit

  • provides toolbar action button

Shape:

{ ... }

Actions:

learning.link.navigated { "block_id": <block_id>, "target": <lms-jump-to-url>, "title": <title-text>, } possibly, tracking events

Template context (legacy)

# target URL example: <LMS>/courses/course-v1:RG+02+101/jump_to/block-v1:RG+02+101+type@course+block@course

New API

// Fetch LMS course live view (new) GET <CMS-API>/learning-link/?course_id=<course_id> --- JSON { <block_id>: { "target": <lms-jump-to-url>, "title": <title-text>, } }
  • toolbar action button

TOOLBAR

  • implements section layout

  • uses:

    • section-create-feature

    • course-index-feature

    • content.collapse-all-toggle-feature

      • possibly should be extracted

 

Shape:

Actions:

Selectors:

Template context (legacy) - 'course_outline.html'

cms/djangoapps/contentstore/views/course.py#course_index --- { 'language_code': request.LANGUAGE_CODE, 'context_course': course_module, 'lms_link': lms_link, 'sections': sections, 'course_structure': course_structure, 'initial_state': course_outline_initial_state(locator_to_show, course_structure) if locator_to_show else None, 'rerun_notification_id': current_action.id if current_action else None, 'course_release_date': course_release_date, 'settings_url': settings_url, 'reindex_link': reindex_link, 'deprecated_blocks_info': deprecated_blocks_info, 'notification_dismiss_url': reverse_course_url( 'course_notifications_handler', current_action.course_key, kwargs={ 'action_state_id': current_action.id, }, ) if current_action else None, 'frontend_app_publisher_url': frontend_app_publisher_url, 'mfe_proctored_exam_settings_url': get_proctored_exam_settings_url(course_module.id), 'advance_settings_url': reverse_course_url('advanced_settings_handler', course_module.id), 'proctoring_errors': proctoring_errors, }

New API

Is there any global non-feature-specific data?

  • LMS view live URL?

  • toolbar section

STATUSBAR

  • implements section layout

  • uses:

    • course-details-feature

    • course-checklists-feature

    • course-highlights-feature

    •  

 

Shape:

Actions:

Template context (legacy)

  • course start date

  • course pacing type

Fetch course attributes

reuse course data (selectors)

  • main layout

ASIDE

  • hosts sub-features

  • implements section layout

 

Shape:

Actions:

 

  • main layout

FEEDBACK

  • displays some outline action is being processed (page bottom right)

 

Shape:

{ "message": <Saving...> }

Actions:

<outline.feedback.show> { "message": <Saving...> } <outline.feedback.hide> {}

No API data.

  • ? possibly it won’t be a 1:1 implementation