This is the proposed engineering implementation to integrate the Question Set Editor inside the Sourcing solution Project Creation workflow, based on this requirements document. (Confluence Document) and these mockups. (Google Slides)
Proposed Implementation
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 thecollectionCategories
array, which must be expanded to include the objects of typequestionSet
.
Code Block | ||
---|---|---|
| ||
// 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']);
}
|
...
language | json |
---|
...
1. Sourcing
- Project Creation
We will use the sunbird-collection-editor to create question sets as target objects.
We will add an
enableQuestionCreation
attribute to the editor configurationWe will implement changes to be able to emit question set metadata on exiting the question set creation page
2. Contribution
- Question Creation
We will use the sunbird-collection-editor to create new questions inside question sets.
We will add a new object type to editor configuration:
Question
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 use the sunbird-collection-editor to search and add new questions to question sets from the existing library.
We will add an
enableLibrary
attribute to the configurationWe will need to enhance the 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.
- Chapter List
In the creation-portal, we will have to enable changes to invoke the sunbird-collection-editor
and handle events emitted by it.
We will have to create new
Questions
and add them to theQuestionSet
hierarchyOnly for projects with target objects of type
QuestionSet
, we will implement opening thesunbid-collection-editor
on "Add from Library" being clicked - for the rest, the existingmvc-library
component will continue to be used.
3. Publish
We will use the sunbird-collection-editor to publish question sets.
We will have to use a combination of the
enableQuestionCreation
attribute andsessionContext
variables to display/hide the "Publish" option
We will make changes to the knowledge-platform to modify the publish pipeline for question sets.
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.
DB Changes: ProgramTable
column_name | datatype | postgresql command |
---|---|---|
targetcollectionprimarycategories | json array |
|
DB Changes: Configuration Table
Code Block | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
{ "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 | ||
---|---|---|
| ||
{ "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 | ||
---|---|---|
| ||
{
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 inconfig.collections
The
contentAdditionMode
will also be saved in the programconfig
The identifiers will be saved in
collectionIds
POST
content/program/v1/update
...
language | json |
---|
...
}, { "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 | ||
---|---|---|
| ||
{ "request":
{
"program_id": "3d5ddd30-bff0-11eb-8aa6-59968608366d",
"channel":"sunbird"
}
} |
...
' |
...
Modify the copyCollections
function as follows:
...
, |
...
|
...
|
...
Modify the publishProgram
function as follows:
Code Block | ||
---|---|---|
| ||
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 === 'new') {
res.copiedCollections = rspObj.result
}
res.copiedCollections = _.map(rspObj.result, (collection) => {
return collection.result.content_id;
});
}
} |
Contribution & Review
In case the
targetCollectionType
of the Project is"QuestionSet"
, thequestion-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.
...
Extract individual components from this repo and publish them as separate NPM packages
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');`
|