Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Objective

  1. The aim is that objects of type questionSet be used as target objects for sourcing projects without having to create them elsewhere.

  2. Objects of type question be contributed and reviewed individually to such sourcing projects.

Background

  1. Product requirements document. (Confluence Document)

  2. Product mockups. (Google Slides)

Proposed Implementation

1. Sourcing

Create New Project  : Define Scope and Schedule

  • The Program Scope tab (see: "Define Scope and Schedule" in [3] (Google Slide)) is to include the ability to select target collection/questionSet and target content type(s)/question type(s). The contents of the Select Target Collection dropdown are fetched from the collectionCategories array, which must be expanded to include the objects of type questionSet .

Code Block
languagejs
// Old Code
 setFrameworkDataToProgram() {

	this.collectionCategories = _.get(this.cacheService.get(this.userService.hashTagId), 'collectionPrimaryCategories'); 
	
	const channelCats = _.get(this.cacheService.get(this.userService.hashTagId), 'primaryCategories');
	
	this.programScope['targetPrimaryCategories'] = [];

	const channeltargetObjectTypeGroup = _.groupBy(channelCats, 'targetObjectType');

	if (this.enableQuestionSetEditor === 'true') {

	const questionSetCategories = _.get(channeltargetObjectTypeGroup, 'QuestionSet');

	this.programScope['targetPrimaryCategories']  = _.map(questionSetCategories, 'name');

	this.programScope['targetPrimaryObjects'] = questionSetCategories;

	}
	
// New Code
 setFrameworkDataToProgram() {

	this.tempCollectionCategories = _.get(this.cacheService.get(this.userService.hashTagId), 'collectionPrimaryCategories'); 
	
	const channelCats = _.get(this.cacheService.get(this.userService.hashTagId), 'primaryCategories');
	
	this.programScope['targetPrimaryCategories'] = [];

	const channeltargetObjectTypeGroup = _.groupBy(channelCats, 'targetObjectType');

	if (this.enableQuestionSetEditor === 'true') {

	const questionSetCategories = _.get(channeltargetObjectTypeGroup, 'QuestionSet');

	this.programScope['targetPrimaryCategories']  = _.map(questionSetCategories, 'name');

	this.programScope['targetPrimaryObjects'] = questionSetCategories;
	
	// Include QuestionSets inside collection categories
	this.collectionCategories = _.concat(this.tempCollectionCategories || [], this.programScope['targetPrimaryCategories']);

	}
	

...

languagejson

...

- Create Program

We will use the sunbird-collection-editor to create question sets as target objects.

  1. We will add an enableQuestionCreation attribute to the editor configuration

  2. We will implement changes to be able to emit question set metadata on exiting the question set creation page

  3. We will use a target_type of questionSets to show/hide the changed UI for creating a program with a target questionSet.

2. Contribution

- Question Creation

We will use the sunbird-collection-editor to create new questions inside question sets.

  1. We will add a new object type to editor configuration: Question

  2. We will implement requisite changes:

    • Editor: Accepting a question ID to initialise the editor

    • Header: Adding Send for review, Publish, Reject etc. buttons

    • Question: Only saving question and not updating the collection hierarchy on save etc.

- Add from Library

We will create a new add-from-library Angular library to add questions from library to question sets.
Scope:

- In release-4.4.0
1. We will add one new attribute to the collection and questionSet KP schemas: reusedContributions, which will contain the list of identifiers added to the collection via library

3. We will use this list of identifiers to detect whether a question has been added from library or not


- In release-4.3.0

  1. We will invoke this library from the portal for an existing question set.

  2. The question set and unit identifiers will be passed to the library, which will use the existing library and player components

  3. On returning/saving, the library will emit the question identifiers that have been added

  4. The portal will then make an update hierarchy call for the given section

  5. Caveat: We will only add questions to the section that was passed to the library to begin with.

  6. We will use the following components of the existing collection editor:

    • Library: We will need to enable the existing library components for objects of type Question

    • Quml Player: We will need to support previewing QuML questions in the library component etc.

- In future releases, we will integrate this library with mvc-library and sunbird-collection-editor

In the creation-portal, we will have to enable changes to invoke the sunbird-collection-editor and handle events emitted by it.

  1. Only for projects with target objects of type QuestionSet, we will implement opening the new library on "Add from Library" being clicked - for the rest, the existing mvc-library component will continue to be used.

3. Question Set Publish

We will use the sunbird-collection-editor to publish question sets.

  1. We will have to use a combination of the enableQuestionCreation attribute and sessionContext variables to display/hide the "Publish" option

We will make changes to the knowledge-platform to modify the publish pipeline for question sets.

  1. We will have to modify this logic to be able to publish question sets only with "Approved" questions, and not re-create questions when the question set is re-published.

  2. We will add acceptedContributions and rejectedContributions attributes in creation-portal question-set schema.

  3. If sourcing reviewer approves the question, we will add its identifier to acceptedContributions array and if question is rejected it will be added to rejectedContributions array.

  4. We are using autoCreatorV2 flink job to copy contents from sourcing to consumption. Here we will add a check condition to validate if acceptedContributions attribute exists or not in question-set metadata.
    - If attribute exist, we follow the new flow, in which we will remove the rejected questions from
    question-set hierarchy and save only with approved questions to consumption.
    - If attribute does not exist, we will use the existing flow and save all questions in question-set to
    consumption irrespective of questions approval and rejection.

DB Changes: ProgramTable

column_name

datatype

postgresql command

targetcollectionprimarycategories

jsonb

ALTER TABLE program ADD COLUMN targetCollectionPrimaryCategories jsonb;

DB Changes: Configuration Table

Code Block
languagesql
INSERT INTO "public"."configuration" ("key", "value", "status") 
VALUES ('programTargetObjectMap', 
'[ {
   "identifier":

...

"obj-cat:content-playlist_collection_all",

...


   "name":

...

"Content Playlist",

...


   "targetObjectType":

...

"Collection",

...


   "associatedAssetTypes":["Content"],
   "contentAdditionMode":["

...

Search"]
   },
   

...

{
    "identifier":

...

"obj-cat:demo-practice-question-set_questionset_all",
     "name":

...

"Demo Practice Question 

...

`Set",
     "targetObjectType":

...

"QuestionSet",
     "associatedAssetTypes":

...

["Question",

...

"QuestionSet"]

...

,

...


...

     "

...

contentAdditionMode":

...

["New"]
    },
    {
     "identifier":

...

"obj-cat:

...

digital-

...

textbook_collection_all",
     "name":

...

"

...

Digital 

...

Textbook",
     "targetObjectType":

...

"Collection",
     "associatedAssetTypes":

...

["Content"]

...

,

...


...

     "

...

contentAdditionMode":["Search"]
  

...

   

...

Based on the selected target collection value ( "Question Paper"or "Demo Practice Question Set" ) and the above mapping, the values in the Select Target Asset dropdown will be updated

...

A new variable may be introduced in the program definition, program.targetCollectionType, which may be one of ["Collection", "QuestionSet"] (Detailed at the end)

The category definition of the selected target collection may be fetched through the getCategoryDefinition function via the following API call:
POST /content/object/category/definition/v1/read?fields=objectMetadata,forms,name

Code Block
{ "request":
	{ "objectCategoryDefinition":
		{ 
			"objectType": "QuestionSet",
			"name": "Demo Practice Question Set",
			"channel": ""
		}
	}
}

Create New Project: Select/Create Objects

On advancing to the next tab:

  • A draft project will be created via the following API:
    POST content/program/v1/create

Code Block
languagejson
{ "request":
  {
	"name": "Test 1",
	"description": "Descr",
	"nomination_enddate": null,
	"shortlisting_enddate": null,
	"content_submission_enddate": "2021-06-06T18:29:59.000Z",
	"rewards": null,
	"target_collection_category": [ "Demo Practice Question Set" ],	
	"content_types": [], 
	"targetCollectionType": ["Question Set"],
	"targetprimarycategories": [
	  {
		"identifier": "obj-cat:practice-question-set_content_all",
		"name": "Practice Question Set",
		"targetObjectType": "Content"
	  }
],			
  }

For the “Search & Add” modal:

...

This will be enabled based on a contentAdditionMode property to be added to the configuration table, which will have an array of strings: search | add

...

This value will be read via an API call to the configuration/v1/search endpoint on loading this component with the selected primary category included in the request.

...

If search is to be enabled for the selected primary category, the list of collections of the required target collection category to display in the search will be fetched by making the following API call:
POST /composite/v1/search(No change)

Code Block
languagejson
{ "request":
	{ "filters":
		{ 
			"objectType": ["Collection"],
			"status":["Draft"],
			"subject": ["Mathematics"],
			"medium": ["English"],
			"gradeLevel": ["Class 4"],
			"primaryCategory":"Question Paper",
			"channel":"",
			"limit": 1000,
			"not_exists":["programId"]
		}
	}
}

...

In case of editing an already created draft project, this tab opens up on load. In that case, the category definition of the selected target collection (or questionSet) as well as a list of already created objects of that collection/question set category are to be populated. Both may be done via the same process as above, where the only difference is that the selected target collection can be retrieved from this.selectedTargetCollection, and the target collection type may be retrieved from this.programDetails.targetCollectionType

Create New Project: Add New

...

Display the “Add New” button only if the targetCollectionType === “Question Set” for the draft program

...

Navigate to the Question Set Editor on clicking the "Add New" button [6] (Google Slide)

...

Use the category definition of the selected target question set to modify the behaviour of the question set editor, using maxDepth.

A data property hideAddQuestion may be added to the routing config, to disable the "Create New" button on the Question Set Editor in this case

Code Block
languagejson
{
  path: 'edit/:programId', component: CreateProgramComponent, canActivate: [ProgramsService, AuthGuard], pathMatch: 'full',
  data: {
   ...
  },
  children: [
    {
      path: 'questionSet', component: QuestionSetEditorComponent, pathMatch: 'full',
      data: {
        hideHeaderNFooter: 'true',     
        hideAddQuestion: true,   
        telemetry: {
         ...
        }
      }
    }

...

The programId will be added to the created questionSet under a programId attribute

...

On "Save", redirect to the draft project creation screen with the created Question Set returned to the parent component and added to the tempQuestionSets array, to display in the selected list [8] (Google Slide)

Create New Project: Publish

...

On “Publish”, the existing workflow to publish the project will be followed with some modifications for projects with targetCollectionType === ‘Question Set’

  • The question sets added to the program will be saved to the program config object in config.collections

  • The contentAdditionMode will also be saved in the program config

  • The identifiers will be saved in collectionIds

POST content/program/v1/update

...

languagejson

...

},
     {
      "identifier":"obj-cat:professional-development-course_collection_all",
      "name":"Course",
      "targetObjectType":"Collection",
      "associatedAssetTypes":["Content"],
      "contentAdditionMode":["Search"]
      },
      {
       "identifier":"obj-cat:question-paper_collection_all",
        "name":"Question paper",
        "targetObjectType":"Collection",
        "associatedAssetTypes":["Content"],
        "

...

contentAdditionMode":

...

[

...

"Search"]
      

...

}

...


    

...

]

...

POST content/program/v1/publish or POST content/program/v1/unlist/publish
(depending on whether nominations have been enabled or not)

Code Block
languagejson
{ "request":
  {   
    "program_id": "3d5ddd30-bff0-11eb-8aa6-59968608366d",
    "channel":"sunbird"
  }
}

...

'

...

Modify the copyCollections function as follows:

...

,

...

 

...


  

...

Modify the publishProgram function as follows:

Code Block
languagejs
function publishProgram(req, response) {
  var data = req.body;
   model.program.findByPk(data.request.program_id)
  .then(function (res) {
    const cb = function(errObj, rspObj) {
      if (!errObj && rspObj) {
        res.copiedCollections = [];
        if (rspObj && rspObj.result) {
          if(rspObj.contentAdditionMode === 'add') {
            res.copiedCollections = rspObj.result
          }
          res.copiedCollections = _.map(rspObj.result, (collection) => {
          return collection.result.content_id;
          });
        }
 
  
  }
  1. Contribution & Review

    • In case the targetCollectionType of the Project is "QuestionSet", the question-list component remains the same. Upon creating or editing questions, the Question Set Editor will open directly on the create/edit question page.

      • For this, one approach is to show the existing question list component, and then redirect to the Question Set Editor for question creation/editing

      • Filtering logic will need to be added to the Question Set Editor package where the question list and the preview will render only for questions added by the current user

      • The other approach (outlined below) involves unbundling the Question Set Editor package to extract a single Question Editor component and using that for the contribution, preview and review user flows.

...

  1. Extract individual components from this repo and publish them as separate NPM package

  2. Include those individual components for creating/editing, previewing and reviewing questions

...

column_name

...

datatype

...

postgresql command

...

targetCollectionType

...

text array

...

ALTER TABLE program ADD COLUMN targetCollectionType text[];

...

questionSetIds

...

text array

...

'active');`