Background:

inQuiry need to enable multi-language support for Questions & QuestionSets where data like question content (body), hints, instructions can be stored in multiple language. So that user can select the language of their choice (user may select one or multiple language) during creation & consumption.


Problem Statement:

objectType

metadata

Current Data Type (v1 api)

Expected Data Type (v2 api)

Comment

QuestionSet

instructions

object

object

QuestionSet

feedback

object

object

Question

body

string

object

Question

answer

string

object

Question

instructions

object

object

Question

hints

array of string

object

Question

feedback

object

object

Question

solutions

array of object

object

Question

interactions

object

object

Solution:

Publicly Exposed API

Service Level API’s

qumlVersion

compatibilityLevel

v1 api’s

v4 api’s

supports 1.0 but not stamped in data.

5

v2 api’s

v5 api’s

supports 1.1 onwards.

6

No Static Migration:

Pros:

  1. Old version of questionset-editor & mobile app works fine with v1 api.

  2. Once older questions edited using v2 editor and api's, it will be migrated to v2 data model. So end user (consumption) won’t be affected on immediate basis and at that point old mobile app will not be able to discover the question (comaptibilityLevel will be upgraded to maintain backward compatibility of Sunbird-Ed mobile app).

Cons:

  1. Platform need to understand all multi-language data (including editorState) and transform it to v2 api format for every read & list api call.

  2. API Performance will be affected (specially question list api). To improve performance, cache will be implemented for list api to hold data post data transformation (currently no cache implementation present for list api).

  3. Once the data is migrated to v2 data model, old player will not be able to discover the question (comaptibilityLevel will be upgraded).

Static Migration:

Pros:

  1. Consumption api’s (question read & list api) doesn't need any run-time data transformation. So api performance won’t be affected.

Cons:

  1. old mobile app won't be able to render existing questions for online consumption.

  2. old editor also start breaking even for v1 api’s

API Specification:

Question Create API:

Sample Input for single language:

{
  "name": "Name of the question",
  "code": "unique code of the question",
  "mimeType": "application/vnd.sunbird.question",
  "primaryCategory": "Multiple Choice Question",
  "interactionTypes": ["choice"],
  "qumlVersion": 1.1,
  "media": [
    {
      "id": "do_2137498365362995201237",
      "type": "image",
      "src": "/assets/public/content/assets/do_2137498365362995201237/tea.jpeg",
      "baseUrl": "https://dev.inquiry.sunbird.org"
    }
  ],
  "body": "<div class='question-body' tabindex='-1'><div class='mcq-title' tabindex='0'><p><span style=\"background-color:#ffffff;color:#202124;\">Which of the following crops is a commercial crop?</span></p></div><div data-choice-interaction='response1' class='mcq-vertical'></div></div>",
  "answer": "<p>Barley</p>",
  "instructions": {
    "language_code": "<div>...</div>"
  },
  "hints": {
    "hint_1": "<div>...</div>",
    "hint_2": "<div>...</div>"
  },
  "feedback": {
    "feedback_1": "<div>...</div>",
    "feedback_2": "<div>...</div>"
  },
  "solutions": {
    "solution_1": "solution 1 html string",
    "solution_2": "solution 2 html string"
  },
  "interactions": {
    "response1": {
      "type": "choice",
      "options": [
        {
          "label": "<p>Wheat</p>",
          "value": "w"
        },
        {
          "label": "<p>Barley</p>",
          "value": "b"
        },
        {
          "label": "<p>Maize</p>",
          "value": "m"
        },
        {
          "label": "<p>Tea</p>",
          "value": "t"
        }
      ],
      "validation": {
        "required": "Yes"
      }
    }
  },
  "responseDeclaration": {
    "response1": {
      "cardinality": "single",
      "type": "integer",
      "correctResponse": {
        "value": 3
      },
      "mapping": [
        {
          "value": 2,
          "score": 0.5
        },
        {
          "value": 1,
          "score": 0.25
        }
      ]
    }
  },
  "outcomeDeclaration": {
    "maxScore": {
      "cardinality": "single",
      "type": "integer",
      "defaultValue": 3
    }
  },
  // any additional properties goes here. e.g: framework, createdBy, etc..
}

Sample Input for multi-language (default input):

{
  "name": "Name of the question",
  "code": "unique code of the question",
  "mimeType": "application/vnd.sunbird.question",
  "primaryCategory": "Multiple Choice Question",
  "interactionTypes": ["choice"],
  "qumlVersion": 1.1,
  "media": [
    {
      "id": "do_2137498365362995201237",
      "type": "image",
      "src": "/assets/public/content/assets/do_2137498365362995201237/tea.jpeg",
      "baseUrl": "https://dev.inquiry.sunbird.org"
    }
  ],
  "body": {
    "language_code": "<div class='question-body' tabindex='-1'><div class='mcq-title' tabindex='0'><p><span style=\"background-color:#ffffff;color:#202124;\">Which of the following crops is a commercial crop?</span></p></div><div data-choice-interaction='response1' class='mcq-vertical'></div></div>"
  },
  "answer": {
    "language_code": "<p>Barley</p>"
  },
  "instructions": {
    "language_code": "<div>...</div>"
  },
  "hints": {
    "hint_1": {
      "language_code": "<div>...</div>"
    },
    "hint_2": {
      "language_code": "<div>...</div>"
    }
  },
  "feedback": {
    "feedback_1": {
      "language_code": "<div>...</div>"
    },
    "feedback_2": {
      "language_code": "<div>...</div>"
    }
  },
  "solutions": {
    "solution_1": {
      "language_code": "<div>...</div>"
    },
    "solution_2": {
      "language_code": "<div>...</div>"
    }
  },
  "interactions": {
    "response1": {
      "type": "choice",
      "options": [
        {
          "label": {
            "language_code": "<p>Wheat</p>"
          },
          "value": 1
        },
        {
          "label": {
            "language_code": "<p>Barley</p>"
          },
          "value": 2
        },
        {
          "label": {
            "language_code": "<p>Maize</p>"
          },
          "value": 3
        },
        {
          "label": {
            "language_code": "<p>Tea</p>"
          },
          "value": 4
        }
      ],
      "validation": {
        "required": "Yes"
      }
    }
  },
  "responseDeclaration": {
    "response1": {
      "cardinality": "single",
      "type": "integer",
      "correctResponse": {
        "value": 3
      },
      "mapping": [
        {
          "value": 2,
          "score": 0.5
        },
        {
          "value": 1,
          "score": 0.25
        }
      ]
    }
  },
  "outcomeDeclaration": {
    "maxScore": {
      "cardinality": "single",
      "type": "integer",
      "defaultValue": 3
    }
  },
  // any additional properties goes here. e.g: framework, createdBy, etc..
}

Success Response (200):

{
    "id": "api.question.create",
    "ver": "5.0",
    "ts": "2023-01-30T19:43:43ZZ",
    "params": {
        "resmsgid": "ca1ed6b6-994d-487c-8ecd-0ca164a84c94",
        "msgid": null,
        "err": null,
        "status": "successful",
        "errmsg": null
    },
    "responseCode": "OK",
    "result": {
        "identifier": "do_2137224832856555521270",
        "versionKey": "1675107822985"
    }
}

Question Read API:

Request:

curl --location --request GET 'https://dev.inquiry.sunbird.org/api/question/v2/read/do_213688205764337664161?mode=edit&fields=answer,instructions' \
--header 'Authorization: {{api_key_new}}'

Response:

{
  "id": "api.question.read",
  "ver": "5.0",
  "ts": "2023-01-30T03:28:23ZZ",
  "params": {
    "resmsgid": "db23eb9e-207c-488c-a45a-7da36a2084ee",
    "msgid": null,
    "err": null,
    "status": "successful",
    "errmsg": null
  },
  "responseCode": "OK",
  "result": {
    "question": {
      "identifier": "do_213688205764337664161",
      "qumlVersion": 1.1,
      "media": [
        {
          "id": "do_2137498365362995201237",
          "type": "image",
          "src": "/assets/public/content/assets/do_2137498365362995201237/tea.jpeg",
          "baseUrl": "https://dev.inquiry.sunbird.org"
        }
      ],
      "body": {
        "language_code": "<div class='question-body' tabindex='-1'><div class='mcq-title' tabindex='0'><p><span style=\"background-color:#ffffff;color:#202124;\">Which of the following crops is a commercial crop?</span></p></div><div data-choice-interaction='response1' class='mcq-vertical'></div></div>"
      },
      "answer": {
        "language_code": "<p>Barley</p>"
      },
      "instructions": {
        "language_code": "<div>...</div>"
      },
      "hints": {
        "hint_1": {
          "language_code": "<div>...</div>"
        },
        "hint_2": {
          "language_code": "<div>...</div>"
        }
      },
      "feedback": {
        "feedback_1": {
          "language_code": "<div>...</div>"
        },
        "feedback_2": {
          "language_code": "<div>...</div>"
        }
      },
      "solutions": {
        "solution_1": {
          "language_code": "<div>...</div>"
        },
        "solution_2": {
          "language_code": "<div>...</div>"
        }
      },
      "interactions": {
        "response1": {
          "type": "choice",
          "options": [
            {
              "label": {
                "language_code": "<p>Wheat</p>"
              },
              "value": 1
            },
            {
              "label": {
                "language_code": "<p>Barley</p>"
              },
              "value": 2
            },
            {
              "label": {
                "language_code": "<p>Maize</p>"
              },
              "value": 3
            },
            {
              "label": {
                "language_code": "<p>Tea</p>"
              },
              "value": 4
            }
          ],
          "validation": {
            "required": "Yes"
          }
        }
      },
      "responseDeclaration": {
        "response1": {
          "cardinality": "single",
          "type": "integer",
          "correctResponse": {
            "value": 3
          },
          "mapping": [
            {
              "value": 2,
              "score": 0.5
            },
            {
              "value": 1,
              "score": 0.25
            }
          ]
        }
      },
      "outcomeDeclaration": {
        "maxScore": {
          "cardinality": "single",
          "type": "integer",
          "defaultValue": 3
        }
      }
    }
  }
}

Question Update API:

Sample Input for Update API (single language):

{
  "name": "Name of the question",
  "versionKey": "123456"
  "media": [
    {
      "id": "do_2137498365362995201237",
      "type": "image",
      "src": "/assets/public/content/assets/do_2137498365362995201237/tea.jpeg",
      "baseUrl": "https://dev.inquiry.sunbird.org"
    }
  ],
  "body": "<div class='question-body' tabindex='-1'><div class='mcq-title' tabindex='0'><p><span style=\"background-color:#ffffff;color:#202124;\">Which of the following crops is a commercial crop?</span></p></div><div data-choice-interaction='response1' class='mcq-vertical'></div></div>",
  "answer": "<p>Barley</p>",
  "instructions": {
    "language_code": "<div>...</div>"
  },
  "hints": {
    "hint_1": "<div>...</div>",
    "hint_2": "<div>...</div>"
  },
  "feedback": {
    "feedback_1": "<div>...</div>",
    "feedback_2": "<div>...</div>"
  },
  "solutions": {
    "solution_1": "solution 1 html string",
    "solution_2": "solution 2 html string"
  },
  "interactions": {
    "response1": {
      "type": "choice",
      "options": [
        {
          "label": "<p>Wheat</p>",
          "value": "w"
        },
        {
          "label": "<p>Barley</p>",
          "value": "b"
        },
        {
          "label": "<p>Maize</p>",
          "value": "m"
        },
        {
          "label": "<p>Tea</p>",
          "value": "t"
        }
      ],
      "validation": {
        "required": "Yes"
      }
    }
  },
  "responseDeclaration": {
    "response1": {
      "cardinality": "single",
      "type": "integer",
      "correctResponse": {
        "value": 3
      },
      "mapping": [
        {
          "value": 2,
          "score": 0.5
        },
        {
          "value": 1,
          "score": 0.25
        }
      ]
    }
  },
  "outcomeDeclaration": {
    "maxScore": {
      "cardinality": "single",
      "type": "integer",
      "defaultValue": 3
    }
  }
}

Sample Input for Update API (default input format):

{
  "versionKey": "12345"
  "name": "Name of the question",
  "media": [
    {
      "id": "do_2137498365362995201237",
      "type": "image",
      "src": "/assets/public/content/assets/do_2137498365362995201237/tea.jpeg",
      "baseUrl": "https://dev.inquiry.sunbird.org"
    }
  ],
  "body": {
    "language_code": "<div class='question-body' tabindex='-1'><div class='mcq-title' tabindex='0'><p><span style=\"background-color:#ffffff;color:#202124;\">Which of the following crops is a commercial crop?</span></p></div><div data-choice-interaction='response1' class='mcq-vertical'></div></div>"
  },
  "answer": {
    "language_code": "<p>Barley</p>"
  },
  "instructions": {
    "language_code": "<div>...</div>"
  },
  "hints": {
    "hint_1": {
      "language_code": "<div>...</div>"
    },
    "hint_2": {
      "language_code": "<div>...</div>"
    }
  },
  "feedback": {
    "feedback_1": {
      "language_code": "<div>...</div>"
    },
    "feedback_2": {
      "language_code": "<div>...</div>"
    }
  },
  "solutions": {
    "solution_1": {
      "language_code": "<div>...</div>"
    },
    "solution_2": {
      "language_code": "<div>...</div>"
    }
  },
  "interactions": {
    "response1": {
      "type": "choice",
      "options": [
        {
          "label": {
            "language_code": "<p>Wheat</p>"
          },
          "value": 1
        },
        {
          "label": {
            "language_code": "<p>Barley</p>"
          },
          "value": 2
        },
        {
          "label": {
            "language_code": "<p>Maize</p>"
          },
          "value": 3
        },
        {
          "label": {
            "language_code": "<p>Tea</p>"
          },
          "value": 4
        }
      ],
      "validation": {
        "required": "Yes"
      }
    }
  },
  "responseDeclaration": {
    "response1": {
      "cardinality": "single",
      "type": "integer",
      "correctResponse": {
        "value": 3
      },
      "mapping": [
        {
          "value": 2,
          "score": 0.5
        },
        {
          "value": 1,
          "score": 0.25
        }
      ]
    }
  },
  "outcomeDeclaration": {
    "maxScore": {
      "cardinality": "single",
      "type": "integer",
      "defaultValue": 3
    }
  }
}

Response:

{
    "id": "api.question.update",
    "ver": "5.0",
    "ts": "2023-01-30T19:43:43ZZ",
    "params": {
        "resmsgid": "ca1ed6b6-994d-487c-8ecd-0ca164a84c94",
        "msgid": null,
        "err": null,
        "status": "successful",
        "errmsg": null
    },
    "responseCode": "OK",
    "result": {
        "identifier": "do_2137224832856555521270",
        "versionKey": "1675107822985"
    }
}

Question Review API :

Question Publish API:

Question Retire API:

Question Copy API:

QuestionSet API’s:

QuestionSet Create API:

QuestionSet Read API & Hierarchy Read API:

QuestionSet Update Hierarchy API:
Sample Input Request with single language attributes:

{
  "request": {
    "data": {
      "nodesModified": {
        "q1": {
          "metadata": {
            "name": "Name of the question",
            "code": "q1",
            "mimeType": "application/vnd.sunbird.question",
            "primaryCategory": "Multiple Choice Question",
            "interactionTypes": [
              "choice"
            ],
            "qumlVersion": 1.1,
            "media": [
              {
                "id": "do_2137498365362995201237",
                "type": "image",
                "src": "/assets/public/content/assets/do_2137498365362995201237/tea.jpeg",
                "baseUrl": "https://dev.inquiry.sunbird.org"
              }
            ],
            "body": "<div class='question-body' tabindex='-1'><div class='mcq-title' tabindex='0'><p><span style=\"background-color:#ffffff;color:#202124;\">Which of the following crops is a commercial crop?</span></p></div><div data-choice-interaction='response1' class='mcq-vertical'></div></div>",
            "answer": "<p>Barley</p>",
            "instructions": {
              "language_code": "<div>...</div>"
            },
            "hints": {
              "hint_1": "<div>...</div>",
              "hint_2": "<div>...</div>"
            },
            "feedback": {
              "feedback_1": "<div>...</div>",
              "feedback_2": "<div>...</div>"
            },
            "solutions": {
              "solution_1": "solution 1 html string",
              "solution_2": "solution 2 html string"
            },
            "interactions": {
              "response1": {
                "type": "choice",
                "options": [
                  {
                    "label": "<p>Wheat</p>",
                    "value": "w"
                  },
                  {
                    "label": "<p>Barley</p>",
                    "value": "b"
                  },
                  {
                    "label": "<p>Maize</p>",
                    "value": "m"
                  },
                  {
                    "label": "<p>Tea</p>",
                    "value": "t"
                  }
                ],
                "validation": {
                  "required": "Yes"
                }
              }
            },
            "responseDeclaration": {
              "response1": {
                "cardinality": "single",
                "type": "integer",
                "correctResponse": {
                  "value": 3
                },
                "mapping": [
                  {
                    "value": 2,
                    "score": 0.5
                  },
                  {
                    "value": 1,
                    "score": 0.25
                  }
                ]
              }
            },
            "outcomeDeclaration": {
              "maxScore": {
                "cardinality": "single",
                "type": "integer",
                "defaultValue": 3
              }
            }
          },
          "objectType": "Question",
          "root": false,
          "isNew": true
        }
      },
      "hierarchy": {
        "questionset_identifier": {
          "children": [
            "q1"
          ],
          "root": true
        }
      }
    }
  }
}


Sample Input Request with multi-language attributes (default format):

{
  "request": {
    "data": {
      "nodesModified": {
        "q1": {
          "metadata": {
            "name": "Name of the question",
            "code": "q1",
            "mimeType": "application/vnd.sunbird.question",
            "primaryCategory": "Multiple Choice Question",
            "interactionTypes": [
              "choice"
            ],
            "qumlVersion": 1.1,
            "media": [
              {
                "id": "do_2137498365362995201237",
                "type": "image",
                "src": "/assets/public/content/assets/do_2137498365362995201237/tea.jpeg",
                "baseUrl": "https://dev.inquiry.sunbird.org"
              }
            ],
            "body": {
              "language_code": "<div class='question-body' tabindex='-1'><div class='mcq-title' tabindex='0'><p><span style=\"background-color:#ffffff;color:#202124;\">Which of the following crops is a commercial crop?</span></p></div><div data-choice-interaction='response1' class='mcq-vertical'></div></div>"
            },
            "answer": {
              "language_code": "<p>Barley</p>"
            },
            "instructions": {
              "language_code": "<div>...</div>"
            },
            "hints": {
              "hint_1": {
                "language_code": "<div>...</div>"
              },
              "hint_2": {
                "language_code": "<div>...</div>"
              }
            },
            "feedback": {
              "feedback_1": {
                "language_code": "<div>...</div>"
              },
              "feedback_2": {
                "language_code": "<div>...</div>"
              }
            },
            "solutions": {
              "solution_1": {
                "language_code": "<div>...</div>"
              },
              "solution_2": {
                "language_code": "<div>...</div>"
              }
            },
            "interactions": {
              "response1": {
                "type": "choice",
                "options": [
                  {
                    "label": {
                      "language_code": "<p>Wheat</p>"
                    },
                    "value": 1
                  },
                  {
                    "label": {
                      "language_code": "<p>Barley</p>"
                    },
                    "value": 2
                  },
                  {
                    "label": {
                      "language_code": "<p>Maize</p>"
                    },
                    "value": 3
                  },
                  {
                    "label": {
                      "language_code": "<p>Tea</p>"
                    },
                    "value": 4
                  }
                ],
                "validation": {
                  "required": "Yes"
                }
              }
            },
            "responseDeclaration": {
              "response1": {
                "cardinality": "single",
                "type": "integer",
                "correctResponse": {
                  "value": 3
                },
                "mapping": [
                  {
                    "value": 2,
                    "score": 0.5
                  },
                  {
                    "value": 1,
                    "score": 0.25
                  }
                ]
              }
            },
            "outcomeDeclaration": {
              "maxScore": {
                "cardinality": "single",
                "type": "integer",
                "defaultValue": 3
              }
            }
          },
          "objectType": "Question",
          "root": false,
          "isNew": true
        }
      },
      "hierarchy": {
        "questionset_identifier": {
          "children": [
            "q1"
          ],
          "root": true
        }
      }
    }
  }
}

Sample Success Response:

{
    "id": "api.questionset.hierarchy.update",
    "ver": "5.0",
    "ts": "2023-05-08T07:03:25ZZ",
    "params": {
        "resmsgid": "deb89409-5775-4970-921d-1298f99dafa7",
        "msgid": null,
        "err": null,
        "status": "successful",
        "errmsg": null
    },
    "responseCode": "OK",
    "result": {
        "identifier": "do_21379147288621056013168",
        "identifiers": {
            "q1": "do_21379147288888115213169"
        }
    }
}

QuestionSet Update API:

QuestionSet Review API:

QuestionSet Publish API:

QuestionSet Retire API:

QuestionSet Copy API: