diff --git a/.gitignore b/.gitignore index 11299d338..53a08bc39 100644 --- a/.gitignore +++ b/.gitignore @@ -49,4 +49,6 @@ Thumbs.db # Web components Files -project-sunbird-sunbird-questionset-editor-web-component-* \ No newline at end of file +project-sunbird-sunbird-questionset-editor-web-component-* + +.env \ No newline at end of file diff --git a/angular.json b/angular.json index c02c7deb3..d7fd032f0 100644 --- a/angular.json +++ b/angular.json @@ -49,6 +49,7 @@ "node_modules/@project-sunbird/sunbird-quml-player-web-component/sunbird-quml-player.js", "src/assets/libs/iziToast/iziToast.min.js", "node_modules/jquery.fancytree/dist/jquery.fancytree-all-deps.min.js", + "node_modules/@project-sunbird/sunbird-file-upload-library/sunbird-file-upload-library.js", "src/assets/lib/dimmer.min.js", "src/assets/lib/transition.min.js", "src/assets/lib/modal.min.js", @@ -176,9 +177,10 @@ "karmaConfig": "projects/questionset-editor-library/karma.conf.js", "scripts": [ "src/assets/libs/iziToast/iziToast.min.js", - "node_modules/jquery/dist/jquery.min.js" + "node_modules/jquery/dist/jquery.min.js", + "node_modules/@project-sunbird/sunbird-file-upload-library/sunbird-file-upload-library.js" ], - "codeCoverageExclude": [ + "codeCoverageExclude": [ "projects/questionset-editor-library/src/lib/interfaces/*/*.ts" ] } @@ -238,6 +240,7 @@ "node_modules/@project-sunbird/sunbird-quml-player-web-component/sunbird-quml-player.js", "src/assets/libs/iziToast/iziToast.min.js", "node_modules/jquery.fancytree/dist/jquery.fancytree-all-deps.min.js", + "node_modules/@project-sunbird/sunbird-file-upload-library/sunbird-file-upload-library.js", "src/assets/lib/dimmer.min.js", "src/assets/lib/transition.min.js", "src/assets/lib/modal.min.js", diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index ccca4794c..25d1dbd76 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -132,7 +132,6 @@ config: { review: string[], }, maxDepth: number, //Ex.: 1 - publicStorageAccount: 'url', //Ex.: https://dockstorage.blob.core.windows.net/ assetConfig: object, objectType: 'string', //Ex.: QuestionSet primaryCategory: 'string', //Ex.: Practice Question Set @@ -180,7 +179,6 @@ Description of the properties for the config: | `iconClass` | It is `string` and that defines the icon of root node | true | `fa fa-book` | | `children` | It is an `object` and If maxdepth is 0 than children inside the root node defines the content type. **For example:** `children: {Question: ["Multiple Choice Question", "Subjective Question"]}` | true | | | `contentPolicyUrl` | It is `string` and It defines where should the content policy link will be redirected. | true | `/term-of-use.html` | -| `publicStorageAccount` | It is `url` and URL of the blob storage **For example:** `https://dockstorage.blob.core.windows.net/` | true | | | `mode` | It is `string` and that defines the mode in editor is to be loaded. **For example:** `edit / review / read / sourcingReview / orgReview` | false | `edit` | | `editableFields` | It is an `object` and that defines the mode in editor is to be loaded. | false | `{ sourcingreview: [], orgreview: [], review: [], }` | | `maxDepth` | It is `number` and Defines the depth to which the textbook is to be created. If the depth is 1, hierarchy should have level1 described. | false | **For example:** `1` | diff --git a/package-lock.json b/package-lock.json index 78cb4bbe7..f09b372b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4208,15 +4208,23 @@ "resolved": "https://registry.npmjs.org/@project-sunbird/sb-styles/-/sb-styles-0.0.9.tgz", "integrity": "sha512-61jigtq4ertmh9XhnSDQlfZDSn1a8TXr7PvMxmJVWlzp4uoXHudZRzPoIi4IpvKD4AEk8nFRjIMc5eSsM5UJhw==" }, + "@project-sunbird/sunbird-file-upload-library": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-file-upload-library/-/sunbird-file-upload-library-1.0.2.tgz", + "integrity": "sha512-oqbyAECe9ejyGac4vQK9lkkXNOdP3fnK+QXMeoZOZn5rAnStcWgAxyK+tdgoC8FDBFC5JOyWGjM9UfntUMsRaA==", + "requires": { + "axios": "^1.4.0" + } + }, "@project-sunbird/sunbird-quml-player-web-component": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-quml-player-web-component/-/sunbird-quml-player-web-component-2.1.0.tgz", - "integrity": "sha512-AHY/EHH7UhExCDnpqc/PsVaW++NMLntLwD6vBCl2Jg2RNrJhbFqUocVNKzBHwQR/u7TgjRpoy2xE//ML3rkXAw==" + "version": "2.2.0-beta.0", + "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-quml-player-web-component/-/sunbird-quml-player-web-component-2.2.0-beta.0.tgz", + "integrity": "sha512-zIXAvA6SJPDCo1wCEDm6gzr7TxqaUp8qgZy+eyZydFYgi+9Z75IUbknvGTknJQUpY1UYvM356AF9wYqrRH42vA==" }, "@project-sunbird/sunbird-resource-library": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-resource-library/-/sunbird-resource-library-6.1.0.tgz", - "integrity": "sha512-tjhYZSsi84qmcunTwsKeUfAefeyEIGCtD0a7Ywz6nD0h0p0FwqrYr7El4gZN8gtJm0oJsSJRLbQsyhp3Ansp7A==", + "version": "6.2.0-beta.0", + "resolved": "https://registry.npmjs.org/@project-sunbird/sunbird-resource-library/-/sunbird-resource-library-6.2.0-beta.0.tgz", + "integrity": "sha512-Mn2WzPDlwJU9OpCdaG6GKCu/aIuzJkJzCEzXaqQXGNKdLIujCEbwWGrGoQ+t8my2lnCZCSXIcynDBhwKwnrdoQ==", "requires": { "tslib": "^2.0.0" } @@ -5371,6 +5379,28 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -7290,6 +7320,12 @@ "domelementtype": "1" } }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -13708,6 +13744,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", diff --git a/package.json b/package.json index 54f3f7691..c34702250 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,9 @@ "@project-sunbird/client-services": "5.0.0", "@project-sunbird/common-form-elements-full": "^6.0.3", "@project-sunbird/sb-styles": "0.0.9", - "@project-sunbird/sunbird-quml-player-web-component": "2.1.0", - "@project-sunbird/sunbird-resource-library": "6.1.0", + "@project-sunbird/sunbird-quml-player-web-component": "2.2.0-beta.0", + "@project-sunbird/sunbird-file-upload-library": "1.0.2", + "@project-sunbird/sunbird-resource-library": "6.2.0-beta.0", "@project-sunbird/telemetry-sdk": "0.0.29", "@types/jquery": "^3.5.5", "alphanum-sort": "^1.0.2", @@ -82,6 +83,7 @@ "protractor": "~7.0.0", "ts-node": "~7.0.0", "tslint": "~6.1.0", - "typescript": "4.9.5" + "typescript": "4.9.5", + "dotenv": "^16.3.1" } } diff --git a/projects/questionset-editor-library-wc/src/styles.scss b/projects/questionset-editor-library-wc/src/styles.scss index c0832a358..7dea030d5 100644 --- a/projects/questionset-editor-library-wc/src/styles.scss +++ b/projects/questionset-editor-library-wc/src/styles.scss @@ -17,6 +17,5 @@ @import "./../../questionset-editor-library/src/lib/components/qumlplayer-page/qumlplayer-page.component.scss"; @import "./../../questionset-editor-library/src/lib/components/slider/slider.component.scss"; @import "./../../questionset-editor-library/src/lib/components/template/template.component.scss"; -@import "./../../questionset-editor-library/src/lib/components/term-and-condition/term-and-condition.component.scss"; @import "./../../questionset-editor-library/src/lib/components/translations/translations.component.scss"; diff --git a/projects/questionset-editor-library/package.json b/projects/questionset-editor-library/package.json index 9f2fb3e1b..45eadf762 100644 --- a/projects/questionset-editor-library/package.json +++ b/projects/questionset-editor-library/package.json @@ -1,6 +1,6 @@ { "name": "@project-sunbird/sunbird-questionset-editor", - "version": "6.1.0", + "version": "6.2.0-beta.5", "dependencies": { "tslib": "^2.0.0" }, diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts index c815a69f4..1e6b4b5d8 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.spec.ts @@ -72,18 +72,6 @@ describe('AssetBrowserComponent', () => { expect(result).toEqual("image/png,image/jpeg"); }); - it('#initializeImagePicker() should set showImagePicker to true', () => { - spyOn(component, 'initializeImagePicker').and.callThrough(); - component.initializeImagePicker(); - expect(component.showImagePicker).toBeTruthy(); - }); - - it('#outputEventHandler() should log event', () => { - spyOn(component, 'outputEventHandler').and.callThrough(); - component.outputEventHandler({}); - expect(component.outputEventHandler).toHaveBeenCalled(); - }) - it('#getMyImages() should return images on API success', async () => { const response = mockData.serverResponse; response.result = { @@ -139,7 +127,7 @@ describe('AssetBrowserComponent', () => { expect(component.imageFormValid).toEqual(false); expect(component.formConfig).toBeTruthy(); }) - it('#uploadAndUseImage should upload image on API success', async () => { + xit('#uploadAndUseImage should upload image on API success', async () => { const createMediaAssetResponse = mockData.serverResponse; createMediaAssetResponse.result = { node_id: 'do_123' @@ -212,10 +200,10 @@ describe('AssetBrowserComponent', () => { it('#uploadToBlob() should upload blob on API success', () => { let signedURL = '/test'; let file = new File([], 'filename'); - let config = {}; + let csp = 'azure' let questionService: QuestionService= TestBed.inject(QuestionService); - spyOn(questionService.http, 'put').and.returnValue(of({"responseCode": "OK"})); - component.uploadToBlob(signedURL, file, config).subscribe(data => { + spyOn(questionService, 'uploadToBlob').and.returnValue(of({"responseCode": "OK"})); + component.uploadToBlob(signedURL, file, csp).subscribe(data => { expect(data.responseCode).toEqual('OK'); }) }) diff --git a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts index 8c91d4533..1e5a4c693 100644 --- a/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts +++ b/projects/questionset-editor-library/src/lib/components/asset-browser/asset-browser.component.ts @@ -27,15 +27,11 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { showErrorMsg: boolean; errorMsg: string; query: string; - uploader; isClosable = true; loading = false; - public mediaobj; - public editorInstance: any; public assetsCount: any; public searchMyInput = ''; public searchAllInput: any; - showAddButton: boolean; appIcon; public formData: any; public assestData = {}; @@ -64,14 +60,6 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { return result.toString(); } - initializeImagePicker() { - this.showImagePicker = true; - } - - outputEventHandler(event) { - console.log(JSON.stringify(event)); - } - getMyImages(offset, query?, search?) { this.assetsCount = 0; if (!search) { @@ -273,7 +261,8 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { } uploadToBlob(signedURL, file, config): Observable { - return this.questionService.http.put(signedURL, file, config).pipe(catchError(err => { + const csp = _.get(this.editorService.editorConfig, 'context.cloudStorage.provider', 'azure'); + return this.questionService.uploadToBlob(signedURL, file, csp).pipe(catchError(err => { const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; this.isClosable = true; this.loading = false; @@ -281,7 +270,6 @@ export class AssetBrowserComponent implements OnInit, OnDestroy { }), map(data => data)); } - dismissImageUploadModal() { if (this.isClosable) { this.showImagePicker = true; diff --git a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts index b3b1a4acf..9e34632e2 100644 --- a/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts +++ b/projects/questionset-editor-library/src/lib/components/ckeditor-tool/ckeditor-tool.component.ts @@ -791,7 +791,8 @@ export class CkeditorToolComponent implements OnInit, AfterViewInit, OnChanges { } uploadToBlob(signedURL, file, config): Observable { - return this.questionService.http.put(signedURL, file, config).pipe(catchError(err => { + const csp = _.get(this.editorService.editorConfig, 'context.cloudStorage.provider', 'azure'); + return this.questionService.uploadToBlob(signedURL, file, csp).pipe(catchError(err => { const errInfo = { errorMsg: _.get(this.configService.labelConfig, 'messages.error.018') }; this.isClosable = true; this.loading = false; diff --git a/projects/questionset-editor-library/src/lib/components/editor/editor.component.html b/projects/questionset-editor-library/src/lib/components/editor/editor.component.html index bd4c70206..636a14891 100755 --- a/projects/questionset-editor-library/src/lib/components/editor/editor.component.html +++ b/projects/questionset-editor-library/src/lib/components/editor/editor.component.html @@ -7,46 +7,7 @@
- -
+
diff --git a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts index cd4a9b237..8f3887307 100644 --- a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.data.ts @@ -5,34 +5,25 @@ export let editorConfig_question: IEditorConfig | undefined; editorConfig = { context: { - framework: 'tpd', + programId: 'f72ad8b0-36df-11ec-a56f-4b503455085f', + contributionOrgId: '', user: { id: '5a587cc1-e018-4859-a0a8-e842650b9d64', - name: 'Vaibhav', - orgIds: ['01309282781705830427'], + fullName: 'Test User', + firstName: 'Test', + lastName: 'User', + orgIds: ['01309282781705830427'] }, - identifier: 'do_113274017771085824116', - unitIdentifier: 'do_113274017771085824119', - collectionObjectType: '', - channel: '01307938306521497658', + identifier: 'do_2138624108252774401216', authToken: ' ', sid: 'iYO2K6dOSdA0rwq7NeT1TDzS-dbqduvV', did: '7e85b4967aebd6704ba1f604f20056b6', uid: 'bf020396-0d7b-436f-ae9f-869c6780fc45', - additionalCategories: [ - { - value: 'Textbook', - label: 'Textbook', - }, - { - value: 'Lesson Plan', - label: 'Lesson Plan', - }, - ], + channel: '01309282781705830427', pdata: { id: 'dev.dock.portal', ver: '2.8.0', - pid: 'creation-portal', + pid: 'creation-portal' }, contextRollup: { l1: '01307938306521497658', @@ -49,25 +40,45 @@ editorConfig = { }, { type: 'linked_collection', - id: 'do_113140468925825024117', - }, + id: 'do_113140468925825024117' + } ], timeDiff: 5, objectRollup: { l1: 'do_113140468925825024117', - l2: 'do_113140468926914560125', + l2: 'do_113140468926914560125' }, - host: '', + host: 'https://dev.inquiry.sunbird.org', defaultLicense: 'CC BY 4.0', endpoint: '/data/v3/telemetry', - env: 'question_set', - cloudStorageUrls: [ - 'https://s3.ap-south-1.amazonaws.com/ekstep-public-qa/', - 'https://ekstep-public-qa.s3-ap-south-1.amazonaws.com/', - 'https://dockstorage.blob.core.windows.net/sunbird-content-dock/', + env: 'questionset_editor', + framework: 'inquiry_k-12', + cloudStorageUrls: ['https://s3.ap-south-1.amazonaws.com/ekstep-public-qa/', 'https://ekstep-public-qa.s3-ap-south-1.amazonaws.com/', + 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/', 'https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/'], + board: 'CBSE', + medium: ['English'], + gradeLevel: ['Class 1'], + subject: ['Hindi'], + additionalCategories: [ + { + value: 'Classroom Teaching Video', + label: 'Classroom Teaching Video' + }, + { + value: 'Concept Map', + label: 'Concept Map' + }, + { + value: 'Curiosity Question Set', + label: 'Curiosity Question Set' + } ], - mode: 'edit', + labels: { + save_collection_btn_label: 'Save as Draft', + }, + correctionComments: false, cloudStorage: { + provider: 'azure', presigned_headers: { 'x-ms-blob-type': 'BlockBlob' } @@ -75,69 +86,45 @@ editorConfig = { }, config: { mode: 'edit', - maxDepth: 2, - objectType: 'Collection', - primaryCategory: 'Course', - isReadOnlyMode: false, + enableQuestionCreation: true, + enableAddFromLibrary: true, + editableFields: { + sourcingreview: ['instructions'], + orgreview: ['name', 'instructions', 'learningOutcome'], + review: ['name', 'description'], + }, + maxDepth: 1, + objectType: 'QuestionSet', + primaryCategory: 'Practice Question Set', isRoot: true, iconClass: 'fa fa-book', - children: {}, - renderTaxonomy: false, - categoryInstance: 'topic', - hierarchy: { - level1: { - name: 'Module', - type: 'Unit', - mimeType: 'application/vnd.ekstep.content-collection', - contentType: 'CourseUnit', - primaryCategory: 'Course Unit', - iconClass: 'fa fa-folder-o', - children: {}, - }, - level2: { - name: 'Sub-Module', - type: 'Unit', - mimeType: 'application/vnd.ekstep.content-collection', - contentType: 'CourseUnit', - primaryCategory: 'Course Unit', - iconClass: 'fa fa-folder-o', - children: { - Content: [ - 'Explanation Content', - 'Learning Resource', - 'eTextbook', - 'Teacher Resource', - 'Course Assessment', - ], - }, - }, - level3: { - name: 'Sub-Sub-Module', - type: 'Unit', - mimeType: 'application/vnd.ekstep.content-collection', - contentType: 'CourseUnit', - primaryCategory: 'Course Unit', - iconClass: 'fa fa-folder-o', - children: { - Content: [ - 'Explanation Content', - 'Learning Resource', - 'eTextbook', - 'Teacher Resource', - 'Course Assessment', - ], - }, - }, - }, - interactionType: 'choice', - collection: { - maxContentsLimit: 10, + hideSubmitForReviewBtn: false, + children: { + Question: [ + 'Multiple Choice Question', + 'Subjective Question' + ] }, - questionSet: { - maxQuestionsLimit: 10, + addFromLibrary: false, + hierarchy: { + "level1": { + "name": "Section", + "type": "Unit", + "mimeType": "application/vnd.sunbird.questionset", + "primaryCategory": "Practice Question Set", + "iconClass": "fa fa-folder-o", + "children": { + "Question": [ + "Multiple Choice Question", + "Subjective Question" + ] + } + } }, contentPolicyUrl: '/term-of-use.html', - }, + assetProxyUrl: '/assets/public/', + commonFrameworkLicenseUrl: 'https://creativecommons.org/licenses/' + } }; editorConfig_question = { @@ -266,14 +253,10 @@ editorConfig_question = { showOriginPreviewUrl: false, showSourcingStatus: false, showCorrectionComments: false, - publicStorageAccount: 'https://dockstorage.blob.core.windows.net/', hideSubmitForReviewBtn: false, questionSet: { maxQuestionsLimit: 500, }, - collection: { - maxContentsLimit: 1200, - }, editableFields: { orgreview: ['name', 'learningOutcome'], sourcingreview: ['name', 'learningOutcome'], @@ -393,129 +376,6 @@ export const editorServiceSelectedChildren = { interactionType: 'choice', }; -export const csvExport = { - successExport: { - id: 'api.collection.export', - ver: '4.0', - ts: '2021-07-05T09:58:51ZZ', - params: { - resmsgid: '0801c119-ad94-4eb9-809c-998ed95789ea', - msgid: null, - err: null, - status: 'successful', - errmsg: null, - }, - responseCode: 'OK', - result: { - collection: { - tocUrl: - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/course/toc/do_11331579492804198413_untitled-course_1625465046239.csv', - ttl: '54000', - }, - }, - }, - errorExport: { - id: 'api.collection.export', - ver: '4.0', - ts: '2021-07-05T10:25:41ZZ', - params: { - resmsgid: '19a77469-4de8-41b6-857c-d0bea04db7f4', - msgid: null, - err: 'COLLECTION_CHILDREN_NOT_EXISTS', - status: 'failed', - errmsg: 'No Children Exists for given Collection.', - }, - responseCode: 'CLIENT_ERROR', - result: { - messages: null, - }, - }, -}; -export const hirearchyGet = { - id: 'api.content.hierarchy.get', - ver: '3.0', - ts: '2021-07-07T12:58:28ZZ', - params: { - resmsgid: '53fdfd79-2ab3-4c1c-8e4a-dc3a44ca3dbd', - msgid: null, - err: null, - status: 'successful', - errmsg: null, - }, - responseCode: 'OK', - result: { - content: { - ownershipType: ['createdBy'], - copyright: 'NIT123', - keywords: ['test'], - subject: ['English', 'Hindi'], - targetMediumIds: ['nit_k-12_medium_hindi'], - channel: '01309282781705830427', - organisation: ['NIT'], - language: ['English'], - mimeType: 'application/vnd.ekstep.content-collection', - targetGradeLevelIds: ['nit_k-12_gradelevel_grade-2'], - objectType: 'Content', - appIcon: '', - primaryCategory: 'Course', - children: [], - contentEncoding: 'gzip', - lockKey: '6ea80434-00b2-4e43-a872-20c34ee78615', - contentType: 'Course', - trackable: { - enabled: 'Yes', - autoBatch: 'Yes', - }, - identifier: 'do_113316577504272384141', - audience: ['Student'], - subjectIds: ['nit_k-12_subject_english', 'nit_k-12_subject_hindi'], - visibility: 'Default', - consumerId: '273f3b18-5dda-4a27-984a-060c7cd398d3', - discussionForum: { - enabled: 'Yes', - }, - mediaType: 'content', - osId: 'org.ekstep.quiz.app', - languageCode: ['en'], - version: 2, - license: 'CC BY 4.0', - name: 'Course', - targetBoardIds: ['nit_k-12_board_ncert'], - status: 'Draft', - code: 'org.sunbird.TEDujL', - credentials: { - enabled: 'Yes', - }, - description: 'Enter description for Course', - idealScreenSize: 'normal', - createdOn: '2021-07-06T08:06:17.392+0000', - targetSubjectIds: ['nit_k-12_subject_hindi'], - copyrightYear: 2021, - contentDisposition: 'inline', - lastUpdatedOn: '2021-07-07T12:20:44.344+0000', - lastStatusChangedOn: '2021-07-06T08:06:17.392+0000', - createdFor: ['01309282781705830427'], - creator: 'N11', - os: ['All'], - targetFWIds: ['nit_k-12'], - versionKey: '1625660444344', - idealScreenDensity: 'hdpi', - framework: 'nit_k-12', - depth: 0, - createdBy: '5a587cc1-e018-4859-a0a8-e842650b9d64', - compatibilityLevel: 1, - userConsent: 'Yes', - timeLimits: { - questionSet: { - min: 0, - max: 300 - } - }, - resourceType: 'Course', - }, - }, -}; - export const categoryDefinition = { result: { objectCategoryDefinition: { @@ -640,1182 +500,1061 @@ export const categoryDefinition = { description: 'Suggested User Type', editable: true, inputType: 'nestedselect', - label: 'Suggested User Type', - name: 'userTypes', - placeholder: 'Suggested User Type', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - required: false, - visible: true, - range: [ - 'Education Official', - 'School leaders (HMs)', - 'Teachers', - 'Students', - 'Parents', - 'Parent', - 'Others', - ], - }, - { - code: 'description', - name: 'Description', - label: 'Description', - placeholder: 'Enter Description', - description: 'Description of the Question Set', - dataType: 'text', - inputType: 'textarea', - editable: true, - required: false, - visible: true, - renderingHints: { - class: 'sb-g-col-lg-1', - }, - }, - { - code: 'allowScoring', - name: 'allowScoring', - label: 'Enable Scoring', - placeholder: 'Enable Scoring', - description: - 'Select to enable the option of scoring for questions in the form. Score can be added to questions only if this is selected', - dataType: 'text', - inputType: 'checkbox', - editable: true, - required: false, - visible: true, - renderingHints: { - class: 'sb-g-col-lg-1', - }, - }, - { - code: 'evidenceMimeType', - dataType: 'list', - depends: ['showEvidence'], - description: 'Evidence', - editable: true, - inputType: 'multiselect', - label: 'evidence', - name: 'evidenceMimeType', - placeholder: 'evidence', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - required: false, - visible: true, - range: [ - { - value: 'image/png', - label: 'image/png', - }, - { value: 'audio/mp3', label: 'audio/mp3' }, - { value: 'video/mp4', label: 'video/mp4' }, - { value: 'video/webm', label: 'video/webm' }, - ], - }, - { - code: 'ecm', - name: 'ECM', - depends: ['allowECM'], - label: 'Select ECM\'s', - placeholder: 'Select ECM\'s', - description: 'ECM for the Observation with rubrics', - dataType: 'list', - inputType: 'selectTextBox', - editable: true, - required: false, - visible: true, - renderingHints: { - class: 'sb-g-col-lg-1', - }, - options: [ - 'Student interview', - 'Teacher interview', - 'HM/HT interview', - 'Parent interview', - 'Official interview', - 'School walkthrough', - 'Class observation', - 'Document Review', - ], - }, - ], - }, - ], - }, - search: { - properties: { code: 'name', editable: true }, - }, - searchConfig: { - properties: { code: 'name', editable: true }, - }, - childMetadata: { - properties: { code: 'name', editable: true }, - }, - relationalMetadata: { - properties: { code: 'name', editable: true }, - }, - }, - label: 'Multiple Choice Question', - }, - }, -}; - -export const hierarchyResponse = [ - { - result: { - content: { - ownershipType: ['createdBy'], - copyright: 'NIT123', - keywords: ['test course'], - subject: ['English', 'Hindi'], - targetMediumIds: ['nit_k-12_medium_english'], - channel: '01309282781705830427', - organisation: ['NIT'], - language: ['English'], - mimeType: 'application/vnd.ekstep.content-collection', - targetGradeLevelIds: ['nit_k-12_gradelevel_grade-1'], - objectType: 'Content', - // tslint:disable-next-line:max-line-length - appIcon: - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_113253856016146432127/artifact/do_113253856016146432127_1617902346121_image.jpg', - primaryCategory: 'Course', - children: [ - { - ownershipType: ['createdBy'], - parent: 'do_113316550746677248131', - code: '89be24d9-d098-39c0-9680-0d0e3df3c647', - keywords: ['unit-1'], - credentials: { - enabled: 'No', - }, - channel: '01309282781705830427', - description: 'unit-1', - language: ['English'], - mimeType: 'application/vnd.ekstep.content-collection', - idealScreenSize: 'normal', - createdOn: '2021-07-06T07:15:40.525+0000', - objectType: 'Content', - primaryCategory: 'Course Unit', - children: [ - { - ownershipType: ['createdBy'], - parent: 'do_113316552626380800133', - code: 'd6067beb-01f9-a634-13a4-c453fc3d05d9', - keywords: ['unit-1.1'], - credentials: { - enabled: 'No', - }, - channel: '01309282781705830427', - description: 'unit-1.1', - language: ['English'], - mimeType: 'application/vnd.ekstep.content-collection', - idealScreenSize: 'normal', - createdOn: '2021-07-06T07:15:50.428+0000', - objectType: 'Content', - primaryCategory: 'Course Unit', - children: [ - { - ownershipType: ['createdBy'], - parent: 'do_113316552707506176135', - unitIdentifiers: ['do_1133124409146736641472'], - copyright: '2021', - previewUrl: - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1133124455783546881476/artifact/do_1133124455783546881476_1625054498447_dummy.pdf', - subject: ['Mathematics'], - channel: '01309282781705830427', - downloadUrl: - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1133124455783546881476/content-name-is-more-than-30-charcater_1625054501658_do_1133124455783546881476_1.0.ecar', - language: ['English'], - source: - 'https://dock.sunbirded.org/api/content/v1/read/do_1133124455783546881476', - mimeType: 'application/pdf', - variants: { - spine: { - ecarUrl: - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/ecar_files/do_1133124455783546881476/content-name-is-more-than-30-charcater_1625054501895_do_1133124455783546881476_1.0_spine.ecar', - size: 1487, - }, - }, - objectType: 'Content', - se_mediums: ['Hindi'], - gradeLevel: ['Class 1'], - primaryCategory: 'eTextbook', - appId: 'dev.dock.portal', - contentEncoding: 'identity', - artifactUrl: - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1133124455783546881476/artifact/do_1133124455783546881476_1625054498447_dummy.pdf', - sYS_INTERNAL_LAST_UPDATED_ON: - '2021-06-30T12:01:42.209+0000', - contentType: 'eTextBook', - se_gradeLevels: ['Class 1'], - trackable: { - enabled: 'No', - autoBatch: 'No', - }, - identifier: 'do_1133124455783546881476', - audience: ['Student'], - visibility: 'Default', - author: 'Vai', - consumerId: '273f3b18-5dda-4a27-984a-060c7cd398d3', - discussionForum: { - enabled: 'Yes', - }, - index: 1, - mediaType: 'content', - osId: 'org.ekstep.quiz.app', - languageCode: ['en'], - lastPublishedBy: '5a587cc1-e018-4859-a0a8-e842650b9d64', - version: 2, - pragma: ['external'], - se_subjects: ['Mathematics'], - license: 'CC BY 4.0', - prevState: 'Review', - size: 14040, - lastPublishedOn: '2021-06-30T12:01:41.657+0000', - name: 'content name is more than 30 charcater', - status: 'Live', - code: '92db93d1-9a9b-61aa-9b6d-66bbb979f4ad', - credentials: { - enabled: 'No', - }, - prevStatus: 'Processing', - origin: 'do_1133124455783546881476', - streamingUrl: - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/do_1133124455783546881476/artifact/do_1133124455783546881476_1625054498447_dummy.pdf', - medium: ['Hindi'], - idealScreenSize: 'normal', - createdOn: '2021-06-30T12:01:38.105+0000', - se_boards: ['CBSE'], - processId: '7b4d85eb-1167-4e99-9941-2091961dfac8', - contentDisposition: 'inline', - lastUpdatedOn: '2021-06-30T12:01:41.299+0000', - originData: { - identifier: 'do_1133124455783546881476', - repository: - 'https://dock.sunbirded.org/api/content/v1/read/do_1133124455783546881476', - }, - collectionId: 'do_1133124409135595521471', - lastStatusChangedOn: '2021-06-30T12:01:42.198+0000', - createdFor: ['01309282781705830427'], - creator: 'Vai', - os: ['All'], - cloudStorageKey: - 'content/do_1133124455783546881476/artifact/do_1133124455783546881476_1625054498447_dummy.pdf', - se_FWIds: ['ekstep_ncert_k-12'], - pkgVersion: 1, - versionKey: '1625054501299', - idealScreenDensity: 'hdpi', - framework: 'ekstep_ncert_k-12', - depth: 3, - s3Key: - 'ecar_files/do_1133124455783546881476/content-name-is-more-than-30-charcater_1625054501658_do_1133124455783546881476_1.0.ecar', - lastSubmittedOn: '2021-06-30T12:01:39.984+0000', - createdBy: 'd3d05422-1670-4c3e-9051-13c306e89a95', - compatibilityLevel: 4, - board: 'CBSE', - programId: '537c2280-d999-11eb-882d-db3e4f86a45f', - }, - ], - contentDisposition: 'inline', - lastUpdatedOn: '2021-07-06T07:15:50.428+0000', - contentEncoding: 'gzip', - contentType: 'CourseUnit', - trackable: { - enabled: 'No', - autoBatch: 'No', - }, - identifier: 'do_113316552707506176135', - lastStatusChangedOn: '2021-07-06T07:15:50.428+0000', - audience: ['Student'], - os: ['All'], - visibility: 'Parent', - discussionForum: { - enabled: 'Yes', - }, - index: 1, - mediaType: 'content', - osId: 'org.ekstep.launcher', - languageCode: ['en'], - version: 2, - versionKey: '1625555750428', - license: 'CC BY 4.0', - idealScreenDensity: 'hdpi', - depth: 2, - compatibilityLevel: 1, - name: 'unit-1.1', - timeLimits: { - questionSet: { - min: 0, - max: 300 - } - }, - status: 'Draft', - level: 3, - }, - ], - contentDisposition: 'inline', - lastUpdatedOn: '2021-07-06T07:15:40.525+0000', - contentEncoding: 'gzip', - contentType: 'CourseUnit', - trackable: { - enabled: 'No', - autoBatch: 'No', - }, - identifier: 'do_113316552626380800133', - lastStatusChangedOn: '2021-07-06T07:15:40.525+0000', - audience: ['Student'], - os: ['All'], - visibility: 'Parent', - discussionForum: { - enabled: 'Yes', - }, - index: 1, - mediaType: 'content', - osId: 'org.ekstep.launcher', - languageCode: ['en'], - version: 2, - versionKey: '1625555740525', - license: 'CC BY 4.0', - idealScreenDensity: 'hdpi', - depth: 1, - compatibilityLevel: 1, - name: 'unit-1', - timeLimits: { - questionSet: { - min: 0, - max: 300 - } - }, - status: 'Draft', - level: 2, - }, - ], - contentEncoding: 'gzip', - lockKey: '49d5e059-5ff7-444c-bcf5-84e293f8da7c', - contentType: 'Course', - trackable: { - enabled: 'Yes', - autoBatch: 'Yes', - }, - identifier: 'do_113316550746677248131', - audience: ['Student'], - subjectIds: ['nit_k-12_subject_english', 'nit_k-12_subject_hindi'], - visibility: 'Default', - consumerId: '273f3b18-5dda-4a27-984a-060c7cd398d3', - childNodes: [ - 'do_1133124455783546881476', - 'do_113316552707506176135', - 'do_113316552626380800133', - ], - discussionForum: { - enabled: 'Yes', - }, - mediaType: 'content', - osId: 'org.ekstep.quiz.app', - languageCode: ['en'], - version: 2, - license: 'CC BY 4.0', - name: 'test course', - targetBoardIds: ['nit_k-12_board_cbse'], - status: 'Draft', - code: 'org.sunbird.Yj12wV', - credentials: { - enabled: 'Yes', - }, - description: 'test course1', - idealScreenSize: 'normal', - createdOn: '2021-07-06T07:11:51.103+0000', - targetSubjectIds: ['nit_k-12_subject_english'], - copyrightYear: 2021, - contentDisposition: 'inline', - additionalCategories: ['Lesson Plan', 'Textbook'], - lastUpdatedOn: '2021-07-06T07:23:08.357+0000', - lastStatusChangedOn: '2021-07-06T07:11:51.103+0000', - createdFor: ['01309282781705830427'], - creator: 'N11', - os: ['All'], - targetFWIds: ['nit_k-12'], - versionKey: '1625556188357', - idealScreenDensity: 'hdpi', - framework: 'nit_k-12', - depth: 0, - createdBy: '5a587cc1-e018-4859-a0a8-e842650b9d64', - compatibilityLevel: 1, - userConsent: 'Yes', - timeLimits: { - questionSet: { - min: 0, - max: 300 - } - }, - resourceType: 'Course', - level: 1, - }, - }, - }, -]; - -export const categoryDefinitionData = { - id: 'api.object.category.definition.read', - ver: '3.0', - ts: '2021-07-07T17:25:31ZZ', - params: { - resmsgid: '8306af95-5259-4a3d-ab11-2d81586b8d30', - msgid: null, - err: null, - status: 'successful', - errmsg: null, - }, - responseCode: 'OK', - result: { - objectCategoryDefinition: { - identifier: 'obj-cat:course_collection_01309282781705830427', - objectMetadata: { - config: { - frameworkMetadata: { - orgFWType: ['K-12', 'TPD'], - targetFWType: ['K-12'], - }, - sourcingSettings: { - collection: { - maxDepth: 4, - objectType: 'Collection', - primaryCategory: 'Course', - isRoot: true, - iconClass: 'fa fa-book', - children: {}, - hierarchy: { - level1: { - name: 'Course Unit', - type: 'Unit', - mimeType: 'application/vnd.ekstep.content-collection', - contentType: 'CourseUnit', - primaryCategory: 'Course Unit', - iconClass: 'fa fa-folder-o', - children: {}, - }, - level2: { - name: 'Course Unit', - type: 'Unit', - mimeType: 'application/vnd.ekstep.content-collection', - contentType: 'CourseUnit', - primaryCategory: 'Course Unit', - iconClass: 'fa fa-folder-o', - children: { - Content: [], - }, - }, - level3: { - name: 'Course Unit', - type: 'Unit', - mimeType: 'application/vnd.ekstep.content-collection', - contentType: 'CourseUnit', - primaryCategory: 'Course Unit', - iconClass: 'fa fa-folder-o', - children: { - Content: [], - }, - }, - level4: { - name: 'Course Unit', - type: 'Unit', - mimeType: 'application/vnd.ekstep.content-collection', - contentType: 'CourseUnit', - primaryCategory: 'Course Unit', - iconClass: 'fa fa-folder-o', - children: { - Content: [], - }, - }, - }, - }, - }, - }, - schema: { - properties: { - trackable: { - type: 'object', - properties: { - enabled: { - type: 'string', - enum: ['Yes', 'No'], - default: 'Yes', - }, - autoBatch: { - type: 'string', - enum: ['Yes', 'No'], - default: 'Yes', - }, - }, - default: { - enabled: 'Yes', - autoBatch: 'Yes', - }, - additionalProperties: false, - }, - monitorable: { - type: 'array', - items: { - type: 'string', - enum: ['progress-report', 'score-report'], - }, - }, - credentials: { - type: 'object', - properties: { - enabled: { - type: 'string', - enum: ['Yes', 'No'], - default: 'Yes', - }, - }, - default: { - enabled: 'Yes', - }, - additionalProperties: false, - }, - userConsent: { - type: 'string', - enum: ['Yes', 'No'], - default: 'Yes', - }, - mimeType: { - type: 'string', - enum: ['application/vnd.ekstep.content-collection'], - } - }, - }, - }, - languageCode: [], - name: 'Course', - forms: { - create: { - templateName: '', - required: [], - properties: [ - { - name: 'First Section', - fields: [ - { - code: 'appIcon', - dataType: 'text', - description: 'appIcon of the content', - editable: true, - inputType: 'appIcon', - label: 'Icon', - name: 'Icon', - placeholder: 'Icon', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - required: true, - visible: true, - }, - { - code: 'name', - dataType: 'text', - description: 'Name of the content', - editable: true, - inputType: 'text', - label: 'Title', - name: 'Name', - placeholder: 'Title', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - required: true, - visible: true, - validations: [ - { - type: 'maxLength', - value: '120', - message: 'Input is Exceeded', - }, - { - type: 'required', - message: 'Title is required', - }, - ], - }, - { - code: 'description', - dataType: 'text', - description: 'Description of the content', - editable: true, - inputType: 'textarea', - label: 'Description', - name: 'Description', - placeholder: 'Description', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - required: false, - visible: true, - validations: [ - { - type: 'maxLength', - value: '256', - message: 'Input is Exceeded', - }, - ], - }, - { - code: 'keywords', - visible: true, - editable: true, - dataType: 'list', - name: 'Keywords', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - description: 'Keywords for the content', - inputType: 'keywords', - label: 'Keywords', - placeholder: 'Enter Keywords', - required: false, - validations: [], - }, - ], - }, - { - name: 'Second Section', - fields: [ - ], - }, - { - name: 'Third Section', - fields: [ - { - code: 'primaryCategory', - dataType: 'text', - description: 'Type', - editable: false, - renderingHints: {}, - inputType: 'select', - label: 'Category', - name: 'Type', - placeholder: '', - required: true, - visible: true, - validations: [], - }, - { - code: 'additionalCategories', - dataType: 'list', - depends: ['primaryCategory'], - description: 'Additonal Category of the Content', - editable: true, - inputType: 'nestedselect', - label: 'Additional Category', - name: 'Additional Category', - placeholder: 'Select Additional Category', - renderingHints: {}, - required: false, - visible: true, - }, - ], - }, - { - name: 'Organisation Framework Terms', - fields: [ - { - code: 'framework', - visible: true, - editable: true, - dataType: 'text', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - description: '', - label: 'Course Type', - required: true, - name: 'Framework', - inputType: 'framework', - placeholder: 'Select Course Type', - output: 'identifier', - validations: [ - { - type: 'required', - message: 'Course Type is required', - }, - ], - }, - { - code: 'subjectIds', - visible: true, - editable: true, - dataType: 'list', - depends: ['framework'], - sourceCategory: 'subject', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - description: '', - label: 'Subjects covered in the course', - required: true, - name: 'Subject', - inputType: 'frameworkCategorySelect', - placeholder: 'Select Subject(s)', - output: 'identifier', - validations: [ - { - type: 'required', - message: 'Subjects Taught is required', - }, - ], - }, - { - code: 'topicsIds', - visible: true, - editable: true, - dataType: 'list', - depends: ['framework', 'subjectIds'], - sourceCategory: 'topic', - renderingHints: {}, - name: 'Topic', - description: 'Choose a Topics', - inputType: 'topicselector', - label: 'Topics covered in the course', - placeholder: 'Choose Topics', - required: false, - output: 'identifier', - }, - ], - }, - { - name: 'Target Framework Terms', - fields: [ - { - code: 'audience', - dataType: 'list', - description: 'Audience', - editable: true, - inputType: 'nestedselect', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - label: 'Audience Type', - name: 'Audience Type', - placeholder: 'Select Audience Type', - required: false, - visible: true, - range: ['Student', 'Teacher', 'Parent', 'Administrator'], - }, - { - code: 'targetBoardIds', - visible: true, - depends: [], - editable: true, - dataType: 'list', - sourceCategory: 'board', - output: 'identifier', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - description: 'Board', - label: 'Board/Syllabus of the audience', - required: true, - name: 'Board/Syllabus', - inputType: 'select', - placeholder: 'Select Board/Syllabus', - validations: [ - { - type: 'required', - message: 'Board is required', - }, - ], - }, - { - code: 'targetMediumIds', - visible: true, - depends: ['targetBoardIds'], - editable: true, - dataType: 'list', - sourceCategory: 'medium', - output: 'identifier', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - description: '', - label: 'Medium(s) of the audience', - required: true, - name: 'Medium', - inputType: 'nestedselect', - placeholder: 'Select Medium', - validations: [ - { - type: 'required', - message: 'Medium is required', - }, - ], - }, - { - code: 'targetGradeLevelIds', - visible: true, - depends: ['targetBoardIds', 'targetMediumIds'], - editable: true, - dataType: 'list', - sourceCategory: 'gradeLevel', - output: 'identifier', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - description: 'Class', - label: 'Class(es) of the audience', - required: true, - name: 'Class', - inputType: 'nestedselect', - placeholder: 'Select Class', - validations: [ - { - type: 'required', - message: 'Class is required', - }, - ], - }, - { - code: 'targetSubjectIds', - visible: true, - depends: [ - 'targetBoardIds', - 'targetMediumIds', - 'targetGradeLevelIds', - ], - editable: true, - dataType: 'list', - sourceCategory: 'subject', - output: 'identifier', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - description: '', - label: 'Subject(s) of the audience', - required: true, - name: 'Subject', - inputType: 'nestedselect', - placeholder: 'Select Subject', - validations: [ - { - type: 'required', - message: 'Subject is required', - }, - ], - }, - ], - }, - { - name: 'Sixth Section', - fields: [ - { - code: 'author', - dataType: 'text', - description: 'Author of the content', - editable: true, - inputType: 'text', - label: 'Author', - name: 'Author', - placeholder: 'Author', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - required: false, - visible: true, - }, - { - code: 'attributions', - dataType: 'text', - description: 'Attributions', - editable: true, - inputType: 'text', - label: 'Attributions', - name: 'Attributions', - placeholder: 'Attributions', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - required: false, - visible: true, - }, - { - code: 'copyright', - dataType: 'text', - description: 'Copyright', - editable: true, - inputType: 'text', - label: 'Copyright', - name: 'Copyright & year', - placeholder: 'Copyright', - renderingHints: { - class: 'sb-g-col-lg-1 required', - }, - required: true, - visible: true, - validations: [ - { - type: 'required', - message: 'Copyright is required', - }, - ], - }, - { - code: 'copyrightYear', - dataType: 'text', - description: 'Year', - editable: true, - inputType: 'text', - label: 'Copyright Year', - name: 'Copyright Year', - placeholder: 'Copyright Year', + label: 'Suggested User Type', + name: 'userTypes', + placeholder: 'Suggested User Type', renderingHints: { - class: 'sb-g-col-lg-1 required', + class: 'sb-g-col-lg-1', }, - required: true, + required: false, visible: true, - validations: [ - { - type: 'required', - message: 'Copyright Year is required', - }, + range: [ + 'Education Official', + 'School leaders (HMs)', + 'Teachers', + 'Students', + 'Parents', + 'Parent', + 'Others', ], }, { - code: 'license', + code: 'description', + name: 'Description', + label: 'Description', + placeholder: 'Enter Description', + description: 'Description of the Question Set', dataType: 'text', - description: 'license', + inputType: 'textarea', editable: true, - inputType: 'select', - label: 'License', - name: 'license', - placeholder: 'Select License', + required: false, + visible: true, renderingHints: { - class: 'sb-g-col-lg-1 required', + class: 'sb-g-col-lg-1', }, - required: true, - visible: true, - defaultValue: 'CC BY 4.0', - validations: [ - { - type: 'required', - message: 'License is required', - }, - ], }, - ], - }, - ], - }, - delete: {}, - publish: {}, - review: {}, - search: { - templateName: '', - required: [], - properties: [ - { - code: 'primaryCategory', - dataType: 'list', - description: 'Type', - editable: true, - default: [], - renderingHints: { - class: 'sb-g-col-lg-1', - }, - inputType: 'nestedselect', - label: 'Content Type(s)', - name: 'Type', - placeholder: 'Select ContentType', - required: false, - visible: true, - }, - { - code: 'board', - visible: true, - depends: [], - editable: true, - dataType: 'list', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - description: 'Board', - label: 'Board', - required: false, - name: 'Board', - inputType: 'select', - placeholder: 'Select Board', - output: 'name', - }, - { - code: 'medium', - visible: true, - depends: ['board'], - editable: true, - dataType: 'list', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - description: '', - label: 'Medium(s)', - required: false, - name: 'Medium', - inputType: 'nestedselect', - placeholder: 'Select Medium', - output: 'name', - }, - { - code: 'gradeLevel', - visible: true, - depends: ['board', 'medium'], - editable: true, - default: '', - dataType: 'list', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - description: 'Class', - label: 'Class(es)', - required: false, - name: 'Class', - inputType: 'nestedselect', - placeholder: 'Select Class', - output: 'name', - }, - { - code: 'subject', - visible: true, - depends: ['board', 'medium', 'gradeLevel'], - editable: true, - default: '', - dataType: 'list', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - description: '', - label: 'Subject(s)', - required: false, - name: 'Subject', - inputType: 'nestedselect', - placeholder: 'Select Subject', - output: 'name', - }, - { - code: 'topic', - visible: true, - editable: true, - dataType: 'list', - depends: ['board', 'medium', 'gradeLevel', 'subject'], - default: '', - renderingHints: { - class: 'sb-g-col-lg-1', - }, - name: 'Topic', - description: 'Choose a Topics', - inputType: 'topicselector', - label: 'Topic(s)', - placeholder: 'Choose Topics', - required: false, - }, - ], - }, - unitMetadata: { - templateName: '', - required: [], - properties: [ - { - name: 'First Section', - fields: [ { - code: 'name', + code: 'allowScoring', + name: 'allowScoring', + label: 'Enable Scoring', + placeholder: 'Enable Scoring', + description: + 'Select to enable the option of scoring for questions in the form. Score can be added to questions only if this is selected', dataType: 'text', - description: 'Name of the content', + inputType: 'checkbox', editable: true, - inputType: 'text', - label: 'Title', - name: 'Title', - placeholder: 'Title', + required: false, + visible: true, renderingHints: { - class: 'sb-g-col-lg-1 required', + class: 'sb-g-col-lg-1', }, - required: true, - visible: true, - validations: [ - { - type: 'max', - value: '120', - message: 'Input is Exceeded', - }, - { - type: 'required', - message: 'Title is required', - }, - ], }, { - code: 'description', - dataType: 'text', - description: 'Description of the content', + code: 'evidenceMimeType', + dataType: 'list', + depends: ['showEvidence'], + description: 'Evidence', editable: true, - inputType: 'textarea', - label: 'Description', - name: 'Description', - placeholder: 'Description', + inputType: 'multiselect', + label: 'evidence', + name: 'evidenceMimeType', + placeholder: 'evidence', renderingHints: { class: 'sb-g-col-lg-1', }, required: false, visible: true, - validations: [ + range: [ { - type: 'max', - value: '256', - message: 'Input is Exceeded', + value: 'image/png', + label: 'image/png', }, + { value: 'audio/mp3', label: 'audio/mp3' }, + { value: 'video/mp4', label: 'video/mp4' }, + { value: 'video/webm', label: 'video/webm' }, ], }, { - code: 'keywords', - visible: true, - editable: true, + code: 'ecm', + name: 'ECM', + depends: ['allowECM'], + label: 'Select ECM\'s', + placeholder: 'Select ECM\'s', + description: 'ECM for the Observation with rubrics', dataType: 'list', - name: 'Keywords', + inputType: 'selectTextBox', + editable: true, + required: false, + visible: true, renderingHints: { class: 'sb-g-col-lg-1', }, - index: 3, - description: 'Keywords for the content', - inputType: 'keywords', - label: 'Keywords', - placeholder: 'Enter Keywords', - required: false, - validations: [], + options: [ + 'Student interview', + 'Teacher interview', + 'HM/HT interview', + 'Parent interview', + 'Official interview', + 'School walkthrough', + 'Class observation', + 'Document Review', + ], }, - { - code: 'topic', - visible: true, - depends: [], - editable: true, - dataType: 'list', - renderingHints: {}, - name: 'Topic', - description: 'Choose a Topics', - index: 11, - inputType: 'topicselector', - label: 'Topics', - placeholder: 'Choose Topics', - required: false, - validations: [], - } ], }, ], }, - update: {}, + search: { + properties: { code: 'name', editable: true }, + }, + searchConfig: { + properties: { code: 'name', editable: true }, + }, + childMetadata: { + properties: { code: 'name', editable: true }, + }, + relationalMetadata: { + properties: { code: 'name', editable: true }, + }, }, + label: 'Multiple Choice Question', }, }, }; +export const hierarchyResponse = [ + { + result: { + "questionset": { + "copyright": "NIT123", + "previewUrl": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/questionset/do_2138622515299368961170/do_2138622515299368961170_html_1692594234677.html", + "subject": [ + "Science" + ], + "channel": "01309282781705830427", + "downloadUrl": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/questionset/do_2138622515299368961170/short-text-questionset_1692594234115_do_2138622515299368961170_2.ecar", + "language": [ + "English" + ], + "mimeType": "application/vnd.sunbird.questionset", + "showHints": false, + "variants": { + "spine": { + "ecarUrl": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/questionset/do_2138622515299368961170/short-text-questionset_1692594233994_do_2138622515299368961170_2_SPINE.ecar", + "size": "7499" + }, + "online": { + "ecarUrl": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/questionset/do_2138622515299368961170/short-text-questionset_1692594234067_do_2138622515299368961170_2_ONLINE.ecar", + "size": "4304" + }, + "full": { + "ecarUrl": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/questionset/do_2138622515299368961170/short-text-questionset_1692594234115_do_2138622515299368961170_2.ecar", + "size": "5812749" + } + }, + "objectType": "QuestionSet", + "se_mediums": [ + "Hindi" + ], + "gradeLevel": [ + "Class 7" + ], + "appIcon": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/questionset/do_2138622515299368961170/artifact/sunbird.thumb.jpeg", + "primaryCategory": "Practice Question Set", + "contentEncoding": "gzip", + "generateDIALCodes": "No", + "se_gradeLevels": [ + "Class 7" + ], + "showSolutions": false, + "trackable": { + "enabled": "No", + "autoBatch": "No" + }, + "identifier": "do_2138622515299368961170", + "audience": [ + "Student" + ], + "visibility": "Default", + "showTimer": true, + "author": "Creator1", + "consumerId": "6968004d-c67e-434a-a350-773aa1e068a3", + "childNodes": [ + "do_2138622530437857281173", + "do_2138622518926049281171", + "do_2138622565343969281175" + ], + "lastPublishedBy": "ae94b68c-a535-4dce-8e7a-fb9662b0ad68", + "languageCode": [ + "en" + ], + "se_subjects": [ + "Science" + ], + "license": "CC BY 4.0", + "size": 5812749, + "lastPublishedOn": "2023-08-21T05:03:53.848+0000", + "name": "Short Text Questionset", + "allowBranching": "No", + "status": "Live", + "code": "7d5aaa70-ffb8-d062-ba10-1db445a11dbc", + "allowSkip": "Yes", + "containsUserData": "No", + "qumlVersion": 1.1, + "prevStatus": "Draft", + "description": "Short Text Questionset", + "medium": [ + "Hindi" + ], + "posterImage": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/content/assets/do_2138623258587217921213/sunbird.jpeg", + "createdOn": "2023-08-16T07:02:56.221+0000", + "pdfUrl": "https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/questionset/do_2138622515299368961170/do_2138622515299368961170_pdf_1692594234677.pdf", + "se_boards": [ + "ICSE" + ], + "scoreCutoffType": "AssessmentLevel", + "contentDisposition": "inline", + "lastUpdatedOn": "2023-08-21T05:03:55.457+0000", + "allowAnonymousAccess": "Yes", + "lastStatusChangedOn": "2023-08-21T05:03:55.457+0000", + "createdFor": [ + "01309282781705830427" + ], + "schemaVersion": "1.1", + "requiresSubmit": "No", + "summaryType": "Complete", + "se_FWIds": [ + "inquiry_k-12" + ], + "setType": "materialised", + "pkgVersion": 2, + "versionKey": "1692188886715", + "showFeedback": false, + "framework": "inquiry_k-12", + "depth": 0, + "createdBy": "5a587cc1-e018-4859-a0a8-e842650b9d64", + "compatibilityLevel": 5, + "navigationMode": "non-linear", + "timeLimits": { + "questionSet": { + "max": 300, + "min": 0 + } + }, + "shuffle": true, + "board": "ICSE" + } + } + } +]; + +export const categoryDefinitionData = { + "id": "api.object.category.definition.read", + "ver": "3.0", + "ts": "2023-08-17T10:40:09ZZ", + "params": { + "resmsgid": "3e4697ae-f129-4a41-bd5a-b8f5b68b9c8c", + "msgid": null, + "err": null, + "status": "successful", + "errmsg": null + }, + "responseCode": "OK", + "result": { + "objectCategoryDefinition": { + "identifier": "obj-cat:practice-question-set_questionset_all", + "objectMetadata": { + "config": { + "frameworkMetadata": { + "orgFWType": ['K-12', 'TPD'], + "targetFWType": ['K-12'], + }, + "sourcingSettings": { + "collection": { + "maxDepth": 1, + "addFromLibraryEnabled": true, + "enableAddFromLibrary": true, + "objectType": "QuestionSet", + "primaryCategory": "Practice Question Set", + "isRoot": true, + "iconClass": "fa fa-book", + "children": {}, + "hierarchy": { + "level1": { + "name": "Section", + "type": "Unit", + "mimeType": "application/vnd.sunbird.questionset", + "primaryCategory": "Practice Question Set", + "iconClass": "fa fa-folder-o", + "children": { + "Question": [ + "Multiple Choice Question", + "Subjective Question" + ] + } + } + }, + "questionSet": { + "maxQuestionsLimit": 5 + } + } + } + }, + "questionSet": { + "maxQuestionsLimit": 5 + }, + "schema": { + "properties": { + "mimeType": { + "type": "string", + "enum": [ + "application/vnd.sunbird.questionset" + ] + }, + "verticals": { + "type": "string", + "enum": [ + "Nipun Bharat", + "Adult Education", + "Vocational Education", + "CWSN", + "Virtual Labs" + ] + } + } + } + }, + "languageCode": [], + "name": "Practice Question Set", + "forms": { + "create": { + "templateName": "", + "required": [], + "properties": [ + { + "name": "Basic details", + "fields": [ + { + "code": "appIcon", + "name": "Icon", + "label": "Icon", + "placeholder": "Icon", + "description": "Icon for the question set", + "dataType": "text", + "inputType": "appIcon", + "editable": true, + "required": true, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1 required" + } + }, + { + "code": "name", + "name": "Name", + "label": "Name", + "placeholder": "Name", + "description": "Name of the QuestionSet", + "dataType": "text", + "inputType": "text", + "editable": true, + "required": true, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "max", + "value": "120", + "message": "Input is Exceeded" + }, + { + "type": "required", + "message": "Name is required" + } + ] + }, + { + "code": "description", + "name": "Description", + "label": "Description", + "placeholder": "Description", + "description": "Description of the content", + "dataType": "text", + "inputType": "textarea", + "editable": true, + "required": true, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "required", + "message": "description is required" + } + ] + }, + { + "code": "keywords", + "name": "Keywords", + "label": "keywords", + "placeholder": "Enter Keywords", + "description": "Keywords for the Question Set", + "dataType": "list", + "inputType": "keywords", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "instructions", + "name": "Instructions", + "label": "Instructions", + "placeholder": "Enter Instructions", + "description": "Instructions for the question set", + "dataType": "text", + "inputType": "richtext", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-2 required" + }, + "validations": [ + { + "type": "maxLength", + "value": "500", + "message": "Input is Exceeded" + } + ] + }, + { + "code": "primaryCategory", + "name": "Type", + "label": "Type", + "placeholder": "", + "description": "Type or Category of the Question Set", + "dataType": "text", + "inputType": "text", + "editable": false, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "additionalCategories", + "name": "Additional Category", + "label": "Additional Category", + "placeholder": "Select Additional Category", + "description": "Additonal Category of the Question Set", + "default": "", + "dataType": "list", + "inputType": "nestedselect", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + } + ] + }, + { + "name": "Framework details", + "fields": [ + { + "code": "board", + "name": "Board/Syllabus", + "label": "Board/Syllabus", + "placeholder": "Select Board/Syllabus", + "description": "Board or Syallbus of the Question Set", + "default": "", + "dataType": "text", + "inputType": "select", + "editable": true, + "required": true, + "visible": true, + "depends": [], + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "required", + "message": "Board is required" + } + ] + }, + { + "code": "medium", + "name": "Medium", + "label": "Medium", + "placeholder": "Select Medium", + "description": "Medium of Instruction for the Question Set", + "default": "", + "dataType": "list", + "inputType": "select", + "editable": true, + "required": true, + "visible": true, + "depends": [ + "board" + ], + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "required", + "message": "Medium is required" + } + ] + }, + { + "code": "gradeLevel", + "name": "Class", + "label": "Class", + "placeholder": "Select Class", + "description": "Class of the Question Set", + "default": "", + "dataType": "list", + "inputType": "select", + "editable": true, + "required": true, + "visible": true, + "depends": [ + "board", + "medium" + ], + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "required", + "message": "Class is required" + } + ] + }, + { + "code": "subject", + "name": "Subject", + "label": "Subject", + "placeholder": "Select Subject", + "description": "Subject of the Question Set", + "default": "", + "dataType": "list", + "inputType": "select", + "editable": true, + "required": true, + "visible": true, + "depends": [ + "board", + "medium", + "gradeLevel" + ], + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "required", + "message": "Subject is required" + } + ] + }, + { + "code": "audience", + "name": "Audience", + "label": "Audience", + "placeholder": "Select Audience", + "description": "Audience of the Question Set", + "dataType": "list", + "inputType": "select", + "editable": true, + "required": true, + "visible": true, + "range": [ + "Student", + "Teacher", + "Administrator" + ], + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "required", + "message": "Audience is required" + } + ] + } + ] + }, + { + "name": "Question set behaviour", + "fields": [ + { + "code": "maxTime", + "name": "MaxTimer", + "label": "Set Maximum Time", + "placeholder": "HH:mm:ss", + "description": "This is the maximum time allowed for the users to complete the assessment", + "default": "3600", + "dataType": "text", + "inputType": "timer", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + }, + "validations": [ + { + "type": "time", + "message": "Please enter in hh:mm:ss", + "value": "HH:mm:ss" + }, + { + "type": "max", + "value": "05:59:59", + "message": "max time should be less than 05:59:59" + } + ] + }, + { + "code": "showTimer", + "name": "show Timer", + "label": "show Timer", + "placeholder": "show Timer", + "description": "show Timer", + "default": false, + "dataType": "boolean", + "inputType": "checkbox", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "requiresSubmit", + "name": "Submit Confirmation", + "label": "Submit Confirmation Page", + "placeholder": "Select Submit Confirmation", + "description": "Allows users to review and submit the assessment", + "dataType": "text", + "inputType": "select", + "output": "identifier", + "range": [ + { + "identifier": "Yes", + "label": "Enable" + }, + { + "identifier": "No", + "label": "Disable" + } + ], + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "maxAttempts", + "name": "Max Attempts", + "label": "Max Attempts", + "placeholder": "Max Attempts", + "description": "Max Attempts", + "dataType": "number", + "inputType": "select", + "editable": true, + "required": false, + "visible": true, + "range": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25 + ], + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "summaryType", + "name": "summaryType", + "label": "Summary Type", + "placeholder": "Select Summary Type", + "description": "summaryType", + "dataType": "text", + "inputType": "select", + "editable": true, + "required": false, + "visible": true, + "range": [ + "Complete", + "Score", + "Duration", + "Score & Duration" + ], + "renderingHints": { + "class": "sb-g-col-lg-1" + } + } + ] + }, + { + "name": "Question set behaviour", + "fields": [ + { + "code": "author", + "name": "Author", + "label": "Author", + "placeholder": "Author", + "description": "Author of the question set", + "dataType": "text", + "inputType": "text", + "editable": true, + "required": true, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "validations": [ + { + "type": "required", + "message": "Author is required" + } + ] + }, + { + "code": "attributions", + "name": "Attributions", + "label": "Attributions", + "placeholder": "Enter Attributions", + "description": "Attributions of the question set", + "dataType": "text", + "inputType": "text", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "copyright", + "name": "Copyright & year", + "label": "Copyright & year", + "placeholder": "Copyright & year", + "description": "Copyright & year", + "dataType": "text", + "inputType": "text", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "license", + "name": "license", + "label": "license", + "placeholder": "Select license", + "description": "license", + "dataType": "text", + "inputType": "select", + "editable": true, + "required": false, + "visible": true, + "range": "", + "renderingHints": { + "class": "sb-g-col-lg-1" + } + } + ] + } + ] + }, + "publish": {}, + "searchConfig": { + "templateName": "", + "required": [], + "properties": [ + { + "code": "primaryCategory", + "dataType": "list", + "description": "Type", + "editable": true, + "default": [], + "renderingHints": { + "class": "sb-g-col-lg-1" + }, + "inputType": "nestedselect", + "label": "Question Type(s)", + "name": "Type", + "placeholder": "Select QuestionType", + "required": false, + "visible": true + }, + { + "code": "board", + "visible": true, + "depends": [], + "editable": true, + "dataType": "list", + "description": "Board", + "label": "Board", + "required": false, + "name": "Board", + "inputType": "select", + "placeholder": "Select Board", + "output": "name", + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "medium", + "visible": true, + "editable": true, + "dataType": "list", + "description": "", + "label": "Medium(s)", + "required": false, + "name": "Medium", + "inputType": "nestedselect", + "placeholder": "Select Medium", + "output": "name", + "depends": [ + "board" + ], + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "gradeLevel", + "visible": true, + "depends": [ + "board", + "medium" + ], + "editable": true, + "default": "", + "dataType": "list", + "renderingHints": { + "class": "sb-g-col-lg-1" + }, + "description": "Class", + "label": "Class(es)", + "required": false, + "name": "Class", + "inputType": "nestedselect", + "placeholder": "Select Class", + "output": "name" + }, + { + "code": "subject", + "visible": true, + "depends": [ + "board", + "medium", + "gradeLevel" + ], + "editable": true, + "default": "", + "dataType": "list", + "renderingHints": { + "class": "sb-g-col-lg-1" + }, + "description": "", + "label": "Subject(s)", + "required": false, + "name": "Subject", + "inputType": "nestedselect", + "placeholder": "Select Subject", + "output": "name" + } + ] + }, + "unitMetadata": { + "templateName": "", + "required": [], + "properties": [ + { + "code": "name", + "dataType": "text", + "description": "Name of the content", + "editable": true, + "inputType": "text", + "label": "Title", + "name": "Title", + "placeholder": "Title", + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "required": true, + "visible": true, + "validations": [ + { + "type": "max", + "value": "120", + "message": "Input is Exceeded" + }, + { + "type": "required", + "message": "Title is required" + } + ] + }, + { + "code": "description", + "dataType": "text", + "description": "Description of the content", + "editable": true, + "inputType": "textarea", + "label": "Description", + "name": "Description", + "placeholder": "Description", + "renderingHints": { + "class": "sb-g-col-lg-1 required" + }, + "required": true, + "visible": true, + "validations": [ + { + "type": "max", + "value": "500", + "message": "Input is Exceeded" + } + ] + }, + { + "code": "instructions", + "name": "Instructions", + "label": "Instructions", + "placeholder": "Enter Instructions", + "description": "Instructions for the section", + "dataType": "text", + "inputType": "richtext", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-2" + }, + "validations": [ + { + "type": "maxLength", + "value": "500", + "message": "Input is Exceeded" + } + ] + }, + { + "code": "maxQuestions", + "name": "Show Questions", + "label": "Count of questions to be displayed in this section", + "placeholder": "Input count of questions to be displayed", + "description": "By default all questions are shown unless specific count is entered.", + "default": "", + "dataType": "number", + "inputType": "select", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "shuffle", + "name": "Shuffle Questions", + "label": "Shuffle Questions", + "placeholder": "Shuffle Questions", + "description": "If shuffle questions is selected, users are presented with questions in a random order whenever they attempt the assessment", + "default": "false", + "dataType": "boolean", + "inputType": "checkbox", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "showFeedback", + "name": "Show Feedback", + "label": "Show Question Feedback", + "placeholder": "Select Option", + "description": "If feedback is selected, users are informed whether they have correctly answered question or not", + "dataType": "boolean", + "inputType": "checkbox", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + }, + { + "code": "showSolutions", + "name": "Show Solution", + "label": "Show Solution", + "placeholder": "Select Option", + "description": "If show solution is selected then solutions for each question will be shown to the user", + "dataType": "boolean", + "inputType": "checkbox", + "editable": true, + "required": false, + "visible": true, + "renderingHints": { + "class": "sb-g-col-lg-1" + } + } + ] + } + } + } + } +}; + export const SelectedNodeMockData = { data: { id: 'do_1134346930267422721115', @@ -1867,26 +1606,6 @@ export const SelectedNodeMockData = { }, }; -export const outcomeDeclarationData = { - questionset: { - identifier: 'do_1134357224765685761203', - outcomeDeclaration: { - levels: { - L1: { - label: 'Good', - }, - L2: { - label: 'Average', - }, - L3: { - label: 'Bad', - }, - }, - }, - languageCode: ['en'], - }, -}; - export const BranchingLogicData = { do_113432866096922624110: { target: ['do_113432866799935488112', 'do_113432921842950144114'], @@ -3787,9 +3506,7 @@ export const nodesModifiedData = { appIcon: '', name: 'questionset', description: 'questionset', - instructions: { - default: '

quuestionset

' - }, + instructions: '

quuestionset

', primaryCategory: 'Practice Question Set', additionalCategories: [], board: 'CBSE', @@ -4164,10 +3881,6 @@ export const questionSetEditorConfig = { questionSet: { maxQuestionsLimit: 500 }, - collection: { - maxContentsLimit: 1200 - }, - publicStorageAccount: 'https://sunbirddev.blob.core.windows.net/', maxDepth: 1, addFromLibraryEnabled: true, enableAddFromLibrary: true, @@ -4194,21 +3907,6 @@ export const questionSetEditorConfig = { } }; -export const mockOutcomeDeclaration = { - result: { - questionset: { - identifier: 'do_1234', - outcomeDeclaration: { - levels: { - L1: { - label: 'Good' - } - } - } - } -} -}; - export const frameworkData = { tpd: { identifier: 'tpd', @@ -4291,3 +3989,28 @@ export const serverResponse = { ver: '', headers: {} }; + +export const categoryDefinitionPublishCheckList = { + "result": { + "objectCategoryDefinition": { + "name": "Practice Question Set", + "forms": { + "publishchecklist": { + "templateName": "", + "required": [], + "properties": [{ + name: 'publish check lists', + fields: [{ + "code": "Quality Check", + "name": "Quality Check", + "label": "Quality Check", + "default": "false", + "dataType": "boolean", + "inputType": "checkbox" + }] + }] + } + } + } + } +}; diff --git a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.ts b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.ts index f93f897de..17b0c97a5 100755 --- a/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/editor/editor.component.spec.ts @@ -11,11 +11,11 @@ import { TreeService } from '../../services/tree/tree.service'; import { editorConfig, editorConfig_question, toolbarConfig_question, nativeElement, getCategoryDefinitionResponse, hierarchyResponse, - categoryDefinition, categoryDefinitionData, csvExport, hirearchyGet, - SelectedNodeMockData, outcomeDeclarationData, observationAndRubericsField, + categoryDefinition, categoryDefinitionData, + SelectedNodeMockData, observationAndRubericsField, questionsetRead, questionsetHierarchyRead, nodesModifiedData, treeNodeData, - questionSetEditorConfig, mockOutcomeDeclaration, - frameworkData, serverResponse} from './editor.component.spec.data'; + questionSetEditorConfig, categoryDefinitionPublishCheckList, + frameworkData, serverResponse } from './editor.component.spec.data'; import { ConfigService } from '../../services/config/config.service'; import { BehaviorSubject, Observable, of, throwError } from 'rxjs'; import { treeData } from './../fancy-tree/fancy-tree.component.spec.data'; @@ -81,7 +81,6 @@ describe('EditorComponent', () => { expect(component.unitFormConfig).toBeUndefined(); expect(component.searchFormConfig).toBeUndefined(); expect(component.leafFormConfig).toBeUndefined(); - expect(component.showLibraryPage).toBeFalsy(); expect(component.questionlibraryInput).toEqual({}); expect(component.isQumlPlayer).toBeUndefined(); expect(component.showQuestionTemplatePopup).toBeFalsy(); @@ -91,12 +90,8 @@ describe('EditorComponent', () => { expect(component.buttonLoaders.saveAsDraftButtonLoader).toBeFalsy(); expect(component.buttonLoaders.addFromLibraryButtonLoader).toBeFalsy(); expect(component.buttonLoaders.showReviewComment).toBeFalsy(); - expect(component.csvDropDownOptions).toEqual({}); - expect(component.showCsvUploadPopup).toBeFalsy(); - expect(component.isCreateCsv).toBeTruthy(); expect(component.isStatusReviewMode).toBeFalsy(); expect(component.ishierarchyConfigSet).toBeFalsy(); - expect(component.isComponenetInitialized).toBeFalsy(); }); it('#setEditorConfig() should set editor config', () => { @@ -106,27 +101,29 @@ describe('EditorComponent', () => { component.setEditorConfig(); }) - it('#ngOnInit() should call all methods inside it (for objectType Collection)', () => { + it('#ngOnInit() should call all methods inside it (for objectType QuestionSet)', () => { const sampleEditorConfig: any = JSON.stringify(editorConfig); component.editorConfig = sampleEditorConfig; - component.objectType = 'questionSet'; + component.objectType = 'questionset'; const editorService = TestBed.inject(EditorService); spyOn(editorService, 'initialize').and.callThrough(); const treeService = TestBed.inject(TreeService); spyOn(treeService, 'initialize').and.callThrough(); const configService = TestBed.inject(ConfigService); component.configService = configService; - spyOn(component, 'isReviewMode').and.returnValue(true); + spyOn(editorService, 'getToolbarConfig').and.callThrough(); + spyOn(component, 'isReviewMode').and.returnValue(false); spyOn(component, 'mergeCollectionExternalProperties').and.returnValue(of(hierarchyResponse)); spyOn(component, 'initializeFrameworkAndChannel').and.callFake(() => {}); - spyOn(editorService, 'getCategoryDefinition').and.returnValue(of(getCategoryDefinitionResponse)); + spyOn(editorService, 'getCategoryDefinition').and.returnValue(of(categoryDefinitionData)); + spyOn(component, 'sethierarchyConfig').and.callFake(() => {}); const telemetryService = TestBed.inject(EditorTelemetryService); spyOn(telemetryService, 'initializeTelemetry').and.callFake(() => { }); spyOn(telemetryService, 'start').and.callFake(() => { }); - const libraryPage: EventEmitter = new EventEmitter(); const questionLibraryPage: EventEmitter = new EventEmitter(); spyOn(editorService, 'getshowQuestionLibraryPageEmitter').and.callFake(() => {return questionLibraryPage}); spyOn(component, 'showQuestionLibraryComponentPage').and.callFake(() => {}); + spyOn(component, 'getFrameworkDetails').and.callFake(() => {}) component.ngOnInit(); expect(component.editorConfig).toBeDefined(); expect(editorService.initialize).toHaveBeenCalledWith(editorConfig); @@ -134,21 +131,16 @@ describe('EditorComponent', () => { expect(component.editorMode).toEqual('edit'); expect(treeService.initialize).toHaveBeenCalledWith(editorConfig); expect(component.collectionId).toBeDefined(); - expect(component.isObjectTypeCollection).toBeTruthy(); expect(component.isReviewMode).toHaveBeenCalled(); - expect(component.isStatusReviewMode).toBeTruthy(); + expect(component.isStatusReviewMode).toBeFalsy(); expect(component.mergeCollectionExternalProperties).toHaveBeenCalled(); expect(component.toolbarConfig).toBeDefined(); - expect(component.toolbarConfig.title).toEqual(hierarchyResponse[0].result.content.name); + expect(component.toolbarConfig.title).toEqual(hierarchyResponse[0].result.questionset.name); expect(component.initializeFrameworkAndChannel).toHaveBeenCalled(); expect(editorService.getCategoryDefinition).toHaveBeenCalled(); expect(telemetryService.initializeTelemetry).toHaveBeenCalled(); expect(telemetryService.telemetryPageId).toEqual('questionset_editor'); expect(telemetryService.start).toHaveBeenCalled(); - // expect(editorService.getshowLibraryPageEmitter).toHaveBeenCalled(); - // expect(component.showLibraryComponentPage).toHaveBeenCalled(); - // expect(editorService.getshowQuestionLibraryPageEmitter).toHaveBeenCalled(); - // expect(component.showQuestionLibraryComponentPage).toHaveBeenCalled(); }); it('#ngOnInit() should call all methods inside it (for objectType Question)', () => { @@ -216,67 +208,201 @@ describe('EditorComponent', () => { }); it('Unit test for #getFrameworkDetails()', () => { - const treeService = TestBed.inject(TreeService); - const frameworkService = TestBed.inject(FrameworkService); - const editorService = TestBed.inject(EditorService); - component.organisationFramework = 'dummy'; - spyOn(component, 'getFrameworkDetails').and.callThrough(); - spyOn(treeService, 'updateMetaDataProperty').and.callFake(() => { }); - spyOn(frameworkService, 'getTargetFrameworkCategories').and.callFake(() => { }); - spyOn(frameworkService, 'getFrameworkData').and.returnValue(of(serverResponse)); - spyOn(component, 'setEditorForms').and.callFake(() => { }); + spyOn(component, 'setPublishCheckList').and.callFake(() => {}); + component.targetFramework = ''; + spyOn(component, 'setTargetFrameworkData').and.callFake(() => {}); + spyOn(component, 'setOrgFrameworkData').and.callFake(()=> {}); component.getFrameworkDetails(categoryDefinitionData); - expect(treeService.updateMetaDataProperty).not.toHaveBeenCalled(); - expect(frameworkService.getTargetFrameworkCategories).not.toHaveBeenCalled(); - expect(frameworkService.getFrameworkData).toHaveBeenCalled(); - expect(component.targetFramework).toBeUndefined(); - expect(treeService.updateMetaDataProperty).not.toHaveBeenCalled(); - expect(frameworkService.getTargetFrameworkCategories).not.toHaveBeenCalled(); + expect(component.setPublishCheckList).toHaveBeenCalled(); + expect(component.setTargetFrameworkData).toHaveBeenCalled(); + expect(component.setOrgFrameworkData).toHaveBeenCalled(); + }); + + it('unit test for #setOrgFrameworkData() having channelFrameworksType same as orgFWType', () => { + spyOn(component, 'setOrgFrameworkData').and.callThrough(); + const helperService = TestBed.inject(HelperService); + spyOnProperty(helperService, 'channelInfo').and.returnValue(questionSetEditorConfig.context.channelData); + const frameworkService = TestBed.inject(FrameworkService); + frameworkService.frameworkValues = undefined; + spyOn(component, 'setEditorForms').and.callFake(() => {}); + component.setOrgFrameworkData(categoryDefinitionData); + expect(frameworkService.frameworkValues).toBeDefined(); expect(component.setEditorForms).toHaveBeenCalled(); }); - it('Unit test for #getFrameworkDetails() when primaryCategory is Obs with rubrics api success', () => { - const treeService = TestBed.inject(TreeService); + it('unit test for #setOrgFrameworkData() having channelFrameworksType not same as orgFWType', () => { + spyOn(component, 'setOrgFrameworkData').and.callThrough(); + const helperService = TestBed.inject(HelperService); + let channelData = {frameworks: ['NIT']} + spyOnProperty(helperService, 'channelInfo').and.returnValue(channelData); const frameworkService = TestBed.inject(FrameworkService); - const editorService = TestBed.inject(EditorService); - component.organisationFramework = 'dummy'; - editorConfig.config.renderTaxonomy = true; - component.editorConfig = editorConfig; - spyOn(editorService, 'fetchOutComeDeclaration').and.returnValue(of(mockOutcomeDeclaration)); - spyOn(component, 'getFrameworkDetails').and.callThrough(); - spyOn(treeService, 'updateMetaDataProperty').and.callFake(() => { }); - spyOn(frameworkService, 'getTargetFrameworkCategories').and.callFake(() => { }); - spyOn(frameworkService, 'getFrameworkData').and.returnValue(of(serverResponse)); - spyOn(component, 'setEditorForms').and.callFake(() => { }); - component.getFrameworkDetails(categoryDefinitionData); - expect(treeService.updateMetaDataProperty).not.toHaveBeenCalled(); - expect(frameworkService.getTargetFrameworkCategories).not.toHaveBeenCalled(); - expect(component.targetFramework).toBeUndefined(); - expect(treeService.updateMetaDataProperty).not.toHaveBeenCalled(); - expect(frameworkService.getTargetFrameworkCategories).not.toHaveBeenCalled(); + let frameworkResponse = serverResponse; + frameworkResponse.result = { Framework: [{ + name: 'NIT', + identifier: 'nit-12', + objectType: 'Framework', + status: 'Live', + type: 'nit' + }] + } + spyOn(frameworkService, 'getFrameworkData').and.returnValue(of(frameworkResponse)) + spyOn(component, 'setEditorForms').and.callFake(() => {}); + component.setOrgFrameworkData(categoryDefinitionData); + expect(component.setEditorForms).toHaveBeenCalled(); }); - it('Unit test for #getFrameworkDetails() when primaryCategory is Obs with rubrics outcome declaration api fail', () => { - const treeService = TestBed.inject(TreeService); + it('unit test for #setOrgFrameworkData() when orgFWIdentifiers is set', () => { + const categoryDefResponse = { + result: { + objectCategoryDefinition: { + objectMetadata: { + schema: {properties: {'framework': {default: 'nit-12'}}} + } + } + } + }; + spyOn(component, 'setOrgFrameworkData').and.callThrough(); + const helperService = TestBed.inject(HelperService); + let channelData = {frameworks: ['NIT']} + spyOnProperty(helperService, 'channelInfo').and.returnValue(channelData); const frameworkService = TestBed.inject(FrameworkService); - const editorService = TestBed.inject(EditorService); - component.organisationFramework = 'dummy'; - editorConfig.config.renderTaxonomy = true; - component.editorConfig = editorConfig; - spyOn(editorService, 'fetchOutComeDeclaration').and.returnValue(throwError('error')); - spyOn(component, 'getFrameworkDetails').and.callThrough(); - spyOn(treeService, 'updateMetaDataProperty').and.callFake(() => { }); - spyOn(frameworkService, 'getTargetFrameworkCategories').and.callFake(() => { }); - spyOn(frameworkService, 'getFrameworkData').and.returnValue(of(serverResponse)); - spyOn(component, 'setEditorForms').and.callFake(() => { }); - component.getFrameworkDetails(categoryDefinitionData); - expect(treeService.updateMetaDataProperty).not.toHaveBeenCalled(); - expect(frameworkService.getTargetFrameworkCategories).not.toHaveBeenCalled(); - expect(component.targetFramework).toBeUndefined(); - expect(treeService.updateMetaDataProperty).not.toHaveBeenCalled(); - expect(frameworkService.getTargetFrameworkCategories).not.toHaveBeenCalled(); + let frameworkResponse = serverResponse; + frameworkResponse.result = { Framework: [{ + name: 'NIT', + identifier: 'nit-12', + objectType: 'Framework', + status: 'Live', + type: 'nit' + }] + } + spyOn(frameworkService, 'getFrameworkData').and.returnValue(of(frameworkResponse)) + spyOn(component, 'setEditorForms').and.callFake(() => {}); + component.setOrgFrameworkData(categoryDefResponse); + expect(component.setEditorForms).toHaveBeenCalled(); }); + it('unit test for #setTargetFrameworkData() having channelFrameworksType same as targetFWType', () => { + const categoryDefResponse = { + result: { + objectCategoryDefinition: { + objectMetadata: { + config: { + frameworkMetadata: { + targetFWType: ['TPD'] + }} + } + } + } + }; + spyOn(component, 'setTargetFrameworkData').and.callThrough(); + const helperService = TestBed.inject(HelperService); + let channelData = questionSetEditorConfig.context.channelData; + channelData.frameworks = [{ + name: 'nit_tpd', + relation: 'hasSequenceMember', + identifier: 'nit_tpd', + description: 'nit_tpd Framework', + objectType: 'Framework', + status: 'Live', + type: 'TPD' + }]; + spyOnProperty(helperService, 'channelInfo').and.returnValue(channelData); + const treeService= TestBed.inject(TreeService); + spyOn(treeService, 'updateMetaDataProperty').and.callFake(() => {}); + const frameworkService = TestBed.inject(FrameworkService); + spyOn(frameworkService, 'getTargetFrameworkCategories').and.callFake(() => {}); + component.setTargetFrameworkData(categoryDefResponse); + expect(treeService.updateMetaDataProperty).toHaveBeenCalled(); + expect(frameworkService.getTargetFrameworkCategories).toHaveBeenCalled(); + }); + + it('unit test for #setTargetFrameworkData() having channelFrameworksType not same as targetFWType', () => { + const categoryDefResponse = { + result: { + objectCategoryDefinition: { + objectMetadata: { + config: { + frameworkMetadata: { + targetFWType: ['TPD', 'NCERT'] + }} + } + } + } + }; + spyOn(component, 'setTargetFrameworkData').and.callThrough(); + const helperService = TestBed.inject(HelperService); + let channelData = questionSetEditorConfig.context.channelData; + channelData.frameworks = [{ + name: 'nit_tpd', + relation: 'hasSequenceMember', + identifier: 'nit_tpd', + description: 'nit_tpd Framework', + objectType: 'Framework', + status: 'Live', + type: 'TPD' + }]; + spyOnProperty(helperService, 'channelInfo').and.returnValue(channelData); + const treeService= TestBed.inject(TreeService); + spyOn(treeService, 'updateMetaDataProperty').and.callFake(() => {}); + const frameworkService = TestBed.inject(FrameworkService); + let frameworkResponse = serverResponse; + frameworkResponse.result = { Framework: [{ + name: 'CBSE', + identifier: 'ncert-k12', + objectType: 'Framework', + status: 'Live', + type: 'NCERT' + }] + } + spyOn(frameworkService, 'getFrameworkData').and.returnValue(of(frameworkResponse)) + spyOn(frameworkService, 'getTargetFrameworkCategories').and.callFake(() => {}); + component.setTargetFrameworkData(categoryDefResponse); + expect(treeService.updateMetaDataProperty).toHaveBeenCalled(); + expect(frameworkService.getTargetFrameworkCategories).toHaveBeenCalled(); + }); + + it('unit test for #setTargetFrameworkData() when targetFWIdentifiers is set', () => { + const categoryDefResponse = { + result: { + objectCategoryDefinition: { + objectMetadata: { + schema: {properties: {'targetFWIds': {default: 'nit-12'}}} + } + } + } + }; + spyOn(component, 'setTargetFrameworkData').and.callThrough(); + const helperService = TestBed.inject(HelperService); + let channelData = {frameworks: ['NIT']} + spyOnProperty(helperService, 'channelInfo').and.returnValue(channelData); + const treeService= TestBed.inject(TreeService); + spyOn(treeService, 'updateMetaDataProperty').and.callFake(() => {}); + const frameworkService = TestBed.inject(FrameworkService); + let frameworkResponse = serverResponse; + frameworkResponse.result = { Framework: [{ + name: 'NIT', + identifier: 'nit-12', + objectType: 'Framework', + status: 'Live', + type: 'nit' + }] + } + spyOn(frameworkService, 'getFrameworkData').and.returnValue(of(frameworkResponse)); + spyOn(frameworkService, 'getTargetFrameworkCategories').and.callFake(() => {}); + spyOn(component, 'setEditorForms').and.callFake(() => {}); + component.setTargetFrameworkData(categoryDefResponse); + expect(treeService.updateMetaDataProperty).toHaveBeenCalled(); + expect(frameworkService.getTargetFrameworkCategories).toHaveBeenCalled(); + }); + + it('Unit test for #setPublishCheckList()', () => { + component.publishchecklist = undefined; + spyOn(component, 'setPublishCheckList').and.callThrough(); + component.setPublishCheckList(categoryDefinitionPublishCheckList); + expect(component.publishchecklist).toBeDefined(); + + }) + it('#setEditorForms() should set variable values for questionset', () => { component.objectType = 'questionset'; spyOn(component, 'setEditorForms').and.callThrough(); @@ -318,14 +444,12 @@ describe('EditorComponent', () => { }); it('#ngAfterViewInit() should call #impression()', () => { - component.isComponenetInitialized = false; const telemetryService = TestBed.inject(EditorTelemetryService); telemetryService.telemetryPageId = 'questionset_editor'; spyOn(telemetryService, 'impression').and.callFake(() => { }); spyOn(component, 'ngAfterViewInit').and.callThrough(); component.ngAfterViewInit(); expect(telemetryService.impression).toHaveBeenCalled(); - expect(component.isComponenetInitialized).toBeTruthy(); }); it('#mergeCollectionExternalProperties() should call fetchCollectionHierarchy for objectType questionset', () => { @@ -341,7 +465,6 @@ describe('EditorComponent', () => { expect(editorService.fetchCollectionHierarchy).toHaveBeenCalled(); expect(editorService.readQuestionSet).toHaveBeenCalled(); expect(component.collectionTreeNodes).toBeDefined(); - expect(component.isTreeInitialized).toBeTruthy(); }); it('#mergeCollectionExternalProperties() should call fetchCollectionHierarchy for objectType collection', () => { @@ -661,15 +784,11 @@ describe('EditorComponent', () => { }); it('#libraryEventListener() should set pageId to questionset_editor', async () => { - component.isEnableCsvAction = false; - component.isComponenetInitialized = false; const res = {}; spyOn(component, 'mergeCollectionExternalProperties').and.returnValue(of(res)); spyOn(component, 'libraryEventListener').and.callThrough(); component.libraryEventListener({}); expect(component.pageId).toEqual('questionset_editor'); - expect(component.isEnableCsvAction).toBeTruthy(); - expect(component.isComponenetInitialized).toBeTruthy(); }); it('#onQuestionLibraryChange() should call #addResourceToQuestionset()', () => { @@ -1001,7 +1120,6 @@ describe('EditorComponent', () => { } } }; - component.isObjectTypeCollection = false; component.editorConfig = editorConfig; spyOn(component, 'updateTreeNodeData').and.callFake(() => { return true; @@ -1227,7 +1345,6 @@ describe('EditorComponent', () => { }); it('#questionEventListener() should set #pageId to questionset_editor', () => { - component.isEnableCsvAction = false; component.telemetryService.telemetryPageId = ''; component.objectType = 'questionSet'; spyOn(component, 'mergeCollectionExternalProperties').and.returnValue(of({})); @@ -1235,7 +1352,6 @@ describe('EditorComponent', () => { component.questionEventListener({type: 'createNewContent'}); expect(component.pageId).toEqual('questionset_editor'); expect(component.telemetryService.telemetryPageId).toEqual('questionset_editor'); - expect(component.isEnableCsvAction).toBeTruthy(); }); it('#questionEventListener() should emit event for objectType question', () => { @@ -1291,149 +1407,12 @@ describe('EditorComponent', () => { expect(result).toBeTruthy(); }); - it('#handleCsvDropdownOptionsOnCollection should set dropdown status initially', () => { - spyOn(component, 'setCsvDropDownOptionsDisable').and.callFake(() => {}); - component.isTreeInitialized = true; - component.handleCsvDropdownOptionsOnCollection(); - expect(component.isEnableCsvAction).toBeTruthy(); - expect(component.isTreeInitialized).toBeFalsy(); - expect(component.setCsvDropDownOptionsDisable).toHaveBeenCalledWith(true); - }); - - it('#handleCsvDropdownOptionsOnCollection should set isEnableCsvAction status false', () => { - component.isEnableCsvAction = true; - component.isTreeInitialized = false; - spyOn(component, 'setCsvDropDownOptionsDisable').and.callFake(() => {}); - component.handleCsvDropdownOptionsOnCollection(); - expect(component.isEnableCsvAction).toBeFalsy(); - expect(component.isTreeInitialized).toBeFalsy(); - expect(component.setCsvDropDownOptionsDisable).toHaveBeenCalledWith(true); - }); - - it('#onClickFolder() should call setCsvDropDownOptionsDisable when isComponenetInitialized is true', () => { - component.isComponenetInitialized = true; - spyOn(component, 'setCsvDropDownOptionsDisable').and.callFake(() => {}); - spyOn(component, 'onClickFolder').and.callThrough(); - component.onClickFolder(); - expect(component.setCsvDropDownOptionsDisable).toHaveBeenCalledWith(); - expect(component.isComponenetInitialized).toBeFalsy(); - }); - - it('#onClickFolder() should call setCsvDropDownOptionsDisable when isEnableCsvAction is true', () => { - component.isEnableCsvAction = true; - component.isComponenetInitialized = false; - spyOn(component, 'setCsvDropDownOptionsDisable').and.callFake(() => {}); - component.onClickFolder(); - expect(component.setCsvDropDownOptionsDisable).toHaveBeenCalledWith(); - }); - - it('#onClickFolder() should not call setCsvDropDownOptionsDisable', () => { - component.isEnableCsvAction = false; - component.isComponenetInitialized = false; - spyOn(component, 'setCsvDropDownOptionsDisable').and.callFake(() => {}); - component.onClickFolder(); - expect(component.setCsvDropDownOptionsDisable).not.toHaveBeenCalledWith(); - }); - - it('#setCsvDropDownOptionsDisable and should set csv dropdown options', () => { - component.csvDropDownOptions = { - isDisableCreateCsv: true, - isDisableUpdateCsv: true, - isDisableDownloadCsv: true - }; - // tslint:disable-next-line:no-string-literal - spyOn(component['editorService'], 'getHierarchyFolder').and.returnValue([1]); - component.setCsvDropDownOptionsDisable(true); - expect(component.csvDropDownOptions.isDisableCreateCsv).toBeTruthy(); - expect(component.csvDropDownOptions.isDisableUpdateCsv).toBeTruthy(); - expect(component.csvDropDownOptions.isDisableDownloadCsv).toBeTruthy(); - }); - - it('#setCsvDropDownOptionsDisable and should set csv dropdown options for empty childs', () => { - component.csvDropDownOptions = { - isDisableCreateCsv: true, - isDisableUpdateCsv: true, - isDisableDownloadCsv: true - }; - // tslint:disable-next-line:no-string-literal - spyOn(component['editorService'], 'getHierarchyFolder').and.returnValue([]); - component.setCsvDropDownOptionsDisable(); - expect(component.csvDropDownOptions.isDisableCreateCsv).toBeFalsy(); - expect(component.csvDropDownOptions.isDisableUpdateCsv).toBeTruthy(); - expect(component.csvDropDownOptions.isDisableDownloadCsv).toBeTruthy(); - }); - - it('#downloadHierarchyCsv() should call downloadHierarchyCsv and success case', () => { - // tslint:disable-next-line:no-string-literal - spyOn(component['editorService'], 'downloadHierarchyCsv').and.returnValue(of(csvExport.successExport)); - spyOn(component, 'downloadCSVFile').and.callThrough(); - component.downloadHierarchyCsv(); - expect(component.downloadCSVFile).toHaveBeenCalledWith(csvExport.successExport.result.collection.tocUrl); - }); - - it('#downloadHierarchyCsv() should call downloadHierarchyCsv and error case', () => { - component.collectionId = 'do_11331581945782272012'; - spyOn(toasterService, 'error').and.callFake(() => {}); - // tslint:disable-next-line:no-string-literal - spyOn(component['editorService'], 'downloadHierarchyCsv').and.returnValue(throwError(csvExport.errorExport)); - spyOn(component, 'downloadCSVFile').and.callThrough(); - component.downloadHierarchyCsv(); - expect(toasterService.error).toHaveBeenCalled(); - }); - it('#isReviewMode should return editor mode status', () => { spyOn(component, 'isReviewMode').and.returnValue(true); const value = component.isReviewMode(); expect(value).toBeTruthy(); }); - it('#downloadFile() should download the file', () => { - component.collectionId = 'do_113274017771085824116'; - // tslint:disable-next-line:max-line-length - const blobUrl = 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/course/toc/do_11331579492804198413_untitled-course_1625465046239.csv'; - const editorService = TestBed.inject(EditorService); - // spyOn(window, 'open').and.callFake(() => {}); - component.downloadCSVFile(blobUrl); - // expect(window.open).toHaveBeenCalled(); - }); - - it('#hanndleCsvEmitter should check for closeModal conditions', () => { - const event = { type: 'closeModal' }; - component.hanndleCsvEmitter(event); - expect(component.showCsvUploadPopup).toBeFalsy(); - }); - - it('#hanndleCsvEmitter should check for downloadCsv conditions', () => { - spyOn(component, 'mergeCollectionExternalProperties').and.returnValue(of(hirearchyGet)); - const event = { type: 'updateHierarchy' }; - component.hanndleCsvEmitter(event); - expect(component.mergeCollectionExternalProperties).toHaveBeenCalled(); - expect(component.pageId).toEqual('questionset_editor'); - expect(component.telemetryService.telemetryPageId).toEqual('questionset_editor'); - expect(component.isEnableCsvAction).toBeTruthy(); - }); - - it('#hanndleCsvEmitter should check for create csv conditions', () => { - const event = { type: 'createCsv' }; - component.hanndleCsvEmitter(event); - expect(component.showCsvUploadPopup).toBeTruthy(); - expect(component.isCreateCsv).toBeTruthy(); - }); - - it('#hanndleCsvEmitter should check for updateCsv conditions', () => { - const event = { type: 'updateCsv' }; - component.hanndleCsvEmitter(event); - expect(component.showCsvUploadPopup).toBeTruthy(); - expect(component.isCreateCsv).toBeFalsy(); - }); - - it('#hanndleCsvEmitter should check for downloadCsv conditions', () => { - spyOn(component, 'downloadHierarchyCsv').and.callFake(() => {}); - const event = { type: 'downloadCsv' }; - component.hanndleCsvEmitter(event); - expect(component.downloadHierarchyCsv).toHaveBeenCalled(); - }); - it('#onFormStatusChange() should store form status when form state changed', () => { const formStatus = {isValid: true}; const expectedResult = { do_12345 : true }; diff --git a/projects/questionset-editor-library/src/lib/components/editor/editor.component.ts b/projects/questionset-editor-library/src/lib/components/editor/editor.component.ts index 4dc80e750..4c00fa961 100755 --- a/projects/questionset-editor-library/src/lib/components/editor/editor.component.ts +++ b/projects/questionset-editor-library/src/lib/components/editor/editor.component.ts @@ -45,7 +45,6 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { public searchFormConfig: any; public leafFormConfig: any; public relationFormConfig: any; - public showLibraryPage = false; public questionlibraryInput: any = {}; public editorMode; public collectionId; @@ -71,26 +70,16 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { showReviewComment: false }; public contentComment: string; - public showComment: boolean; public showReviewModal: boolean; - public csvDropDownOptions: any = {}; - public showCsvUploadPopup = false; public objectType: string; - public isObjectTypeCollection: any; - public isCreateCsv = true; public isStatusReviewMode = false; - public isEnableCsvAction: any; - public isTreeInitialized: any; public ishierarchyConfigSet = false; public publishchecklist: any; - public isComponenetInitialized = false; public unSubscribeshowQuestionLibraryPageEmitter: Subscription; public sourcingSettings: any; public setChildQuestion: any; public unsubscribe$ = new Subject(); public onComponentDestroy$ = new Subject(); - public outcomeDeclaration: any; - public levelsArray: any; constructor(private editorService: EditorService, public treeService: TreeService, private frameworkService: FrameworkService, private helperService: HelperService, public telemetryService: EditorTelemetryService, private router: Router, private toasterService: ToasterService, @@ -122,7 +111,6 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { this.objectType = this.configService.categoryConfig[this.editorConfig.config.objectType]; this.collectionId = _.get(this.editorConfig, 'context.identifier'); this.toolbarConfig = this.editorService.getToolbarConfig(); - this.isObjectTypeCollection = this.objectType === 'questionset' ? false : true; this.isStatusReviewMode = this.isReviewMode(); if (this.objectType === 'question') { @@ -204,63 +192,19 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { } getFrameworkDetails(categoryDefinitionData) { + this.setPublishCheckList(categoryDefinitionData); + if (_.isEmpty(this.targetFramework || _.get(this.editorConfig, 'context.targetFWIds'))) { + this.setTargetFrameworkData(categoryDefinitionData); + } + this.setOrgFrameworkData(categoryDefinitionData) + + } + + setOrgFrameworkData(categoryDefinitionData) { let orgFWIdentifiers: any; - let targetFWIdentifiers: any; let orgFWType: any; - let targetFWType: any; orgFWIdentifiers = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.objectMetadata.schema.properties.framework.enum') || - _.get(categoryDefinitionData, 'result.objectCategoryDefinition.objectMetadata.schema.properties.framework.default'); - if (_.get(this.editorConfig, 'config.renderTaxonomy') === true) { - const orgId = _.get(this.editorConfig, 'context.identifier'); - this.editorService.fetchOutComeDeclaration(orgId).toPromise() - .then(data => { - if (data?.result) { - this.outcomeDeclaration = _.get(data?.result, 'questionset.outcomeDeclaration'); - this.levelsArray = Object.keys(this.outcomeDeclaration); - } - }); - } - // tslint:disable-next-line:max-line-length - this.publishchecklist = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.forms.publishchecklist.properties') || _.get(categoryDefinitionData, 'result.objectCategoryDefinition.forms.review.properties') || []; - if (_.isEmpty(this.targetFramework || _.get(this.editorConfig, 'context.targetFWIds'))) { - // tslint:disable-next-line:max-line-length - targetFWIdentifiers = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.objectMetadata.schema.properties.targetFWIds.default'); - if (_.isEmpty(targetFWIdentifiers)) { - // tslint:disable-next-line:max-line-length - targetFWType = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.objectMetadata.config.frameworkMetadata.targetFWType'); - const channelFrameworks = _.get(this.helperService.channelInfo, 'frameworks'); - const channelFrameworksType = _.map(channelFrameworks, 'type'); - const difference = _.difference(targetFWType, _.uniq(channelFrameworksType)); - - if (targetFWType && channelFrameworksType && _.isEmpty(difference)) { - this.targetFramework = _.get(_.first(_.filter(channelFrameworks, framework => { - return framework.type === _.first(targetFWType); - })), 'identifier'); - this.treeService.updateMetaDataProperty('targetFWIds', _.castArray(this.targetFramework)); - this.frameworkService.getTargetFrameworkCategories(_.castArray(this.targetFramework)); - } else if ((targetFWType && channelFrameworksType && !_.isEmpty(difference)) || _.isEmpty(channelFrameworksType)) { - this.frameworkService.getFrameworkData(undefined, difference, undefined, 'Yes').subscribe( - (targetResponse) => { - this.targetFramework = _.get(_.first(_.get(targetResponse, 'result.Framework')), 'identifier'); - if (!_.isEmpty(this.targetFramework)) { - this.treeService.updateMetaDataProperty('targetFWIds', _.castArray(this.targetFramework)); - this.frameworkService.getTargetFrameworkCategories(_.castArray(this.targetFramework)); - } - } - ); - } - } else { - this.frameworkService.getFrameworkData(undefined, undefined, targetFWIdentifiers).subscribe( - (targetResponse) => { - this.targetFramework = _.get(_.first(_.get(targetResponse, 'result.Framework')), 'identifier'); - if (!_.isEmpty(this.targetFramework)) { - this.treeService.updateMetaDataProperty('targetFWIds', _.castArray(this.targetFramework)); - this.frameworkService.getTargetFrameworkCategories(_.castArray(this.targetFramework)); - } - } - ); - } - } + _.get(categoryDefinitionData, 'result.objectCategoryDefinition.objectMetadata.schema.properties.framework.default'); if (_.isEmpty(orgFWIdentifiers)) { let orgFrameworkList = []; @@ -304,6 +248,50 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { } } + setTargetFrameworkData(categoryDefinitionData) { + let targetFWIdentifiers; + let targetFWType; + targetFWIdentifiers = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.objectMetadata.schema.properties.targetFWIds.default'); + if (_.isEmpty(targetFWIdentifiers)) { + targetFWType = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.objectMetadata.config.frameworkMetadata.targetFWType'); + const channelFrameworks = _.get(this.helperService.channelInfo, 'frameworks'); + const channelFrameworksType = _.map(channelFrameworks, 'type'); + const difference = _.difference(targetFWType, _.uniq(channelFrameworksType)); + + if (targetFWType && channelFrameworksType && _.isEmpty(difference)) { + this.targetFramework = _.get(_.first(_.filter(channelFrameworks, framework => { + return framework.type === _.first(targetFWType); + })), 'identifier'); + this.treeService.updateMetaDataProperty('targetFWIds', _.castArray(this.targetFramework)); + this.frameworkService.getTargetFrameworkCategories(_.castArray(this.targetFramework)); + } else if ((targetFWType && channelFrameworksType && !_.isEmpty(difference)) || _.isEmpty(channelFrameworksType)) { + this.frameworkService.getFrameworkData(undefined, difference, undefined, 'Yes').subscribe( + (targetResponse) => { + this.targetFramework = _.get(_.first(_.get(targetResponse, 'result.Framework')), 'identifier'); + if (!_.isEmpty(this.targetFramework)) { + this.treeService.updateMetaDataProperty('targetFWIds', _.castArray(this.targetFramework)); + this.frameworkService.getTargetFrameworkCategories(_.castArray(this.targetFramework)); + } + } + ); + } + } else { + this.frameworkService.getFrameworkData(undefined, undefined, targetFWIdentifiers).subscribe( + (targetResponse) => { + this.targetFramework = _.get(_.first(_.get(targetResponse, 'result.Framework')), 'identifier'); + if (!_.isEmpty(this.targetFramework)) { + this.treeService.updateMetaDataProperty('targetFWIds', _.castArray(this.targetFramework)); + this.frameworkService.getTargetFrameworkCategories(_.castArray(this.targetFramework)); + } + } + ); + } + } + + setPublishCheckList(categoryDefinitionData) { + this.publishchecklist = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.forms.publishchecklist.properties') || _.get(categoryDefinitionData, 'result.objectCategoryDefinition.forms.review.properties') || []; + } + setEditorForms(categoryDefinitionData) { const formsConfigObj = _.get(categoryDefinitionData, 'result.objectCategoryDefinition.forms'); this.unitFormConfig = _.get(formsConfigObj, 'unitMetadata.properties'); @@ -338,13 +326,11 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { type: 'edit', pageid: this.telemetryService.telemetryPageId, uri: this.router.url, duration: (Date.now() - this.pageStartTime) / 1000 }); - this.isComponenetInitialized = true; } mergeCollectionExternalProperties(): Observable { const requests = []; this.collectionTreeNodes = null; - this.isTreeInitialized = true; requests.push(this.editorService.fetchCollectionHierarchy(this.collectionId)); if (this.objectType === 'questionset') { requests.push(this.editorService.readQuestionSet(this.collectionId)); @@ -438,7 +424,6 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { this.saveContent().then((message: string) => { this.buttonLoaders.saveAsDraftButtonLoader = false; this.toasterService.success(message); - this.isEnableCsvAction = true; if (_.get(this.editorConfig, 'config.enableQuestionCreation') === false) { this.mergeCollectionExternalProperties().subscribe(response => { this.redirectToChapterListTab({ @@ -449,7 +434,6 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { } }).catch(((error: string) => { this.buttonLoaders.saveAsDraftButtonLoader = false; - this.isEnableCsvAction = false; this.toasterService.error(error); })); break; @@ -477,9 +461,6 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { break; case 'onFormStatusChange': this.onFormStatusChange(event.event); - if (this.isObjectTypeCollection) { - this.handleCsvDropdownOptionsOnCollection(); - } break; case 'onFormValueChange': this.updateToolbarTitle(event); @@ -576,8 +557,6 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { this.mergeCollectionExternalProperties().subscribe((res: any) => { this.pageId = 'questionset_editor'; this.telemetryService.telemetryPageId = this.pageId; - this.isEnableCsvAction = true; - this.isComponenetInitialized = true; }); } @@ -604,7 +583,7 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { } }, err => { const errInfo = { - errorMsg: _.get(this.configService, 'labelConfig.messages.error.043') + errorMsg: _.get(this.configService, 'labelConfig.messages.error.041') }; return throwError(this.editorService.apiErrorHandling(err, errInfo)); }); @@ -798,16 +777,12 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { treeEventListener(event: any) { this.actionType = event.type; this.updateTreeNodeData(); - if (this.isObjectTypeCollection) { - this.handleCsvDropdownOptionsOnCollection(); - } switch (event.type) { case 'nodeSelect': this.updateSubmitBtnVisibility(); this.selectedNodeData = _.cloneDeep(event.data); this.isCurrentNodeFolder = _.get(this.selectedNodeData, 'folder'); this.isCurrentNodeRoot = _.get(this.selectedNodeData, 'data.root'); - // TODO: rethink below line code this.isQumlPlayer = _.get(this.selectedNodeData, 'data.metadata.mimeType') === 'application/vnd.sunbird.question'; this.setTemplateList(); this.changeDetectionRef.detectChanges(); @@ -1041,7 +1016,6 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { this.mergeCollectionExternalProperties().subscribe((res: any) => { this.pageId = 'questionset_editor'; this.telemetryService.telemetryPageId = this.pageId; - this.isEnableCsvAction = true; }); } } @@ -1065,77 +1039,10 @@ export class EditorComponent implements OnInit, OnDestroy, AfterViewInit { } return false; } - handleCsvDropdownOptionsOnCollection() { - if (this.isTreeInitialized) { - this.isEnableCsvAction = true; - this.isTreeInitialized = false; - } else { - this.isEnableCsvAction = false; - } - this.setCsvDropDownOptionsDisable(true); - } - onClickFolder() { - if (this.isComponenetInitialized) { - this.isComponenetInitialized = false; - this.setCsvDropDownOptionsDisable(); - } else if (this.isEnableCsvAction) { - this.setCsvDropDownOptionsDisable(); - } - } - setCsvDropDownOptionsDisable(disable?) { - const status = this.editorService.getHierarchyFolder().length ? true : false; - this.csvDropDownOptions.isDisableCreateCsv = disable ? disable : status; - this.csvDropDownOptions.isDisableUpdateCsv = disable ? disable : !status; - this.csvDropDownOptions.isDisableDownloadCsv = disable ? disable : !status; - } - downloadHierarchyCsv() { - this.editorService.downloadHierarchyCsv(this.collectionId).subscribe(res => { - const tocUrl = _.get(res, 'result.collection.tocUrl'); - this.downloadCSVFile(tocUrl); - }, error => { - this.toasterService.error(_.get(error, 'error.params.errmsg')); - }); - } + isReviewMode() { return _.includes([ 'review', 'read', 'sourcingreview', 'orgreview' ], this.editorService.editorMode); } - downloadCSVFile(tocUrl) { - const downloadConfig = { - blobUrl: tocUrl, - successMessage: false, - fileType: 'csv', - fileName: this.collectionId - }; - window.open(downloadConfig.blobUrl, '_blank'); - /*this.editorService.downloadBlobUrlFile(downloadConfig);*/ - } - hanndleCsvEmitter(event) { - switch (event.type) { - case 'closeModal': - this.showCsvUploadPopup = false; - break; - case 'updateHierarchy': - this.mergeCollectionExternalProperties().subscribe((res: any) => { - this.pageId = 'questionset_editor'; - this.telemetryService.telemetryPageId = this.pageId; - this.isEnableCsvAction = true; - }); - break; - case 'createCsv': - this.showCsvUploadPopup = true; - this.isCreateCsv = true; - break; - case 'updateCsv': - this.showCsvUploadPopup = true; - this.isCreateCsv = false; - break; - case 'downloadCsv': - this.downloadHierarchyCsv(); - break; - default: - break; - } - } onFormStatusChange(form) { const selectedNode = this.treeService.getActiveNode(); diff --git a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.ts b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.ts index 4f578ac70..90cc1a630 100755 --- a/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.ts +++ b/projects/questionset-editor-library/src/lib/components/fancy-tree/fancy-tree.component.ts @@ -354,6 +354,15 @@ export class FancyTreeComponent implements OnInit, AfterViewInit, OnDestroy { this.visibility.addChild = ((node.folder === false) || (nodeLevel >= this.config.maxDepth)) ? false : true; // tslint:disable-next-line:max-line-length this.visibility.addSibling = ((node.folder === true) && (!node.data.root) && !((node.getLevel() - 1) > this.config.maxDepth)) ? true : false; + this.handleCreateAddVisibility(node, nodeLevel); + if (_.get(this.editorService, 'editorConfig.config.renderTaxonomy') === true) { + this.visibility.addChild = false; + this.visibility.addSibling = false; + } + this.cdr.detectChanges(); + } + + handleCreateAddVisibility(node, nodeLevel) { if (nodeLevel === 0) { this.visibility.createNew = _.isEmpty(_.get(this.config, 'children')) || _.get(this.config, 'enableQuestionCreation') === false ? false : true; this.visibility.addQuestionFromLibrary = !_.isEmpty(_.get(this.config, 'children')) && _.get(this.config, 'enableAddFromLibrary') === true ? true : false; @@ -363,19 +372,11 @@ export class FancyTreeComponent implements OnInit, AfterViewInit, OnDestroy { this.visibility.createNew = ((node.folder === false) || _.isEmpty(_.get(hierarchylevelData, 'children')) || _.get(this.config, 'enableQuestionCreation') === false) ? false : true; this.visibility.addQuestionFromLibrary = ((node.folder === true) && !_.isEmpty(_.get(hierarchylevelData, 'children')) && _.get(this.config, 'enableAddFromLibrary') === true) ? true : false; } - - if (_.get(this.editorService, 'editorConfig.config.renderTaxonomy') === true) { - this.visibility.addChild = false; - this.visibility.addSibling = false; - } - this.cdr.detectChanges(); } addChild() { this.telemetryService.interact({ edata: this.getTelemetryInteractEdata('add_child') }); const tree = $(this.tree.nativeElement).fancytree('getTree'); - const nodeConfig = this.config.hierarchy[tree.getActiveNode().getLevel()]; - const childrenTypes = _.get(nodeConfig, 'children.Content'); if ((((tree.getActiveNode().getLevel() - 1) >= this.config.maxDepth))) { return this.toasterService.error(_.get(this.configService, 'labelConfig.messages.error.007')); } @@ -502,7 +503,7 @@ export class FancyTreeComponent implements OnInit, AfterViewInit, OnDestroy { let current = buffer.pop(); let max = 0; - while (current && current.node) { + while (current?.node) { // Find all children of this node. _.forEach(current.node.children, (child) => { buffer.push({ node: child, depth: current.depth + 1 }); diff --git a/projects/questionset-editor-library/src/lib/components/header/header.component.html b/projects/questionset-editor-library/src/lib/components/header/header.component.html index 36dc5aafb..9d77741ab 100755 --- a/projects/questionset-editor-library/src/lib/components/header/header.component.html +++ b/projects/questionset-editor-library/src/lib/components/header/header.component.html @@ -20,9 +20,6 @@
{{ sourcingStatusText }} - {{configService.labelConfig?.lbl?.viewComments}} @@ -197,8 +194,6 @@ -
diff --git a/projects/questionset-editor-library/src/lib/components/header/header.component.ts b/projects/questionset-editor-library/src/lib/components/header/header.component.ts index c1bb769da..28a605286 100755 --- a/projects/questionset-editor-library/src/lib/components/header/header.component.ts +++ b/projects/questionset-editor-library/src/lib/components/header/header.component.ts @@ -15,7 +15,6 @@ export class HeaderComponent implements OnDestroy, OnInit { @Input() pageId: any; @Input() labelConfigData: any; @Input() buttonLoaders: any; - @Input() showComment: any; @Input() publishchecklist: any; @Input() set requestChange(action: string) { if (action) { @@ -27,7 +26,6 @@ export class HeaderComponent implements OnDestroy, OnInit { @ViewChild('modal') public modal; @Output() qualityParamEmitter = new EventEmitter(); public visibility: any; - public showReviewModal: boolean; public showPublishCollectionPopup: boolean; public showRequestChangesPopup: boolean; public rejectComment: string; diff --git a/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.data.ts index 76cbc27b3..4e11002c3 100644 --- a/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.data.ts @@ -423,9 +423,7 @@ export const mockData = { }, shuffle: true, board: 'CBSE', - instructions: { - default: '

Chapter 1: माता का अँचल

Chapter 2: जॉर्ज पंचम की नाक

Chapter 3: साना – साना हाथ जोड़ि

Chapter 4: एही ठैयाँ झुलनी हेरानी हो रामा!

Chapter 5: मैं क्यों लिखता हूँ?

Chapter 6: लेखक परिचय

Chapter 7: लेखक परिचय

' - }, + instructions:'

Chapter 1: माता का अँचल

Chapter 2: जॉर्ज पंचम की नाक

Chapter 3: साना – साना हाथ जोड़ि

Chapter 4: एही ठैयाँ झुलनी हेरानी हो रामा!

Chapter 5: मैं क्यों लिखता हूँ?

Chapter 6: लेखक परिचय

Chapter 7: लेखक परिचय

', level: 1 }, root: true diff --git a/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.ts b/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.ts index c4952fe49..a251c31e4 100644 --- a/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.spec.ts @@ -128,7 +128,6 @@ describe('MetaFormComponent', () => { component.rootFormConfig = mockData.rootFormConfig; spyOn(component, 'isReviewMode').and.returnValue(false); spyOn(component, 'setAppIconData').and.callThrough(); - spyOn(component, 'ifFieldIsEditable').and.callFake(() => {return false}); component.appIconConfig = { isAppIconEditable: true }; @@ -137,7 +136,6 @@ describe('MetaFormComponent', () => { expect(component.appIcon).toBeDefined(); expect(component.isReviewMode).toHaveBeenCalled(); expect(component.appIconConfig.isAppIconEditable).toBeTruthy(); - expect(component.ifFieldIsEditable).toHaveBeenCalled(); }); it('#setAppIconData() should set appIcon as non editable', () => { @@ -147,17 +145,14 @@ describe('MetaFormComponent', () => { component.rootFormConfig = mockData.rootFormConfigWithoutGrouping; spyOn(component, 'isReviewMode').and.returnValue(true); spyOn(component, 'setAppIconData').and.callThrough(); - spyOn(component, 'ifFieldIsEditable').and.callFake(() => {return false}); component.appIconConfig = { isAppIconEditable: true }; component.setAppIconData(); expect(component.showAppIcon).toBeTruthy(); - // expect(component.appIcon).toBeDefined(); expect(component.appIcon).toBeUndefined(); expect(component.isReviewMode).toHaveBeenCalled(); expect(component.appIconConfig.isAppIconEditable).toBeFalsy(); - expect(component.ifFieldIsEditable).toHaveBeenCalled(); }); it('#fetchFrameWorkDetails() should set fetchFrameWorkDetails and for targetFrameworkIds', () => { diff --git a/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.ts b/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.ts index e70228ca8..67469e7a0 100644 --- a/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.ts +++ b/projects/questionset-editor-library/src/lib/components/meta-form/meta-form.component.ts @@ -62,7 +62,6 @@ export class MetaFormComponent implements OnChanges, OnDestroy { } else { this.appIconConfig = {...this.appIconConfig , ... {isAppIconEditable: true}}; } - const ifEditable = this.ifFieldIsEditable('appIcon'); } setShuffleValue(value) { diff --git a/projects/questionset-editor-library/src/lib/components/options/options.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/options/options.component.spec.data.ts index 0a4435b68..62a6d7002 100644 --- a/projects/questionset-editor-library/src/lib/components/options/options.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/options/options.component.spec.data.ts @@ -19,6 +19,22 @@ export const mockOptionData = { templateId: 'mcq-split-grid', answer: 0, numberOfOptions: 4, + interactions:{ + response1:{ + options:[ + { + "label": "

Option1

", + "value": 'first', + "hint": "82baa452-62dc-4906-b63c-48e459f4589d" + }, + { + "label": "

Option 222

", + "value": 'second', + "hint": "dce993df-3b74-48ad-b3b1-0cd7d739af2b" + } + ] + } + } }, prepareMcqBody: { templateId: 'mcq-vertical', @@ -101,13 +117,17 @@ export const mockOptionData = { [{ id: 'addHint', name: 'Add Hint', - value: 'test', + value: 'first', enabled: false, type: 'input', label: 'label', show:true }] - ] + ], + hints:{ + '82baa452-62dc-4906-b63c-48e459f4589d':{en:'first'}, + 'dce993df-3b74-48ad-b3b1-0cd7d739af2b':{en:'second'} + } }; export const sourcingSettingsMock = { diff --git a/projects/questionset-editor-library/src/lib/components/options/options.component.spec.ts b/projects/questionset-editor-library/src/lib/components/options/options.component.spec.ts index 83113a3bc..d8ab51f48 100644 --- a/projects/questionset-editor-library/src/lib/components/options/options.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/options/options.component.spec.ts @@ -250,9 +250,10 @@ describe('OptionsComponent', () => { it('#subMenuChange() should set the sub-menu value ', () => { component.subMenus = mockOptionData.subMenus; + component.hints = mockOptionData.hints; spyOn(component, 'subMenuChange').and.callThrough(); - component.subMenuChange({index:1,value:'test'},1) - expect(component.subMenus[0][0].value).toBe('test'); + component.subMenuChange({index:1,value:'first'},1) + expect(component.subMenus[0][0].value).toBe('first'); }) it('#subMenuConfig() should set on initialize', () => { diff --git a/projects/questionset-editor-library/src/lib/components/options/options.component.ts b/projects/questionset-editor-library/src/lib/components/options/options.component.ts index 23039e0ce..a57129d9a 100644 --- a/projects/questionset-editor-library/src/lib/components/options/options.component.ts +++ b/projects/questionset-editor-library/src/lib/components/options/options.component.ts @@ -5,6 +5,7 @@ import { ConfigService } from '../../services/config/config.service'; import { SubMenu } from '../question-option-sub-menu/question-option-sub-menu.component'; import { TreeService } from '../../services/tree/tree.service'; import { EditorService } from '../../services/editor/editor.service'; +import { v4 as uuidv4 } from 'uuid'; @Component({ selector: 'lib-options', templateUrl: './options.component.html', @@ -23,7 +24,7 @@ export class OptionsComponent implements OnInit, OnChanges { public setImageLimit = 1; public templateType = 'mcq-vertical'; subMenus: SubMenu[][]; - hints = []; + hints:any = {}; showSubMenu:boolean=false; parentMeta: any; selectedOptions = []; @@ -35,6 +36,7 @@ export class OptionsComponent implements OnInit, OnChanges { ) {} ngOnInit() { + this.hints = this.editorState.hints ? this.editorState.hints : {}; if(!_.isUndefined(this.editorState.answer)) { this.addSelectedOptions(); } @@ -110,12 +112,14 @@ export class OptionsComponent implements OnInit, OnChanges { outcomeDeclaration: this.getOutcomeDeclaration(), interactionTypes: ['choice'], interactions: this.getInteractions(editorState.options), + hints:this.hints, editorState: { options, }, qType: 'MCQ', primaryCategory: this.questionPrimaryCategory || 'Multiple Choice Question', }; + this.subMenuConfig(editorState.options); return metadata; } @@ -172,10 +176,8 @@ export class OptionsComponent implements OnInit, OnChanges { let index; const interactOptions = _.map(options, (opt, key) => { index = Number(key); - const hints = _.get(this.editorState, `interactions.response1.options[${index}].hints`) - return { label: opt.body, value: index, hints }; + return { label: opt.body, value: index, hint: this.hints[this.editorState?.interactions?.response1?.options[index]?.hint] ? Object.keys(this.hints).find(element => element == this.editorState?.interactions?.response1?.options[index]?.hint) : '' }; }); - this.subMenuConfig(options); const interactions = { response1: { type: 'choice', @@ -191,20 +193,34 @@ export class OptionsComponent implements OnInit, OnChanges { } subMenuChange({ index, value }, optionIndex) { - _.set(this.editorState, `interactions.response1.options[${optionIndex}].hints.en`, value) + if(value.length && Object.keys(this.hints).length < this.editorState.interactions.response1.options.length ) { + const hint = {[uuidv4()] : {en:value}} + this.hints = {...this.hints, ...hint} + this.editorState.interactions.response1.options[optionIndex].hint = Object.keys(hint)[0] + } + else if (value.length) { + this.hints[this.editorState.interactions.response1.options[optionIndex].hint].en = value; + } } subMenuConfig(options) { this.subMenus = [] options.map((opt, index) => { - const value = _.get(this.editorState, `interactions.response1.options[${index}].hints.en`) + const uuid = _.get(this.editorState, `interactions.response1.options[${index}].hint`) this.subMenus[index] = [ { id: 'addHint', name: 'Add Hint', - value, + value: (():any => { + if(this.hints[uuid]) { + return this.hints[uuid].en + } + else { + return this.editorState?.hints?.[uuid] ? this.editorState.hints[uuid].en : '' + } + })(), label: 'Hint', - enabled: value ? true : false, + enabled: uuid ? true : false, type: 'input', show: _.get(this.sourcingSettings, 'showAddHints'), }, diff --git a/projects/questionset-editor-library/src/lib/components/publish-checklist/publish-checklist.component.ts b/projects/questionset-editor-library/src/lib/components/publish-checklist/publish-checklist.component.ts index 1e5a71103..4f22115ac 100644 --- a/projects/questionset-editor-library/src/lib/components/publish-checklist/publish-checklist.component.ts +++ b/projects/questionset-editor-library/src/lib/components/publish-checklist/publish-checklist.component.ts @@ -38,7 +38,7 @@ export class PublishChecklistComponent implements OnInit { publishData[field.code] = this.fieldsAvailable[field.code]; // asign value to field other than checkbox's example publishComment = 'some comment' } }); - if (checkBoxData && checkBoxData.length) { + if (checkBoxData?.length) { publishData['publishChecklist'] = checkBoxData; } this.publishEmitter.emit({ button: this.actionType, publishData: publishData}); diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts index 87f48783c..900e3a075 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.data.ts @@ -137,6 +137,7 @@ export const mockData = { ], question: "

MCQ Question

", }, + instructions:'', templateId: "mcq-vertical", solutions: [ { @@ -151,23 +152,17 @@ export const mockData = { { label: "

option 1

", value: 0, - hints: { - en: "test hint 1", - }, + hint:'70c82bf5-9459-4c43-8897-0e58b7e1da62' }, { label: "

option 2

", value: 1, - hints: { - en: "test hint 2", - }, + hint: '70c82bf5-9459-4c43-8897-0e58b7e1as73' }, { label: "

option 3

", value: 2, - hints: { - en: "test hint 3", - }, + hint:'70c82bf5-9459-4c43-8897-0e58b7e1gf87' }, ], autoCapture: "Yes", @@ -180,6 +175,12 @@ export const mockData = { }, }, }, + hints: { + "70c82bf5-9459-4c43-8897-0e58b7e1da64": { en: "test hint 1" }, + "70c82bf5-9459-4c43-8897-0e58b7e1as73": { en: "test hint 2" }, + "70c82bf5-9459-4c43-8897-0e58b7e1gf87": { en: "test hint 3" }, + "70f9a0b2-94c3-4d81-86c0-2082fb10a47b": { en: "test hint 4" } + }, evidence: { mimeType: ["audio", "vedio"], }, @@ -208,7 +209,12 @@ export const mockData = { cardinality: "multiple", type: "integer", defaultValue: 1 - } + }, + hint :{ + cardinality: "single", + type: "string", + defaultValue: "70f9a0b2-94c3-4d81-86c0-2082fb10a47b" + } }, remarks: { maxLength: 100, @@ -300,11 +306,16 @@ export const mockData = { }, }, hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } }, - instructions: { - en: [null], + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, + instructions: null, interactionTypes: ["slider"], primaryCategory: "Slider", }, @@ -360,11 +371,16 @@ export const mockData = { }, }, hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } }, - instructions: { - en: [null], + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, + instructions: null, interactionTypes: ["date"], primaryCategory: "Date", }, @@ -427,11 +443,16 @@ export const mockData = { }, }, hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } }, - instructions: { - en: [null], + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, + instructions: null, interactionTypes: ["text"], primaryCategory: "Text", }, @@ -451,9 +472,7 @@ export const mockData = { responseCode: "OK", result: { question: { - instructions: { - en: [null], - }, + instructions:null, responseDeclaration: { response1: { type: "string", @@ -477,7 +496,14 @@ export const mockData = { identifier: "do_11345671149997260811", solutions: [], hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } + }, + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, languageCode: ["en"], interactionTypes: "", @@ -718,9 +744,7 @@ export const readQuestionMock = { responseCode: "OK", result: { question: { - instructions: { - en: [null], - }, + instructions: null, showRemarks: "No", responseDeclaration: { response1: { @@ -777,7 +801,14 @@ export const readQuestionMock = { identifier: "do_1134355569264885761166", solutions: [], hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } + }, + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, qType: "MCQ", languageCode: ["en"], @@ -802,9 +833,7 @@ export const readQuestionMockSlider = { responseCode: "OK", result: { question: { - instructions: { - en: [null], - }, + instructions: null, showRemarks: "No", mimeType: "application/vnd.sunbird.question", media: [], @@ -830,7 +859,14 @@ export const readQuestionMockSlider = { identifier: "do_1134355571590184961168", solutions: [], hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } + }, + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, languageCode: ["en"], interactionTypes: ["slider"], @@ -854,9 +890,7 @@ export const readQuestionMockDate = { responseCode: "OK", result: { question: { - instructions: { - en: [null], - }, + instructions: null, mimeType: "application/vnd.sunbird.question", media: [], body: "

Dep Date 3

", @@ -878,7 +912,14 @@ export const readQuestionMockDate = { identifier: "do_1134355574936780801170", solutions: [], hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } + }, + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, languageCode: ["en"], interactionTypes: ["date"], @@ -901,9 +942,7 @@ export const readQuestionMockText = { responseCode: "OK", result: { question: { - instructions: { - en: [null], - }, + instructions:null, showRemarks: "No", mimeType: "application/vnd.sunbird.question", media: [], @@ -930,7 +969,14 @@ export const readQuestionMockText = { identifier: "do_1134347722012835841130", solutions: [], hints: { - en: [null], + "70c82bf5-9459-4c43-8897-0e58b7e2er76": { "en": "delhi" } + }, + outcomeDeclaration: { + "hint": { + "cardinality": "single", + "type": "string", + "defaultValue": "70c82bf5-9459-4c43-8897-0e58b7e2er76" + } }, languageCode: ["en"], interactionTypes: ["text"], diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts index 5da77e2e7..007fdd0a3 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.spec.ts @@ -168,7 +168,7 @@ describe("QuestionComponent", () => { it("#saveQuestions call on click save button", () => { spyOn(component, "saveQuestions"); const metaData = mockData.textQuestionNetaData.result.question; - spyOn(questionService, "updateHierarchyQuestionCreate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -547,13 +547,6 @@ describe("QuestionComponent", () => { expect(component.initialize).toHaveBeenCalled(); }); - it('#contentPolicyUrl() should return content policy url', () => { - editorService.contentPolicyUrl = 'https://preprod.ntp.net.in/term-of-use.html'; - spyOn(component, 'contentPolicyUrl').and.callThrough(); - const contentPolicyURL = component.contentPolicyUrl; - expect(contentPolicyURL).toBeDefined(); - }); - it("#toolbarEventListener() should call toolbarEventListener for saveContent", () => { const event = { button: "saveContent" }; component.actionType = event.button; @@ -646,49 +639,41 @@ describe("QuestionComponent", () => { expect(component.toolbarEventListener).toHaveBeenCalledWith(data); }); - it("Unit test for #populateFormData question markAsNotMandatory reqired yes", () => { - spyOn(component,'populateFormData').and.callThrough(); - component.childFormData = {}; - component.isReadOnlyMode=false; - component.questionInteractionType="choice"; - component.questionMetaData=mockData.mcqQuestionMetaData.result.question; - component.leafFormConfig = leafFormConfigMock; - component.questionId = "do_123"; - component.questionSetHierarchy = collectionHierarchyMock.result.questionset; - spyOn(component,'fetchFrameWorkDetails').and.callFake(()=>{}); - spyOn(component,'previewFormData').and.callFake(()=>{}) + it('Unit test for #populateFormData() for existing question', () => { + spyOn(component, 'populateFormData').and.callThrough(); + component.questionId = 'do_12345'; + spyOn(component, 'setExistingQuestionData').and.callFake(() => {}); + spyOn(component, 'fetchFrameWorkDetails').and.callFake(() => {}); + component.isReadOnlyMode = false; + spyOn(component, 'previewFormData').and.callFake(() => {}); component.populateFormData(); + expect(component.setExistingQuestionData).toHaveBeenCalled(); expect(component.previewFormData).toHaveBeenCalled(); }); - it("Unit test for #populateFormData without Question Id", () => { - component.childFormData = {}; + it('Unit test for #populateFormData() for new question', () => { + spyOn(component, 'populateFormData').and.callThrough(); component.questionId = undefined; - component.questionInteractionType="choice"; - component.isReadOnlyMode=false; component.leafFormConfig = leafFormConfigMock; - component.initialLeafFormConfig = leafFormConfigMock; - component.questionFormConfig = leafFormConfigMock; - component.questionMetaData=mockData.mcqQuestionMetaData.result.question; component.questionSetHierarchy = collectionHierarchyMock.result.questionset; - spyOn(component,'fetchFrameWorkDetails').and.callFake(()=>{}); + component.initialLeafFormConfig = leafFormConfigMock; + component.maxScore = 1; + spyOn(component, 'setExistingQuestionData').and.callFake(() => {}); + spyOn(component, 'fetchFrameWorkDetails').and.callFake(() => {}); + component.isReadOnlyMode = true; + spyOn(component, 'previewFormData').and.callFake(() => {}); component.populateFormData(); - }); + expect(component.setExistingQuestionData).not.toHaveBeenCalled(); + expect(component.previewFormData).toHaveBeenCalled(); + }); - it("Unit test for #populateFormData readonly mode true ", () => { - spyOn(component,'populateFormData').and.callThrough(); - component.childFormData = {}; - component.isReadOnlyMode=true; - component.questionInteractionType="choice"; - component.questionMetaData=mockData.mcqQuestionMetaData.result.question; + it('Unit test for #setExistingQuestionData()', () => { + spyOn(component, 'setExistingQuestionData').and.callThrough(); component.leafFormConfig = leafFormConfigMock; - component.initialLeafFormConfig = leafFormConfigMock; - component.questionFormConfig = leafFormConfigMock; - component.questionId = "do_123"; - component.questionSetHierarchy = collectionHierarchyMock.result.questionset; - spyOn(component,'previewFormData').and.callFake(()=>{}) - component.populateFormData(); - expect(component.previewFormData).toHaveBeenCalled(); + component.questionMetaData = mockData.mcqQuestionMetaData.result.question; + component.childFormData = {}; + spyOn(component, 'setChildAliasData').and.callThrough(); + component.setExistingQuestionData(); }); it("should call previewFormData ", () => { @@ -701,10 +686,12 @@ describe("QuestionComponent", () => { expect(component.leafFormConfig).toEqual(mockData.childMetadata.properties); expect(component.previewFormData).toHaveBeenCalled(); }); + it("should call valueChanges", () => { component.valueChanges(childMetaData); expect(component.childFormData).toEqual(childMetaData); }); + it("should call validateFormFields", () => { component.leafFormConfig = mockData.childMetadata; component.childFormData = childMetaData; @@ -999,15 +986,6 @@ describe("QuestionComponent", () => { expect(outcomeDeclaration.maxScore.cardinality).toEqual('single'); }); - it("Unit test for #isEditable without queston id", () => { - component.creationContext = creationContextMock; - component.questionId=undefined; - expect(component.isEditable("bloomsLevel")).toBeTruthy(); - }); - it("Unit test for #isEditable with queston id", () => { - component.creationContext = creationContextMock; - expect(component.isEditable("bloomsLevel")).toBeFalsy(); - }); it("Unit test for #prepareQuestionBody", () => { component.questionId = 'do_12345'; const editorService = TestBed.inject(EditorService); @@ -1124,7 +1102,7 @@ describe("QuestionComponent", () => { }, }) ); - spyOn(questionService, "updateHierarchyQuestionUpdate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1245,7 +1223,7 @@ describe("QuestionComponent", () => { component.setQuestionTypeValues( mockData.mcqQuestionMetaData.result.question ); - spyOn(questionService, "updateHierarchyQuestionCreate").and.returnValue( + spyOn(questionService, "updateQuestionHierarchy").and.returnValue( throwError("error") ); component.saveQuestion(); @@ -1262,7 +1240,7 @@ describe("QuestionComponent", () => { component.setQuestionTypeValues( mockData.mcqQuestionMetaData.result.question ); - spyOn(questionService, "updateHierarchyQuestionCreate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1288,7 +1266,7 @@ describe("QuestionComponent", () => { component.isChildQuestion = true; component.condition = "eq"; component.selectedOptions = 1; - spyOn(questionService, "updateHierarchyQuestionCreate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1317,7 +1295,7 @@ describe("QuestionComponent", () => { component.subMenus = mockData.subMenus; component.questionInteractionType = "choice"; component.showOptions = false; - spyOn(questionService, "updateHierarchyQuestionCreate").and.returnValue( + spyOn(questionService, "updateQuestionHierarchy").and.returnValue( throwError("error") ); component.createQuestion(); @@ -1330,7 +1308,7 @@ describe("QuestionComponent", () => { component.subMenus = mockData.subMenus; component.questionInteractionType = "choice"; component.showOptions = false; - spyOn(questionService, "updateHierarchyQuestionCreate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1705,7 +1683,7 @@ describe("QuestionComponent", () => { it("#saveQuestions call on click save button", () => { spyOn(component, "saveQuestions"); - spyOn(questionService, "updateHierarchyQuestionCreate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1722,7 +1700,7 @@ describe("QuestionComponent", () => { }); it("#saveQuestions call on click save button for update api success", () => { - spyOn(questionService, "updateHierarchyQuestionCreate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1738,7 +1716,7 @@ describe("QuestionComponent", () => { }); it("#saveQuestions call on click save button for update api success", () => { - spyOn(questionService, "updateHierarchyQuestionCreate").and.returnValue( + spyOn(questionService, "updateQuestionHierarchy").and.returnValue( throwError("error") ); component.saveQuestions( @@ -1802,8 +1780,11 @@ describe("QuestionComponent", () => { component.questionMetaData = mockData.mcqQuestionMetaData.result.question; sourcingSettingsMock.showAddSecondaryQuestion = true; component.questionMetaData.hints = { - en: [], - }; + "70c82bf5-9459-4c43-8897-0e58b7e1da64": { en: "test hint 1" }, + "70c82bf5-9459-4c43-8897-0e58b7e1as73": { en: "test hint 2" }, + "70c82bf5-9459-4c43-8897-0e58b7e1gf87": { en: "test hint 3" }, + "70f9a0b2-94c3-4d81-86c0-2082fb10a47b": { en: "test hint 4" } + } sourcingSettingsMock.showAddHints = false; component.sourcingSettings = sourcingSettingsMock; component.questionInput.setChildQuestion = true; @@ -1853,7 +1834,7 @@ describe("QuestionComponent", () => { component.questionInteractionType = "choice"; component.getQuestionMetadata(); component.setQuestionTypeValues(metaData); - spyOn(questionService, "updateHierarchyQuestionUpdate").and.returnValue( + spyOn(questionService, "updateQuestionHierarchy").and.returnValue( throwError("error") ); component.saveUpdateQuestions(); @@ -1870,7 +1851,7 @@ describe("QuestionComponent", () => { component.questionInteractionType = "choice"; component.getQuestionMetadata(); component.setQuestionTypeValues(metaData); - spyOn(questionService, "updateHierarchyQuestionUpdate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1931,7 +1912,7 @@ describe("QuestionComponent", () => { it("#saveQuestions call on click save button", () => { spyOn(component, "saveQuestions"); const metaData = mockData.textQuestionNetaData.result.question; - spyOn(questionService, "updateHierarchyQuestionCreate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -1947,7 +1928,7 @@ describe("QuestionComponent", () => { it("#saveQuestions call on click save button api fail", () => { spyOn(component, "saveQuestions"); const metaData = mockData.textQuestionNetaData.result.question; - spyOn(questionService, "updateHierarchyQuestionCreate").and.returnValue( + spyOn(questionService, "updateQuestionHierarchy").and.returnValue( throwError("error") ); component.saveQuestions(metaData, "update"); @@ -2117,7 +2098,7 @@ describe("QuestionComponent", () => { BranchingLogic, component.selectedSectionId ); - spyOn(questionService, "updateHierarchyQuestionUpdate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -2137,7 +2118,7 @@ describe("QuestionComponent", () => { component.isChildQuestion=false; component.questionId='1245' component.showAddSecondaryQuestionCat=true; - spyOn(questionService, "updateHierarchyQuestionUpdate").and.callFake(() => { + spyOn(questionService, "updateQuestionHierarchy").and.callFake(() => { return of({ result: { identifiers: { @@ -2158,7 +2139,7 @@ describe("QuestionComponent", () => { component.isChildQuestion=false; component.questionId='1245' component.showAddSecondaryQuestionCat=true; - spyOn(questionService, "updateHierarchyQuestionUpdate").and.returnValue(throwError('error')) + spyOn(questionService, "updateQuestionHierarchy").and.returnValue(throwError('error')) component.saveUpdateQuestions(); component.updateQuestion(); }); diff --git a/projects/questionset-editor-library/src/lib/components/question/question.component.ts b/projects/questionset-editor-library/src/lib/components/question/question.component.ts index 89d54c3af..2d1847bf4 100644 --- a/projects/questionset-editor-library/src/lib/components/question/question.component.ts +++ b/projects/questionset-editor-library/src/lib/components/question/question.component.ts @@ -18,7 +18,6 @@ import { filter, finalize, take, takeUntil } from 'rxjs/operators'; import { SubMenu } from '../question-option-sub-menu/question-option-sub-menu.component'; import { ICreationContext } from '../../interfaces/CreationContext'; -const evidenceMimeType=''; const evidenceSizeLimit='20480'; const DEFAULT_SCORE = 1; @@ -29,7 +28,6 @@ const DEFAULT_SCORE = 1; encapsulation: ViewEncapsulation.None, }) export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { - QumlPlayerConfig: any = {}; @Input() questionInput: any; @Input() leafFormConfig: any; @Input() sourcingSettings: any; @@ -48,14 +46,12 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { public actionType: string; assetType: string; selectedSolutionType: string; - selectedSolutionTypeIndex: string; showSolutionDropDown = true; showSolution = false; assetSolutionName: string; assetSolutionData: any; assetThumbnail: string; solutionUUID: string; - solutionValue: string; solutionTypes: any = [{ type: 'html', value: 'Text+Image' @@ -85,7 +81,6 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { questionSetHierarchy: any; showConfirmPopup = false; showSubmitConfirmPopup = false; - validQuestionData = false; questionPrimaryCategory: string; pageId = 'question'; pageStartTime: any; @@ -121,6 +116,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { showQualityParameterPopup: boolean =false; public qualityFormConfig: any; requestChangesPopupAction: string; + hintsUUID:string = '' constructor( private questionService: QuestionService, public editorService: EditorService, public telemetryService: EditorTelemetryService, public playerService: PlayerService, private toasterService: ToasterService, private treeService: TreeService, @@ -245,6 +241,12 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { }, { templateId, numberOfOptions,maximumOptions }); this.editorState.solutions = this.questionMetaData?.editorState?.solutions; this.editorState.interactions = interactions; + if(this.questionMetaData?.hints) { + this.editorState.hints = this.questionMetaData.hints; + } + else { + this.editorState.hints = {}; + } if (_.has(this.questionMetaData, 'responseDeclaration')) { this.editorState.responseDeclaration = _.get(this.questionMetaData, 'responseDeclaration'); } @@ -252,6 +254,12 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { if (_.has(this.questionMetaData, 'primaryCategory')) { this.editorState.primaryCategory = _.get(this.questionMetaData, 'primaryCategory'); } + if(this.questionMetaData?.outcomeDeclaration?.hint) { + this.hintsUUID = this.questionMetaData?.outcomeDeclaration?.hint?.defaultValue + } + else { + this.hintsUUID = uuidv4() + } this.setQuestionTitle(this.questionId); if (!_.isEmpty(this.editorState.solutions)) { this.selectedSolutionType = this.editorState.solutions[0].type; @@ -291,9 +299,11 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { } if (_.isUndefined(this.questionId)) { this.tempQuestionId = uuidv4(); + this.hintsUUID = uuidv4(); this.populateFormData(); this.setQuestionTitle(); let editorState = {} + this.editorState.hints = {}; if (this.questionInteractionType === 'default') { if (this.questionCategory) { editorState = _.get(this.configService, `editorConfig.defaultStates.nonInteractiveQuestions.${this.questionCategory}`); @@ -305,11 +315,11 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { else if (this.questionInteractionType === 'choice') { this.editorState = new McqForm({ question: '', options: [] }, { numberOfOptions: _.get(this.questionInput, 'config.numberOfOptions'), maximumOptions: _.get(this.questionInput, 'config.maximumOptions') }); } + this.showLoader = false; /** for observation and survey to show hint,tip,dependent question option. */ if(!_.isUndefined(this.editorService?.editorConfig?.config?.renderTaxonomy)){ this.subMenuConfig(); } - this.showLoader = false; } }, (err: ServerResponse) => { const errInfo = { @@ -319,10 +329,6 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { }); } - get contentPolicyUrl() { - return this.editorService.contentPolicyUrl; - } - toolbarEventListener(event) { this.actionType = event.button; switch (event.button) { @@ -399,7 +405,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { if (this.showFormError === false && this.questionMetadataFormStatus === true) { this.saveQuestion(); } else { - this.toasterService.error(_.get(this.configService, 'labelConfig.messages.error.044')); + this.toasterService.error(_.get(this.configService, 'labelConfig.messages.error.042')); } } @@ -912,7 +918,6 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { getDefaultSessionContext() { return _.omitBy(_.merge( { - creator: _.get(this.editorService.editorConfig, 'context.user.fullName'), createdBy: _.get(this.editorService.editorConfig, 'context.user.id'), ..._.pick(_.get(this.editorService.editorConfig, 'context'), ['board', 'medium', 'gradeLevel', 'subject', 'topic']) }, @@ -941,8 +946,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { }; } metaData.interactions = metaData.interactions || {}; - - if (this.questionInteractionType !== 'default') { + if (this.questionInteractionType !== 'default' && metaData.interactions.response1) { metaData.interactions.response1.validation = { required: this.childFormData.markAsNotMandatory === 'Yes' ? 'No' : 'Yes'}; } @@ -950,18 +954,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { metaData.responseDeclaration.response1.cardinality = 'multiple'; } - _.forEach(this.subMenus, (el: any) => { - if (el.id === 'addHint') { - metaData.hints = { - en: [el.value] - }; - } - if (el.id === 'addTip') { - metaData.instructions = { - en: [el.value] - }; - } - }); + this.InsertHintAndInstructions(metaData) if (!_.isEmpty(this.sliderDatas) && this.questionInteractionType === 'slider') { metaData.interactionTypes = [this.questionInteractionType]; @@ -1007,6 +1000,20 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { // return metaData; } + InsertHintAndInstructions(metaData) { + _.forEach(this.subMenus, (el: any) => { + if (el.id === 'addHint') { + metaData.hints = metaData.hints ? metaData.hints : {}; + metaData.hints[this.hintsUUID] = {en: el.value} + this.getOutcomeDeclaration(metaData) + } + if (el.id === 'addTip') { + metaData.instructions = el.value; + } + }); + return metaData + } + prepareRequestBody() { const questionId = this.questionId ? this.questionId : uuidv4(); this.newQuestionID = questionId; @@ -1094,7 +1101,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { saveQuestions(requestBody, type) { this.showHideSpinnerLoader(true); - this.questionService.updateHierarchyQuestionCreate(requestBody).pipe( + this.questionService.updateQuestionHierarchy(requestBody).pipe( finalize(() => { this.showHideSpinnerLoader(false); })).subscribe((response: ServerResponse) => { @@ -1129,7 +1136,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { saveUpdateQuestions() { const requestBody = this.prepareRequestBody(); this.showHideSpinnerLoader(true); - this.questionService.updateHierarchyQuestionUpdate(requestBody).pipe( + this.questionService.updateQuestionHierarchy(requestBody).pipe( finalize(() => { this.showHideSpinnerLoader(false); })).subscribe((response: ServerResponse) => { @@ -1173,7 +1180,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { this.showPreview = true; this.toolbarConfig.showPreview = true; } else { - this.toasterService.error(_.get(this.configService, 'labelConfig.messages.error.044')); + this.toasterService.error(_.get(this.configService, 'labelConfig.messages.error.042')); } } @@ -1212,19 +1219,16 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { cardinality: cardinality, type: 'integer', defaultValue: this.maxScore + }, + hint: { + cardinality: "single", + type: "string", + defaultValue: this.hintsUUID ? this.hintsUUID : '' } }; return outcomeDeclaration; } - getPlayerEvents(event) { - console.log('get player events', JSON.stringify(event)); - } - - getTelemetryEvents(event) { - console.log('event is for telemetry', JSON.stringify(event)); - } - setQuestionId(questionId) { this.questionId = questionId; } @@ -1310,52 +1314,12 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { this.questionFormConfig = formConfig; } - isEditable(fieldCode) { - if (this.creationMode === 'edit') { - return true; - } - if (!this.questionId) { - return true; - } - return false; - } - populateFormData() { this.childFormData = {}; - _.forEach(this.leafFormConfig, (formFieldCategory) => { if (!_.isUndefined(this.questionId)) { - if (formFieldCategory.code === 'maxScore' && this.questionInteractionType === 'choice') { - this.childFormData[formFieldCategory.code] = _.has(this.questionMetaData, 'outcomeDeclaration.maxScore.defaultValue') ? - _.get(this.questionMetaData, 'outcomeDeclaration.maxScore.defaultValue') : this.maxScore; - } else if (formFieldCategory.code === 'allowMultiSelect' && this.questionInteractionType === 'choice') { - this.childFormData[formFieldCategory.code] = _.get(this.questionMetaData, 'responseDeclaration.response1.cardinality') === 'multiple' ? 'Yes' : 'No'; - } - else if (this.questionMetaData && _.has(this.questionMetaData, formFieldCategory.code)) { - formFieldCategory.default = this.questionMetaData[formFieldCategory.code]; - this.childFormData[formFieldCategory.code] = this.questionMetaData[formFieldCategory.code]; - } - try { - const availableAlias = { - dateFormat: 'interactions.response1.validation.pattern', - autoCapture: 'interactions.response1.autoCapture', - markAsNotMandatory: 'interactions.validation.required', - numberOnly: 'interactions.response1.type.number', - characterLimit: 'interactions.response1.validation.limit.maxLength', - remarksLimit: 'remarks.maxLength', - evidenceMimeType: 'evidence.mimeType' - }; - if (this.questionMetaData && _.has(availableAlias, formFieldCategory.code)) { - let defaultValue = _.get(this.questionMetaData, availableAlias[formFieldCategory.code]); - if (formFieldCategory.code === 'markAsNotMandatory') { - defaultValue === 'Yes' ? (defaultValue = 'No') : (defaultValue = 'Yes'); - } - formFieldCategory.default = defaultValue; - this.childFormData[formFieldCategory.code] = defaultValue; - } - } catch (error) { - - } + this.setExistingQuestionData(); } else { + _.forEach(this.leafFormConfig, (formFieldCategory) => { // tslint:disable-next-line:max-line-length const questionSetDefaultValue = _.get(this.questionSetHierarchy, formFieldCategory.code) ? _.get(this.questionSetHierarchy, formFieldCategory.code) : ''; const defaultEditStatus = _.find(this.initialLeafFormConfig, {code: formFieldCategory.code}).editable === true; @@ -1364,12 +1328,50 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { if (formFieldCategory.code === 'maxScore' && this.questionInteractionType === 'choice') { this.childFormData[formFieldCategory.code] = this.maxScore; } - } - }); + }); + } this.fetchFrameWorkDetails(); (this.isReadOnlyMode ===true && !_.isUndefined(this.editorService?.editorConfig?.config?.renderTaxonomy)) ? this.previewFormData(false) : this.previewFormData(true); } + setExistingQuestionData() { + const availableAlias = { + dateFormat: 'interactions.response1.validation.pattern', + autoCapture: 'interactions.response1.autoCapture', + markAsNotMandatory: 'interactions.validation.required', + numberOnly: 'interactions.response1.type.number', + characterLimit: 'interactions.response1.validation.limit.maxLength', + remarksLimit: 'remarks.maxLength', + evidenceMimeType: 'evidence.mimeType' + }; + _.forEach(this.leafFormConfig, (formFieldCategory) => { + if (formFieldCategory.code === 'maxScore' && this.questionInteractionType === 'choice') { + const defaultValue = _.get(this.questionMetaData, 'outcomeDeclaration.maxScore.defaultValue'); + this.childFormData[formFieldCategory.code] = defaultValue || this.maxScore; + } + else if (formFieldCategory.code === 'allowMultiSelect' && this.questionInteractionType === 'choice') { + const defaultValue = _.get(this.questionMetaData, 'responseDeclaration.response1.cardinality') + this.childFormData[formFieldCategory.code] = defaultValue === 'multiple' ? 'Yes' : 'No'; + } + else if (this.questionMetaData && _.has(availableAlias, formFieldCategory.code)) { + this.setChildAliasData(availableAlias, formFieldCategory); + } + else if (this.questionMetaData && _.has(this.questionMetaData, formFieldCategory.code)) { + formFieldCategory.default = this.questionMetaData[formFieldCategory.code]; + this.childFormData[formFieldCategory.code] = this.questionMetaData[formFieldCategory.code]; + } + }); + } + + setChildAliasData(availableAlias, formFieldCategory) { + let defaultValue = _.get(this.questionMetaData, availableAlias[formFieldCategory.code]); + if (formFieldCategory.code === 'markAsNotMandatory') { + defaultValue === 'Yes' ? (defaultValue = 'No') : (defaultValue = 'Yes'); + } + formFieldCategory.default = defaultValue; + this.childFormData[formFieldCategory.code] = defaultValue; + } + subMenuChange({ index, value }) { if (this.subMenus[index].id === 'addDependantQuestion') { this.showAddSecondaryQuestionCat = true; @@ -1391,24 +1393,8 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { } subMenuConfig() { this.subMenus = [ - { - id: 'addHint', - name: 'Add Hint', - value: _.get(this.questionMetaData, 'hints.en[0]'), - label: 'Hint', - enabled: _.get(this.questionMetaData, 'hints.en[0]') ? true : false, - type: 'input', - show: _.get(this.sourcingSettings, 'showAddHints') - }, - { - id: 'addTip', - name: 'Add Tip', - value: _.get(this.questionMetaData, 'instructions.en[0]'), - label: 'Tip', - enabled: _.get(this.questionMetaData, 'instructions.en[0]') ? true : false, - type: 'input', - show: _.get(this.sourcingSettings, 'showAddTips') - }, + this.getHints(), + this.getInstructions(), { id: 'addDependantQuestion', name: 'Add Dependant Question', @@ -1417,13 +1403,64 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { enabled: false, type: '', show: _.get(this.sourcingSettings, 'showAddSecondaryQuestion') && !this.questionInput.setChildQueston - }, + } ]; if (!_.get(this.sourcingSettings, 'showAddSecondaryQuestion') && !this.questionInput.setChildQueston) { this.showOptions = false; } else { this.showOptions = (this.questionInput.setChildQueston === true) ? true : false; + } + } + getHints() { + return { + id: 'addHint', + name: 'Add Hint', + value:(() => { + if(this.questionMetaData?.outcomeDeclaration ) { + return this.questionMetaData?.hints[this.questionMetaData.outcomeDeclaration.hint.defaultValue].en; + } + else { + return ''; + } + })(), + label: 'Hint', + enabled:(() => { + if(this.questionMetaData?.outcomeDeclaration && this.questionMetaData?.hints[this.questionMetaData.outcomeDeclaration.hint.defaultValue].en.length > 0) { + return true; + } + else { + return false; + } + })(), + type: 'input', + show: _.get(this.sourcingSettings, 'showAddHints') + } } + + getInstructions() { + return { + id: 'addTip', + name: 'Add Tip', + value: (() => { + if(this.questionMetaData) { + return this.questionMetaData?.instructions + } + else { + return ''; + } + })(), + label: 'Tip', + enabled: (() => { + if(this.questionMetaData && this.questionMetaData?.instructions?.length > 0) { + return true; + } + else { + return false; + } + })(), + type: 'input', + show: _.get(this.sourcingSettings, 'showAddTips') + } } ngOnDestroy() { this.onComponentDestroy$.next(); @@ -1541,7 +1578,7 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { if (res.responseCode === 'OK') { const result = res.result.question; if (result.interactionTypes[0] === 'choice') { - const numberOfOptions = result.editorState.options.length; + const numberOfOptions = result.interactions.response1.options.length; this.editorService.optionsLength = numberOfOptions; this.getOptions(); } @@ -1613,4 +1650,4 @@ export class QuestionComponent implements OnInit, AfterViewInit, OnDestroy { openRequestChangesPopup() { this.requestChangesPopupAction = 'rejectQuestion'; } -} +} \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.spec.data.ts index 0b09988e1..90eb1d405 100644 --- a/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/quml-player/quml-player.component.spec.data.ts @@ -589,9 +589,7 @@ export const mockData = { }, "shuffle": true, "board": "CBSE", - "instructions": { - "default": "
  1. The instructions
HeaderInstruction1Instruction1
   
   

 

" - }, + "instructions": "
  1. The instructions
HeaderInstruction1Instruction1
   
   

 

", "level": 1, "topic": "", "maxTime": "00:05:00", diff --git a/projects/questionset-editor-library/src/lib/components/qumlplayer-page/qumlplayer-page.component.spec.data.ts b/projects/questionset-editor-library/src/lib/components/qumlplayer-page/qumlplayer-page.component.spec.data.ts index 23fc4b514..5d9a6095c 100644 --- a/projects/questionset-editor-library/src/lib/components/qumlplayer-page/qumlplayer-page.component.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/components/qumlplayer-page/qumlplayer-page.component.spec.data.ts @@ -496,9 +496,7 @@ export const mockData = { }, "shuffle": true, "board": "CBSE", - "instructions": { - "default": "
  1. The instructions
HeaderInstruction1Instruction1
   
   

 

" - }, + "instructions": "
  1. The instructions
HeaderInstruction1Instruction1
   
   

 

", "level": 1, "topic": "", "maxTime": "00:05:00", diff --git a/projects/questionset-editor-library/src/lib/components/term-and-condition/term-and-condition.component.scss b/projects/questionset-editor-library/src/lib/components/term-and-condition/term-and-condition.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/projects/questionset-editor-library/src/lib/components/term-and-condition/term-and-condition.component.ts b/projects/questionset-editor-library/src/lib/components/term-and-condition/term-and-condition.component.ts index 0bde2fcba..721d6de5f 100644 --- a/projects/questionset-editor-library/src/lib/components/term-and-condition/term-and-condition.component.ts +++ b/projects/questionset-editor-library/src/lib/components/term-and-condition/term-and-condition.component.ts @@ -5,8 +5,7 @@ import {EditorService} from '../../services/editor/editor.service'; @Component({ selector: 'lib-term-and-condition', - templateUrl: './term-and-condition.component.html', - styleUrls: ['./term-and-condition.component.scss'] + templateUrl: './term-and-condition.component.html' }) export class TermAndConditionComponent { @Input() showEditingConsent = true; diff --git a/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts b/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts index 592acfe77..554693e5c 100644 --- a/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts +++ b/projects/questionset-editor-library/src/lib/components/translations/translations.component.ts @@ -43,9 +43,7 @@ export class TranslationsComponent { hints: { en: ["hints in the specified language"], }, - instructions: { - en: "instructions in the specified language", - }, + instructions: "instructions in the specified language", interactions: { response1: { type: "choice", diff --git a/projects/questionset-editor-library/src/lib/directives/date-format/date-format.pipe.spec.ts b/projects/questionset-editor-library/src/lib/directives/date-format/date-format.pipe.spec.ts deleted file mode 100644 index 1d8fd97db..000000000 --- a/projects/questionset-editor-library/src/lib/directives/date-format/date-format.pipe.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DateFormatPipe } from './date-format.pipe'; -import * as moment from 'moment'; - -describe('DateFormatPipe', () => { - describe('#transform', () => { - it('should take default format for date', () => { - const pipe = new DateFormatPipe(); - const date = new Date(); - const result = pipe.transform(date, ''); - const ans = moment(date).format('Do MMMM YYYY'); - expect(result).toBe(ans); - }); - - it('test for given format for date', () => { - const pipe = new DateFormatPipe(); - const date = new Date(); - const result = pipe.transform(date, 'MMMM YYYY Do'); - const ans = moment(date).format('MMMM YYYY Do'); - expect(result).toBe(ans); - }); - - it('if date is blank', () => { - const pipe = new DateFormatPipe(); - const result = pipe.transform('', 'Do MMMM YYYY'); - expect(result).toBe('-'); - }); - - }); -}); diff --git a/projects/questionset-editor-library/src/lib/directives/date-format/date-format.pipe.ts b/projects/questionset-editor-library/src/lib/directives/date-format/date-format.pipe.ts deleted file mode 100644 index 5295e59c5..000000000 --- a/projects/questionset-editor-library/src/lib/directives/date-format/date-format.pipe.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Pipe, PipeTransform } from '@angular/core'; -import * as moment from 'moment'; -const momentConstructor: (value?: any) => moment.Moment = (moment).default || moment; - -/** - * Pipe for date format - * - */ -@Pipe({ - name: 'dateFormat' -}) -export class DateFormatPipe implements PipeTransform { - /** - * To create date format pipe - * - * @param {Date} value current Date, string or number - * @param {string} format format of Date - * - */ - transform(value: Date | moment.Moment | string | number, format: string): string { - if (value) { - return momentConstructor(value).format(format || 'Do MMMM YYYY'); - } else { - return '-'; - } - } - -} diff --git a/projects/questionset-editor-library/src/lib/interfaces/CreationContext.ts b/projects/questionset-editor-library/src/lib/interfaces/CreationContext.ts index b0d68fc08..e8c3047e6 100644 --- a/projects/questionset-editor-library/src/lib/interfaces/CreationContext.ts +++ b/projects/questionset-editor-library/src/lib/interfaces/CreationContext.ts @@ -2,7 +2,7 @@ export interface ICreationContext { objectType?: string; collectionObjectType?: string; unitIdentifier?: string; - isReadOnlyMode?: boolean | undefined; + isReadOnlyMode?: boolean; mode?: string; correctionComments?: string, editableFields?: any; diff --git a/projects/questionset-editor-library/src/lib/interfaces/McqForm.ts b/projects/questionset-editor-library/src/lib/interfaces/McqForm.ts index 45c0b61df..bc2c12dcc 100644 --- a/projects/questionset-editor-library/src/lib/interfaces/McqForm.ts +++ b/projects/questionset-editor-library/src/lib/interfaces/McqForm.ts @@ -38,7 +38,7 @@ export class McqForm { this.maxScore = maxScore; this.numberOfOptions = numberOfOptions || 2; this.maximumOptions = maximumOptions || 4 - if (!this.options || !this.options.length) { + if (!this.options || !this.options?.length) { _.times(this.numberOfOptions, index => this.options.push(new McqOptions(''))); } } diff --git a/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts b/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts index b77cdc461..d456b4b61 100644 --- a/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts +++ b/projects/questionset-editor-library/src/lib/questionset-editor-library.module.ts @@ -20,7 +20,6 @@ import { AnswerComponent } from './components/answer/answer.component'; import { CkeditorToolComponent } from './components/ckeditor-tool/ckeditor-tool.component'; import { QuestionComponent } from './components/question/question.component'; import { TelemetryInteractDirective } from './directives/telemetry-interact/telemetry-interact.directive'; -import { DateFormatPipe } from './directives/date-format/date-format.pipe'; import { AssetBrowserComponent } from './components/asset-browser/asset-browser.component'; import { CollectionIconComponent } from './components/collection-icon/collection-icon.component'; import { QumlPlayerComponent } from './components/quml-player/quml-player.component'; @@ -54,7 +53,6 @@ import { AssetsBrowserComponent } from './components/assets-browser/assets-brows AnswerComponent, CkeditorToolComponent, TemplateComponent, - DateFormatPipe, TelemetryInteractDirective, AssetBrowserComponent, CollectionIconComponent, diff --git a/projects/questionset-editor-library/src/lib/services/config/category.config.json b/projects/questionset-editor-library/src/lib/services/config/category.config.json index 68fa11002..a090e59d4 100644 --- a/projects/questionset-editor-library/src/lib/services/config/category.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/category.config.json @@ -1,9 +1,7 @@ { "QuestionSet": "questionset", - "Collection": "content", "Question": "question", "additionalCategories": { - "QuestionSet": "contentAdditionalCategories", - "Collection": "collectionAdditionalCategories" + "QuestionSet": "contentAdditionalCategories" } } diff --git a/projects/questionset-editor-library/src/lib/services/config/editor.config.json b/projects/questionset-editor-library/src/lib/services/config/editor.config.json index 3757b97da..b9d7a4b4d 100644 --- a/projects/questionset-editor-library/src/lib/services/config/editor.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/editor.config.json @@ -4,7 +4,6 @@ "showOriginPreviewUrl": false, "showSourcingStatus": false, "showCorrectionComments": false, - "publicStorageAccount": "", "assetConfig": { "image": { "size": "1", @@ -27,10 +26,7 @@ "readQuestionFields": "body,primaryCategory,mimeType,qType,answer,templateId,responseDeclaration,interactionTypes,interactions,name,solutions,editorState,media,remarks,evidence,hints,instructions,outcomeDeclaration,", "omitFalseyProperties":["topic", "topicsIds", "targetTopicIds", "keywords"], "questionSet": { - "maxQuestionsLimit": "500" - }, - "collection": { - "maxContentsLimit": "1200" + "maxQuestionsLimit": 500 }, "defaultStates": { "nonInteractiveQuestions": { diff --git a/projects/questionset-editor-library/src/lib/services/config/label.config.json b/projects/questionset-editor-library/src/lib/services/config/label.config.json index 75c4bb495..4dfc84fec 100644 --- a/projects/questionset-editor-library/src/lib/services/config/label.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/label.config.json @@ -30,7 +30,6 @@ "search_btn_label":"Search", "add_btn_label":"Add", "upload_from_computer_btn_label":"Upload from Computer", - "request_btn_label":"Request", "no_btn_label":"No", "yes_btn_label":"Yes", "close_btn_label":"Close", @@ -40,8 +39,6 @@ "create_new_btn_label":"Create New", "add_from_library_btn_label":" Add from library", "submit_review_btn_label":"Submit Review", - "apply_btn_label":"Apply", - "reset_btn_label":"Reset", "delete_btn_label":"Delete", "next_btn_label":"Next", "remove_btn_label":"Remove", @@ -50,29 +47,7 @@ "add_page_numbers_to_questions_btn_label": "Pagination" }, "lbl":{ - "search":"Search", - "subject":"Subject", - "medium":"Medium", - "gradeLevel":"Class", - "contentType":"Content Type", - "reset":"Reset", - "apply":"apply", - "filterText":"Change Filters", "Questiondetails":"Question details", - "selectContent":"Select content", - "noMatchingContent":"Sorry there is no matching content", - "changeFilterMessage":"Changing filter helps you find more content", - "changeFilter":"Change filters", - "whereDoYouWantToAddThisContent":"Where do you want to add this content?", - "selectContentToAdd":"Use search and filters above to find more content", - "addedToCollection":"Added to collection", - "changingFilters":"Changing filters make you find more content", - "ChangeFilters":"Change filters", - "addFromLibrary":"Add from Library", - "showContentAddedToCollection":"Show content added to collection", - "addContent":"Add content", - "sortBy":"Sort By", - "sortlabel":"A - Z", "viewOnOrigin":"View Content on consumption", "answers":"Answers", "answersRequired":"Answer is required", @@ -119,23 +94,11 @@ "selectAudio":"Select Audio", "searchPlaceholder":"Search...", "addAnImage":"Add an image", - "name":"Name", - "copyRightsAndYear":"Copyright & Yrar", - "license":"License", - "author":"Author", - "grade":"Grade", - "board":"Board", - "audience":"Audience", - "copyRight":"Copyright", - "licensedBy":"Licensed by", - "attributions":"Attributions", - "requestForQrCode":"Request for QR Codes", "confirmDeleteContent":"Confirm Delete Content", "confirmDeleteNode":"Are you sure want to delete the selected Node?", "comments":"Comments", "reviewComments":"Review Comments", "questionSetPreview":"Question Set Preview", - "numberToolarge": "This number is too large for the request", "acceptTerms":"Accepting Terms & Conditions", "iAgreeSubmit":"I agree that by submitting / publishing this Content,", "iconfirmContent":"I confirm that this Content complies with prescribed guidelines, including the Terms of Use and Content Policy and that I consent to publish it under the", @@ -149,7 +112,6 @@ "publishCollection":"Publish ${objectType}", "confirmPublishCollection":"Are you sure you want to publish this ${objectType}?", "fillComments":"Fill comments", - "searchLibrary":"Search Library", "options":"Options", "optionsPopupText":"Please Preview to check how layout looks. Layout is responsive to the resolution of your device", "selectOneAns":"Select one correct answer", @@ -167,45 +129,18 @@ "solution":"Solution", "optional":"(Optional)", "questionRequired":"Question is required", - "addingTo":"Adding To", "selectTemplate":"Select a template to get started", "createNew":"Create new", "selectLayout":"Select Layout", "vertical":"Vertical", "grid":"Grid", "horizontal":"Horizontal", - "folders":"Folders", - "createHierarchyCsv":"Create folders using csv file", - "downloadFoldersInCSV":"Download folders as csv file", - "uploadUpdateCSV":"Update folder metadata using csv file", - "downloadSampleHierarchyCSv":"Upload csv file as per the given sample to create folders", - "makeSureFile":"Make sure that:", - "allColumnsAreAvailable":"The file has all the required columns", - "hasAllMandatoryColumn":"The file has all the required values filled in as per the format", - "noDuplicateRow":"There are no duplicate rows (with exactly same folder levels) in the file", - "downloadSampleCSV":"Download sample csv file", - "dragAndDropCSV":"Drag and Drop files to upload", - "selectFileToUpload":"Select file to upload", - "uploadEntries":"File accepted - csv", - "Or":"Or", - "pleaseWait":"Please wait", - "validateCsvFile":"Validating CSV file", - "hierarchyValidationError":"Error in processing the file", - "followingErrors":"Following errors are found in the file. Please correct and upload again", - "reUploadCSV":"Upload file again", - "hierarchyValidation":"Hierarchy Validation", - "hierarchyAdded":"Folders have been successfully created. Please close the dialog", - "hierarchyUpdated":"Folder metadata has been successfully updated. Please close the dialog", - "successful":"Successful !", - "csvDownloadInstruction":"Please make sure that this is the file downloaded using the “Download folders as csv file” option and changes are made to it", "sliderValue":"Slider Value", "left":"Left", "stepSize":"Step size", "right":"Right", "translation":"Translation", "publishchecklistTitle": "Please confirm that ALL the following items are verified (by ticking the check-boxes) before you can publish:", - "retry": "retry", - "dragAndDrop": "Drag and Drop file", "loaderHeading": "Please wait", "loaderMessage": "We are fetching details.", "slidervalue": "Set the minimum and maximum values with which the slider would start and end respectively.", @@ -225,7 +160,6 @@ "somethingWentWrong":"Something went wrong", "contentNotFoundonOrigin": "The content not found in consumption", "resultNotFound": "No Results Found", - "userNotFound": "No users found", "searchConfigNotFound": "Search filters configuration not available. Please contact system admin for support." }, "termsAndConditions": { @@ -261,13 +195,8 @@ "006": "Fetch question set template failed. Please try again...", "007": "Sorry, this operation is not allowed...", "008": "Please fill the required fields", - "009": "File is required to upload", "010": "No external link allowed", "011": "Unable to read the Video, Please Try Again", - "012": "QR code(s) updating failed!", - "013": "No new QR Codes have been generated!", - "014": "The number should be at least {number}", - "015": "The number should be greater than {number}", "016": "No images found, please try searching for something else", "017": "No videos found, please try searching for something else", "018": "Unable to upload to Blob and Content Creation Failed, Please Try Again", @@ -280,48 +209,30 @@ "025": " Unable to create an Asset", "026": "Unable to get pre_signed_url and Content Creation Failed, Please Try Again", "027": "Unable to update pre_signed_url with Content Id and Content Creation Failed, Please Try Again", - "028": "This number is too large for the request", "029": "Please fill the required metadata", "030": "search failed", "031": "Cannot create question. reached limit of maximum number of questions allowed.", - "032": "Cannot add content. reached limit of maximum number of contents allowed.", + "032": "Cannot add question. reached limit of maximum number of questions allowed.", "033": "failed to convert url to blob ", "034": "failed to download {FILE_TYPE} file using blob url ", - "035": "Unable to get pre_signed_url and Question bulk upload Failed, Please Try Again", - "036": "Unable to upload to Blob and Question bulk upload Failed, Please Try Again", "037": "Unable to accepted question", "038": "Unable to approve content", - "039": "Unable to reject content", - "040": "Unable to send content back for corrections", - "041": "Cannot add question. reached limit of maximum number of questions allowed.", - "042": "Cannot create content. reached limit of maximum number of contents allowed.", - "043": "Adding question to questionset failed. Please try again.", - "044": "Please fill valid question details." + "041": "Adding question to questionset failed. Please try again.", + "042": "Please fill valid question details." }, "success": { "001": "Content is saved", "002": "Content is sent for review", "003": "Content is sent back for corrections", "004": "Content is published", - "005": "Content is added to the folder", "006": "Asset Successfully Uploaded...", "007": "Question is created sucessfully", "008": "Question is updated sucessfully", - "009": "QR code(s) updated successfully", - "010": "QR code generated.", - "011": "QR codes downloaded" , "013": "Question saved successfully", "037": "Question accepted successfully", "038": "Content is successfully approved", "039": "Content is successfully rejected", "040": "Content sent back for corrections" - }, - "warning" : { - "001" : "Errors found in linked QR Codes. Please check and correct.", - "002" : "Unable to update some of the QR codes." - }, - "info" : { - "001" : "QR code image generation is in progress. Please try downloading after sometime" } }, "categoryLabels": { diff --git a/projects/questionset-editor-library/src/lib/services/config/player.config.json b/projects/questionset-editor-library/src/lib/services/config/player.config.json index 896bb03d5..e4884c00f 100644 --- a/projects/questionset-editor-library/src/lib/services/config/player.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/player.config.json @@ -1,101 +1,3 @@ { - "playerConfig":{ - "context":{ - "mode":"play", - "partner":[ - - ], - "pdata":{ - "id":"sunbird.portal", - "ver":"1.8.0", - "pid":"sunbird-portal" - } - }, - "config":{ - "showEndPage":false, - "endPage":[ - { - "template":"assessment", - "contentType":[ - "SelfAssess" - ] - } - ], - "showStartPage":true, - "host":"", - "overlay":{ - "showUser":false - }, - "splash":{ - "text":"", - "icon":"", - "bgImage":"assets/icons/splacebackground_1.png", - "webLink":"" - }, - "sideMenu":{ - "showDownload":true, - "showExit":false, - "showShare":true - }, - "apislug":"/action", - "repos":[ - "/sunbird-plugins/renderer" - ], - "plugins":[ - { - "id":"org.sunbird.iframeEvent", - "ver":1.0, - "type":"plugin" - }, - { - "id":"org.sunbird.player.endpage", - "ver":1.1, - "type":"plugin" - } - ] - } - }, - "contentType":{ - "Course":"Course" - }, - "MIME_TYPE":{ - "collection":"application/vnd.ekstep.content-collection", - "ecmlContent":"application/vnd.ekstep.ecml-archive", - "genericMimeType":[ - "application/pdf", - "video/mp4", - "video/x-youtube", - "video/youtube", - "application/vnd.ekstep.html-archive", - "application/epub", - "application/vnd.ekstep.h5p-archive", - "video/webm", - "text/x-url" - ], - "pdf":"application/pdf", - "mp4":"video/mp4", - "youtube":"video/x-youtube", - "pYoutube":"video/youtube", - "html":"application/vnd.ekstep.html-archive", - "ePub":"application/epub", - "h5p":"application/vnd.ekstep.h5p-archive", - "webm":"video/webm", - "xUrl":"text/x-url" - }, - "playerType":{ - "pdf-player":[ - "application/pdf" - ], - "video-player":[ - "video/mp4", - "video/webm" - ], - "epub-player": [ - "application/epub" - ] - }, - "baseURL":"/content/preview/preview.html?webview=true", - "localBaseUrl":"/contentPlayer/preview/preview.html?", - "cdnUrl":"/content/preview/preview_cdn.html?webview=true", "threshold" : 2 } \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/services/config/url.config.json b/projects/questionset-editor-library/src/lib/services/config/url.config.json index 84cc3811b..fdb1fc20f 100644 --- a/projects/questionset-editor-library/src/lib/services/config/url.config.json +++ b/projects/questionset-editor-library/src/lib/services/config/url.config.json @@ -48,14 +48,6 @@ "frameworkRead":"framework/v1/read/", "channelRead":"channel/v1/read/", "telemetry":"/data/v3/telemetry", - "CSV": { - "UPLOAD": "collection/v1/import/", - "DOWNLOAD": "collection/v1/export/", - "SAMPLE_COLLECTION_HIERARCHY": "sourcing/collection-hierarchy/createfoldersamplecsvfile.csv" - }, - "USER": { - "SEARCH":"user/v1/search" - }, "ASSET": { "READ":"asset/v1/read/", "CREATE":"asset/v1/create", diff --git a/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.data.ts b/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.data.ts index 155721ce3..52486747f 100644 --- a/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.data.ts @@ -238,9 +238,7 @@ export const questionsData = [ author: 'N11', solutions: [], hints: { - en: [ - null - ] + "70c82bf5-9459-4c43-8897-0e58b7e1da64": { en: "test hint 1" }, }, qType: 'SA', languageCode: [ @@ -356,9 +354,7 @@ export const questionsData = [ author: 'N11', solutions: [], hints: { - en: [ - null - ] + "70c82bf5-9459-4c43-8897-0e58b7e1da64": { en: "test hint 1" }, }, qType: 'MCQ', languageCode: [ @@ -403,4 +399,4 @@ export const serverResponse = { ts: '', ver: '', headers: {} -}; +}; \ No newline at end of file diff --git a/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts b/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts index b0c0fbc63..aa4f06496 100755 --- a/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts +++ b/projects/questionset-editor-library/src/lib/services/editor/editor.service.spec.ts @@ -22,7 +22,8 @@ describe('EditorService', () => { const configStub = { urlConFig: (urlConfig as any).default, labelConfig: (labelConfig as any).default, - categoryConfig: (categoryConfig as any).default + categoryConfig: (categoryConfig as any).default, + editorConfig: (editorConfig as any).default }; const configServiceData = { labelConfig: { @@ -267,103 +268,6 @@ describe('EditorService', () => { expect(editorService.getContentChildrens).toHaveBeenCalled(); expect(result).toBe(false); }); - it('#downloadBlobUrlFile() should download the file', () => { - const service: EditorService = TestBed.inject(EditorService); - const httpClient = TestBed.inject(HttpClient); - const toasterService = TestBed.inject(ToasterService); - spyOn(toasterService, 'success').and.callFake(() => {}); - const downloadConfig = { - // tslint:disable-next-line:max-line-length - blobUrl: 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/textbook/toc/do_113312173590659072160_dev-testing-1_1625022971409.csv', - successMessage: 'File downloaded', - fileType: 'csv', - fileName: 'do_113312173590659072160' - }; - spyOn(httpClient, 'get').and.returnValue(of(new Blob([downloadConfig.blobUrl], {}))); - spyOn(service, 'downloadBlobUrlFile').and.callThrough(); - service.downloadBlobUrlFile(downloadConfig); - expect(httpClient.get).toHaveBeenCalled(); - expect(toasterService.success).toHaveBeenCalledWith(configServiceData.labelConfig.messages.success['011']); - }); - it('#downloadBlobUrlFile() should download the file and dose not show toaster message', () => { - const service: EditorService = TestBed.inject(EditorService); - const http = TestBed.inject(HttpClient); - const toasterService = TestBed.inject(ToasterService); - spyOn(toasterService, 'success').and.callThrough(); - const downloadConfig = { - // tslint:disable-next-line:max-line-length - blobUrl: 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/textbook/toc/do_113312173590659072160_dev-testing-1_1625022971409.csv', - successMessage: false, - fileType: 'csv', - fileName: 'do_113312173590659072160' - }; - spyOn(http, 'get').and.returnValue(of(new Blob([downloadConfig.blobUrl], {}))); - spyOn(service, 'downloadBlobUrlFile').and.callThrough(); - service.downloadBlobUrlFile(downloadConfig); - expect(http.get).toHaveBeenCalled(); - expect(http.get).toHaveBeenCalledTimes(1); - expect(http.get).toHaveBeenCalled(); - expect(toasterService.success).not.toHaveBeenCalledWith(configServiceData.labelConfig.messages.success['011']); - }); - it('#downloadHierarchyCsv() should downloadHierarchyCsv', async () => { - const publicDataService: PublicDataService = TestBed.inject(PublicDataService); - spyOn(publicDataService, 'get').and.returnValue(of({ - id: 'api.collection.export', - ver: '4.0', - ts: '2021-07-05T07:43:10ZZ', - params: { - resmsgid: 'd54936f9-9f9a-449a-a797-5564d5a97c6c', - msgid: null, - err: null, - status: 'successful', - errmsg: null - }, - responseCode: 'OK', - result: { - collection: { - // tslint:disable-next-line:max-line-length - tocUrl: 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/content/textbook/toc/do_113312173590659072160_dev-testing-1_1625022971409.csv', - ttl: '54000' - } - } - })); - editorService.downloadHierarchyCsv('do_113312173590659072160').subscribe(data => { - expect(data.responseCode).toBe('OK'); - }); - }); - it('#validateCSVFile() should validateCSVFile', async () => { - const publicDataService: PublicDataService = TestBed.inject(PublicDataService); - const file = new File([''], 'filename', { type: 'csv/text' }); - const event = { - target: { - files: [ - file - ] - } - }; - spyOn(publicDataService, 'post').and.returnValue(of({ - id: 'api.collection.import', - ver: '4.0', - ts: '2021-07-05T08:28:06ZZ', - params: { - resmsgid: 'f151855b-98fd-4baf-b8dc-00c31cc47b71', - msgid: null, - err: 'INVALID_CSV_FILE', - status: 'failed', - errmsg: 'Please provide valid csv file. Please check for data columns without headers.' - }, - responseCode: 'CLIENT_ERROR', - result: { - messages: null - } - })); - editorService.validateCSVFile(event.target.files[0], 'do_113312173590659072160').subscribe(data => { - }, - error => { - expect(error.error.responseCode).toBe('CLIENT_ERROR'); - - }); - }); it('#generatePreSignedUrl() should call generatePreSignedUrl', () => { const publicDataService: PublicDataService = TestBed.inject(PublicDataService); spyOn(publicDataService, 'post').and.returnValue(of()); @@ -570,15 +474,6 @@ describe('EditorService', () => { expect(editorService._toFlatObjFromHierarchy).toHaveBeenCalled(); }); - it('#fetchOutComeDeclaration() should return the levels for rubrics', async()=> { - const questionSetId = 'do_11330102570702438417'; - const publicDataService = TestBed.inject(PublicDataService); - spyOn(publicDataService, 'get').and.returnValue(of(mockData.serverResponse)); - editorService.fetchOutComeDeclaration(questionSetId).subscribe(data => { - expect(data.responseCode).toEqual('OK'); - }); - }); - it('#appendCloudStorageHeaders should set cloud storage headers if exist', () => { const config = editorService.appendCloudStorageHeaders({}); expect(config).toEqual({headers: { 'x-ms-blob-type': 'BlockBlob' }}); diff --git a/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts b/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts index a5cc8404e..6f07b404e 100755 --- a/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts +++ b/projects/questionset-editor-library/src/lib/services/editor/editor.service.ts @@ -10,7 +10,6 @@ import { EditorTelemetryService } from '../../services/telemetry/telemetry.servi import { DataService } from '../data/data.service'; import { map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; -import { ExportToCsv } from 'export-to-csv'; interface SelectedChildren { label?: string; primaryCategory?: string; @@ -48,7 +47,7 @@ export class EditorService { public initialize(config: IEditorConfig) { this._editorConfig = config; - if (this.configService.editorConfig && this.configService.editorConfig.default) { + if (this.configService?.editorConfig && this.configService?.editorConfig?.default) { this._editorConfig.config = _.assign(this.configService.editorConfig.default, this._editorConfig.config); } this._editorMode = _.get(this._editorConfig, 'config.mode').toLowerCase(); @@ -86,7 +85,7 @@ export class EditorService { } private setIsReviewerEditEnable(value: boolean) { - return this._isReviewerEditEnable = value; + this._isReviewerEditEnable = value; } get isReviewModificationAllowed() { @@ -94,7 +93,7 @@ export class EditorService { } setIsReviewModificationAllowed(value: boolean) { - return this._isReviewModificationAllowed = value; + this._isReviewModificationAllowed = value; } get contentPolicyUrl() { @@ -345,10 +344,8 @@ export class EditorService { if (!_.isEmpty(children, 'children')) { if (children.children[i].data.objectType === 'QuestionSet') { self.setQuestionIds([children.children[i]]); - } else { - if (!_.includes(this.questionIds, children.children[i].data.id)) { - this.questionIds.push(children.children[i].data.id); - } + } else if (!_.includes(this.questionIds, children.children[i].data.id)) { + this.questionIds.push(children.children[i].data.id); } } } @@ -380,12 +377,10 @@ export class EditorService { const nodeData = this.treeService.getNodeById(question.identifier); if (_.has(nodeData.parent.data.metadata, 'shuffle') && nodeData.parent.data.metadata.shuffle === true) { return sum + 1; + } else if (question?.responseDeclaration?.response1) { + return sum + (question?.outcomeDeclaration?.maxScore?.defaultValue ? _.get(question, 'outcomeDeclaration.maxScore.defaultValue') : 0); } else { - if (question?.responseDeclaration?.response1) { - return sum + (question?.outcomeDeclaration?.maxScore?.defaultValue ? _.get(question, 'outcomeDeclaration.maxScore.defaultValue') : 0); - } else { - return sum + 0; - } + return sum + 0; } }, 0); } @@ -402,7 +397,7 @@ export class EditorService { getHierarchyObj(data, questionId?, selectUnitId?, parentId?) { const instance = this; - if (data && data.data) { + if (data?.data) { const relationalMetadata = this.getRelationalMetadataObj(data.children); instance.data[data.data.id] = { name: data.title, @@ -435,7 +430,7 @@ export class EditorService { _toFlatObjFromHierarchy(data) { const instance = this; - if (data && data.children) { + if (data?.children) { instance.data[data.identifier] = { name: data.name, children: _.map(data.children, (child) => { @@ -547,20 +542,11 @@ export class EditorService { }; if (_.get(this.editorConfig, 'config.objectType') === 'QuestionSet') { config.maxLimit = _.get(this.editorConfig, 'config.questionSet.maxQuestionsLimit'); - if (buttonAction === 'add') { - config.errorMessage = _.get(this.configService, 'labelConfig.messages.error.041'); - } - if (buttonAction === 'create') { - config.errorMessage = _.get(this.configService, 'labelConfig.messages.error.031'); - } - - } else { - config.maxLimit = _.get(this.editorConfig, 'config.collection.maxContentsLimit'); if (buttonAction === 'add') { config.errorMessage = _.get(this.configService, 'labelConfig.messages.error.032'); } if (buttonAction === 'create') { - config.errorMessage = _.get(this.configService, 'labelConfig.messages.error.042'); + config.errorMessage = _.get(this.configService, 'labelConfig.messages.error.031'); } } const childrenCount = this.getContentChildrens().length + this.contentsCount; @@ -583,21 +569,6 @@ export class EditorService { } return contents; } - validateCSVFile(formData, collectionnId: any) { - const url = _.get(this.configService.urlConFig, 'URLS.CSV.UPLOAD'); - const reqParam = { - url: `${url}${collectionnId}`, - data: formData.data - }; - return this.publicDataService.post(reqParam); - } - downloadHierarchyCsv(collectionId) { - const url = _.get(this.configService.urlConFig, 'URLS.CSV.DOWNLOAD'); - const req = { - url: `${url}${collectionId}`, - }; - return this.publicDataService.get(req); - } generatePreSignedUrl(req, contentId: any, type) { const reqParam = { url: `${this.configService.urlConFig.URLS.CONTENT.UPLOAD_URL}${contentId}?type=${type}`, @@ -607,48 +578,6 @@ export class EditorService { }; return this.publicDataService.post(reqParam); } - downloadBlobUrlFile(config) { - try { - this.httpClient.get(config.blobUrl, {responseType: 'blob'}) - .subscribe(blob => { - const objectUrl: string = URL.createObjectURL(blob); - const a: HTMLAnchorElement = document.createElement('a') as HTMLAnchorElement; - a.href = objectUrl; - a.download = config.fileName; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(objectUrl); - if (config.successMessage) { - this.toasterService.success(config.successMessage); - } - }, (error) => { - console.error(_.get(this.configService, 'labelConfig.messages.error.034') + error); - }); - } catch (error) { - console.error( _.replace(_.get(this.configService, 'labelConfig.messages.error.033'), '{FILE_TYPE}', config.fileType ) + error); - } - } - - generateCSV(config) { - const tableData = config.tableData; - delete config.tableData; - let options = { - fieldSeparator: ',', - quoteStrings: '"', - decimalSeparator: '.', - showLabels: true, - useTextFile: false, - useBom: true, - showTitle: true, - title: '', - filename: '', - headers: [] - }; - options = _.merge(options, config); - const csvExporter = new ExportToCsv(options); - csvExporter.generateCsv(tableData); - } getBranchingLogicByFolder(identifier) { const nodeData = this.treeService.getNodeById(identifier); @@ -723,30 +652,12 @@ getDependentNodes(identifier) { return _.get(nodeData, 'data.primaryCategory'); } - /** - * fetch Outcome Declaration levels using the questionsetId - * only for Observation with Rubrics - * @param identifier questionset identifier - */ - fetchOutComeDeclaration(questionSetId, option: any = { params: {} }): Observable { - const url = this.configService.urlConFig.URLS[this.editorConfig.config.objectType]; - const param = { - fields: 'outcomeDeclaration' - }; - const hierarchyUrl = `${url.READ}/${questionSetId}`; - const req = { - url: hierarchyUrl, - param: { ...param, ...option.params } - }; - return this.publicDataService.get(req); - } - get qualityFormConfig(){ return this._qualityFormConfig; } private setQualityFormConfig(value: any){ - return this._qualityFormConfig = value; + this._qualityFormConfig = value; } get isReviewerQualityCheckEnabled(){ @@ -754,7 +665,7 @@ getDependentNodes(identifier) { } private setIsReviewerQualityCheckEnabled(value: boolean){ - return this._isReviewerQualityCheckEnabled = value; + this._isReviewerQualityCheckEnabled = value; } appendCloudStorageHeaders(config) { diff --git a/projects/questionset-editor-library/src/lib/services/helper/helper.service.spec.ts b/projects/questionset-editor-library/src/lib/services/helper/helper.service.spec.ts index 5e7ae7d63..2c8611f6e 100644 --- a/projects/questionset-editor-library/src/lib/services/helper/helper.service.spec.ts +++ b/projects/questionset-editor-library/src/lib/services/helper/helper.service.spec.ts @@ -130,22 +130,6 @@ describe('HelperService', () => { expect(result).toEqual('HH:mm:ss'); }); - it('#getAllUser() should call publicDataService.post()', () => { - const publicDataService: PublicDataService = TestBed.inject(PublicDataService); - const userSearchBody = { - request: { - filters: { - 'organisations.roles': 'CONTENT_CREATOR', - rootOrgId: '12345' - } - } - }; - spyOn(publicDataService, 'post').and.returnValue(of(serverResponse)); - spyOn(service, 'getAllUser').and.callThrough(); - service.getAllUser(userSearchBody); - expect(publicDataService.post).toHaveBeenCalled(); - }); - it('#addDepthToHierarchy should call', () => { spyOn(service, 'addDepthToHierarchy').and.callThrough(); const data = [ diff --git a/projects/questionset-editor-library/src/lib/services/helper/helper.service.ts b/projects/questionset-editor-library/src/lib/services/helper/helper.service.ts index 5a640340e..8fadaf938 100644 --- a/projects/questionset-editor-library/src/lib/services/helper/helper.service.ts +++ b/projects/questionset-editor-library/src/lib/services/helper/helper.service.ts @@ -131,18 +131,6 @@ export class HelperService { } } - getAllUser(userSearchBody) { - const req = { - url: _.get(this.configService.urlConFig, 'URLS.USER.SEARCH'), - param: {fields: 'orgName'}, - data: { - request: userSearchBody.request - } - }; - - return this.publicDataService.post(req); - } - addDepthToHierarchy(arr, depth = 0, index = 0) { if (arr && index < arr.length) { _.forEach(arr, child => { diff --git a/projects/questionset-editor-library/src/lib/services/player/player.service.spec.ts b/projects/questionset-editor-library/src/lib/services/player/player.service.spec.ts index 03ab2a570..cfce26d0b 100644 --- a/projects/questionset-editor-library/src/lib/services/player/player.service.spec.ts +++ b/projects/questionset-editor-library/src/lib/services/player/player.service.spec.ts @@ -6,40 +6,12 @@ import { ConfigService } from '../../services/config/config.service'; import * as urlConfig from '../../services/config/url.config.json'; import * as labelConfig from '../../services/config/label.config.json'; import * as categoryConfig from '../../services/config/category.config.json'; - +import * as playerConfig from '../../services/config/player.config.json'; const configStub = { urlConFig: (urlConfig as any).default, labelConfig: (labelConfig as any).default, categoryConfig: (categoryConfig as any).default, - playerConfig: { - playerConfig: { - context: { - contentId: '', - sid: '', - uid: '', - timeDiff: '', - contextRollup: '', - channel: '', - did: '', - pdata: { - ver: '' - }, - dims: '', - tags: {}, - app: {}, - cdata: '' - }, - metadata: {}, - data: {}, - config: { - enableTelemetryValidation: false, - previewCdnUrl: '' - } - }, - MIME_TYPE: { - ecmlContent: {} - } - } + playerConfig: (playerConfig as any).default, }; const mockEditorService = { @@ -88,18 +60,4 @@ describe('PlayerService', () => { expect(result.metadata).toBeTruthy(); expect(result.data).toBeTruthy(); }) - - it('#getPlayerConfig() should return player config', () => { - const service: PlayerService = TestBed.inject(PlayerService); - const contentDetails = { - contentId: 'do_123', - courseId: 'do_1234', - batchId: 'do_12345', - contentData: {}, - body: {}, - mimeType: 'image/png' - }; - const result = service.getPlayerConfig(contentDetails); - expect(result).toBeTruthy(); - }) }); diff --git a/projects/questionset-editor-library/src/lib/services/player/player.service.ts b/projects/questionset-editor-library/src/lib/services/player/player.service.ts index 90a9118ef..a5b468733 100644 --- a/projects/questionset-editor-library/src/lib/services/player/player.service.ts +++ b/projects/questionset-editor-library/src/lib/services/player/player.service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@angular/core'; import * as _ from 'lodash-es'; -import { ConfigService } from '../../services/config/config.service'; import { EditorService } from '../../services/editor/editor.service'; interface PlayerConfig { @@ -21,7 +20,7 @@ export class PlayerService { showReplay: true, showExit: false, }; - constructor(private editorService: EditorService, private configService: ConfigService) { } + constructor(private editorService: EditorService) { } /** * returns QUML player config details. @@ -35,65 +34,4 @@ export class PlayerService { configuration.data = {}; return configuration; } - - /** - * returns player config details. - */ - getPlayerConfig(contentDetails): PlayerConfig { - const configuration: any = _.cloneDeep(_.get(this.configService.playerConfig, 'playerConfig')); - configuration.context.contentId = contentDetails.contentId; - configuration.context.sid = _.get(this.editorService.editorConfig, 'context.sid'); - configuration.context.uid = _.get(this.editorService.editorConfig, 'context.uid'); - configuration.context.timeDiff = _.get(this.editorService.editorConfig, 'context.timeDiff'); - configuration.context.contextRollup = _.get(this.editorService.editorConfig, 'context.contextRollup'); - // this.getRollUpData(this.userService.userProfile.hashTagIds); - configuration.context.channel = _.get(this.editorService.editorConfig, 'context.channel'); - // const deviceId = (document.getElementById('buildNumber')); - // configuration.context.pdata.ver = buildNumber && buildNumber.value ? - // buildNumber.value.slice(0, buildNumber.value.lastIndexOf('.')) : '1.0'; - configuration.context.pdata.ver = 1.0; - if (_.isUndefined(contentDetails.courseId)) { - configuration.context.dims = ''; - // this.userService.dims; - } else { - const cloneDims = []; - // _.cloneDeep(this.userService.dims) || []; - cloneDims.push(contentDetails.courseId); - if (contentDetails.batchId) { - cloneDims.push(contentDetails.batchId); - } - configuration.context.dims = cloneDims; - } - const tags = []; - _.forEach(_.get(this.editorService.editorConfig, 'user'), (org) => { - if (org.hashTagId) { - tags.push(org.hashTagId); - } - }); - configuration.context.tags = tags; - configuration.context.app = [_.get(this.editorService.editorConfig, 'context.channel')]; - if (contentDetails.courseId) { - configuration.context.cdata = [{ - id: contentDetails.courseId, - type: 'course' - }]; - if (contentDetails.batchId) { - configuration.context.cdata.push({ type: 'batch', - id: contentDetails.batchId} ); - } - } - configuration.context.pdata.id = _.get(this.editorService.editorConfig, 'context.pdata.id'); - configuration.metadata = contentDetails.contentData; - configuration.data = contentDetails.contentData.mimeType !== this.configService.playerConfig.MIME_TYPE.ecmlContent ? - {} : contentDetails.contentData.body; - configuration.config.enableTelemetryValidation = false, - // environment.enableTelemetryValidation; // telemetry validation - configuration.config.previewCdnUrl = undefined; - // this.previewCdnUrl = (document.getElementById('previewCdnUrl')) - // ? (document.getElementById('previewCdnUrl')).value : undefined; - return configuration; - } - } diff --git a/projects/questionset-editor-library/src/lib/services/question/question.service.spec.ts b/projects/questionset-editor-library/src/lib/services/question/question.service.spec.ts index 21004c163..f0fed2153 100644 --- a/projects/questionset-editor-library/src/lib/services/question/question.service.spec.ts +++ b/projects/questionset-editor-library/src/lib/services/question/question.service.spec.ts @@ -10,7 +10,7 @@ import { PublicDataService } from '../public-data/public-data.service'; import { EditorService } from '../../services/editor/editor.service'; import { editorConfig } from './../../components/editor/editor.component.spec.data'; import { mockRes } from "./question.service.spec.data"; - +declare const SunbirdFileUploadLib: any; describe('QuestionService', () => { let questionService: QuestionService; @@ -51,18 +51,10 @@ describe('QuestionService', () => { }); }); - it('#updateHierarchyQuestionCreate() it should update hierarchy on question create', () => { - const hierarchyBody = {}; - spyOn(publicDataService, 'patch').and.returnValue(of(mockRes.serverResponse)); - questionService.updateHierarchyQuestionCreate(hierarchyBody).subscribe(data => { - expect(data.responseCode).toEqual('OK'); - }); - }); - - it('#updateHierarchyQuestionUpdate() it should update hierarchy on question update', () => { + it('#updateQuestionHierarchy() it should update hierarchy on question create and update', () => { const hierarchyBody = {}; spyOn(publicDataService, 'patch').and.returnValue(of(mockRes.serverResponse)); - questionService.updateHierarchyQuestionUpdate(hierarchyBody).subscribe(data => { + questionService.updateQuestionHierarchy(hierarchyBody).subscribe(data => { expect(data.responseCode).toEqual('OK'); }); }); @@ -123,4 +115,70 @@ describe('QuestionService', () => { }); }); + it('#uploadToBlob should handle error during upload', (done) => { + const mockSignedURL = 'mock-signed-url'; + const mockFile = new File(['mock content'], 'mock.png', { type: 'image/png' }); + const mockCSP = 'azure'; + + spyOn(SunbirdFileUploadLib.FileUploader.prototype, 'upload') + .and.returnValue({ + on: (eventName, callback) => { + if (eventName === 'error') { + callback('mock-error'); + } + return this; + }, + }); + + questionService.uploadToBlob(mockSignedURL, mockFile, mockCSP).subscribe( + (response) => { + fail('Should not have succeeded'); + done(); + }, + (error) => { + expect(error).toBe('mock-error'); + done(); + } + ); + }); + + it('should handle upload error', (done) => { + const signedURL = 'mockSignedURL'; + const file = 'mockFile'; + const csp = 'mockCSP'; + const expectedError = 'Upload error'; + + // Mock the FileUploader behavior + spyOn(SunbirdFileUploadLib.FileUploader.prototype, 'upload').and.callFake(() => { + return { + on: (eventName, callback) => { + if (eventName === 'completed') { + callback('mock-completed-response'); + } + return { + on: (eventName, callback) => { + if (eventName === 'completed') { + callback('mock-completed-response'); + } + return { + on: () => {}, + }; + } + } + } + } + }); + + questionService.uploadToBlob(signedURL, file, csp).subscribe( + (response) => { + expect(response).toBe('mock-completed-response'); + done(); + }, + (error) => { + fail('Should not have encountered an error'); + done(); + } + ); + + }); }); diff --git a/projects/questionset-editor-library/src/lib/services/question/question.service.ts b/projects/questionset-editor-library/src/lib/services/question/question.service.ts index 96afce790..543913f33 100644 --- a/projects/questionset-editor-library/src/lib/services/question/question.service.ts +++ b/projects/questionset-editor-library/src/lib/services/question/question.service.ts @@ -8,6 +8,8 @@ import { v4 as uuidv4 } from 'uuid'; import { HttpClient } from '@angular/common/http'; import { ConfigService } from '../config/config.service'; import { EditorService } from '../editor/editor.service'; +declare const SunbirdFileUploadLib: any; + @Injectable({ providedIn: 'root' }) @@ -54,20 +56,7 @@ export class QuestionService { return this.publicDataService.patch(req); } - updateHierarchyQuestionCreate(hierarchyBody): Observable { - const requestObj = { - data: hierarchyBody - }; - const req = { - url: this.configService.urlConFig.URLS[this.editorService.editorConfig.config.objectType].HIERARCHY_UPDATE, - data: { - request: requestObj - } - }; - return this.publicDataService.patch(req); - } - - updateHierarchyQuestionUpdate(hierarchyBody): Observable { + updateQuestionHierarchy(hierarchyBody): Observable { const requestObj = { data: hierarchyBody }; @@ -137,6 +126,19 @@ export class QuestionService { return this.publicDataService.post(reqParam); } + uploadToBlob(signedURL, file, csp) { + return new Observable((observer) => { + const uploaderLib = new SunbirdFileUploadLib.FileUploader(); + uploaderLib.upload({ url: signedURL, file, csp }) + .on("error", (error) => { + observer.error(error); + }).on("completed", (completed) => { + observer.next(completed); + observer.complete(); + }) + }); + } + getVideo(videoId) { const reqParam = { url: `${this.configService.urlConFig.URLS.ASSET.READ}${videoId}` diff --git a/projects/questionset-editor-library/src/lib/services/tree/tree.service.spec.data.ts b/projects/questionset-editor-library/src/lib/services/tree/tree.service.spec.data.ts index c2bb9a752..b4502805d 100644 --- a/projects/questionset-editor-library/src/lib/services/tree/tree.service.spec.data.ts +++ b/projects/questionset-editor-library/src/lib/services/tree/tree.service.spec.data.ts @@ -91,9 +91,7 @@ export const treeNode = { }, "shuffle": true, "board": "CBSE", - "instructions": { - "default": "New instructions " - }, + "instructions": "New instructions ", "level": 1, "maxTime": "00:06:00", "warningTime": "00:00:10", diff --git a/projects/questionset-editor-library/src/lib/services/tree/tree.service.ts b/projects/questionset-editor-library/src/lib/services/tree/tree.service.ts index 2792e1651..fdcee4f88 100755 --- a/projects/questionset-editor-library/src/lib/services/tree/tree.service.ts +++ b/projects/questionset-editor-library/src/lib/services/tree/tree.service.ts @@ -242,7 +242,7 @@ export class TreeService { this.treeCache.nodesModified[nodeId].metadata = _.assign(this.treeCache.nodesModified[nodeId].metadata, _.omit(metadata, 'objectType')); } else { this.treeCache.nodesModified[nodeId] = { - root: activeNode && activeNode.root ? true : false, + root: activeNode?.root ? true : false, objectType: metadata.objectType, metadata: { ..._.omit(metadata, ['objectType']) }, ...(nodeId.includes('do_') ? { isNew: false } : { isNew: true }) diff --git a/proxy.conf.json b/proxy.conf.json index 74dcf6d59..5037a91bd 100644 --- a/proxy.conf.json +++ b/proxy.conf.json @@ -1,4 +1,10 @@ { + "/action/asset/v1/upload": { + "target": "http://localhost:3000", + "secure": false, + "logLevel": "debug", + "changeOrigin": true + }, "/action/program/v1": { "target": "http://localhost:3000", "secure": false, diff --git a/server.js b/server.js index 34e2dcd86..3cb2568cd 100644 --- a/server.js +++ b/server.js @@ -4,10 +4,12 @@ var express = require('express'), proxy = require('express-http-proxy'), urlHelper = require('url'); const latexService = require('./latexService.js') +const dotenv = require('dotenv'); +dotenv.config(); -const BASE_URL = "dev.inquiry.sunbird.org"; -const API_AUTH_TOKEN = ""; -const USER_TOKEN = ""; +const BASE_URL = process.env.BASE_URL || "dev.inquiry.sunbird.org"; +const API_AUTH_TOKEN = process.env.AUTH_API_TOKEN; +const USER_TOKEN = process.env.USER_API_TOKEN; const PORTAL_COOKIES= "" var app = express(); @@ -16,6 +18,27 @@ app.use(express.json()) app.get("/latex/convert", latexService.convert) app.post("/latex/convert", bodyParser.json({ limit: '1mb' }), latexService.convert); app.use(express.static(__dirname + '/web-component-examples/vanilla-js')); + +const decoratePublicRequestHeaders = function () { + return function (proxyReqOpts, srcReq) { + proxyReqOpts.headers['authorization'] = `Bearer ${API_AUTH_TOKEN}`; + proxyReqOpts.headers['x-authenticated-user-token'] = USER_TOKEN; + return proxyReqOpts; + } +}; + +app.post(["/action/asset/v1/upload/*"], proxy(BASE_URL, { + https: true, + parseReqBody: false, + proxyReqPathResolver: function (req) { + console.log('proxyReqPathResolver ::', req.originalUrl); + let originalUrl = req.originalUrl.replace("/action/", "/api/"); + return urlHelper.parse(originalUrl).path; + }, + proxyReqOptDecorator: decoratePublicRequestHeaders() + }) +); + app.all(['/api/framework/v1/read/*', '/learner/framework/v1/read/*', '/api/channel/v1/read/*', @@ -25,15 +48,7 @@ app.all(['/api/framework/v1/read/*', console.log('proxyReqPathResolver ', urlHelper.parse(req.url).path); return urlHelper.parse(req.url).path; }, - proxyReqOptDecorator: function(proxyReqOpts, srcReq) { - console.log('proxyReqOptDecorator 2') - // you can update headers - proxyReqOpts.headers['Content-Type'] = 'application/json'; - proxyReqOpts.headers['user-id'] = 'content-editor'; - proxyReqOpts.headers['Cookie'] = PORTAL_COOKIES; - proxyReqOpts.headers['authorization'] = `Bearer ${API_AUTH_TOKEN}`; - return proxyReqOpts; - } + proxyReqOptDecorator: decoratePublicRequestHeaders() })); app.use(['/action/questionset/v2/*', '/action/question/v2/*', @@ -48,16 +63,7 @@ app.use(['/action/questionset/v2/*', console.log('proxyReqPathResolver questionset', originalUrl, require('url').parse(originalUrl).path); return require('url').parse(originalUrl).path; }, - proxyReqOptDecorator: function (proxyReqOpts, srcReq) { - console.log('proxyReqOptDecorator 3') - // you can update headers - proxyReqOpts.headers['Content-Type'] = 'application/json'; - proxyReqOpts.headers['user-id'] = 'content-editor'; - proxyReqOpts.headers['Cookie'] = PORTAL_COOKIES; - proxyReqOpts.headers['authorization'] = `Bearer ${API_AUTH_TOKEN}`; - proxyReqOpts.headers['x-authenticated-user-token'] = USER_TOKEN; - return proxyReqOpts; - } + proxyReqOptDecorator: decoratePublicRequestHeaders() })); app.use(['/action/composite/v3/search' @@ -69,16 +75,7 @@ app.use(['/action/composite/v3/search' console.log('proxyReqPathResolver questionset', originalUrl, require('url').parse(originalUrl).path); return require('url').parse(originalUrl).path; }, - proxyReqOptDecorator: function (proxyReqOpts, srcReq) { - console.log('proxyReqOptDecorator 3') - // you can update headers - proxyReqOpts.headers['Content-Type'] = 'application/json'; - proxyReqOpts.headers['user-id'] = 'content-editor'; - proxyReqOpts.headers['Cookie'] = PORTAL_COOKIES; - proxyReqOpts.headers['authorization'] = `Bearer ${API_AUTH_TOKEN}`; - proxyReqOpts.headers['x-authenticated-user-token'] = USER_TOKEN; - return proxyReqOpts; - } + proxyReqOptDecorator: decoratePublicRequestHeaders() })); app.use(['/action/program/v1/*', @@ -92,16 +89,7 @@ app.use(['/action/program/v1/*', console.log('proxyReqPathResolver questionset', originalUrl, require('url').parse(originalUrl).path); return require('url').parse(originalUrl).path; }, - proxyReqOptDecorator: function (proxyReqOpts, srcReq) { - console.log('proxyReqOptDecorator 3') - // you can update headers - proxyReqOpts.headers['Content-Type'] = 'application/json'; - proxyReqOpts.headers['user-id'] = 'content-editor'; - proxyReqOpts.headers['Cookie'] = PORTAL_COOKIES; - proxyReqOpts.headers['authorization'] = `Bearer ${API_AUTH_TOKEN}`; - proxyReqOpts.headers['x-authenticated-user-token'] = USER_TOKEN; - return proxyReqOpts; - } + proxyReqOptDecorator: decoratePublicRequestHeaders() })); app.use(['/api','/assets','/action'], proxy(BASE_URL, { @@ -111,14 +99,7 @@ app.use(['/api','/assets','/action'], proxy(BASE_URL, { console.log('proxyReqPathResolver ', urlHelper.parse(req.url).path); return urlHelper.parse(req.url).path; }, - proxyReqOptDecorator: function(proxyReqOpts, srcReq) { - console.log('proxyReqOptDecorator 4') - // you can update headers - proxyReqOpts.headers['Content-Type'] = 'application/json'; - proxyReqOpts.headers['user-id'] = 'content-editor'; - proxyReqOpts.headers['Cookie'] = PORTAL_COOKIES; - return proxyReqOpts; - } + proxyReqOptDecorator: decoratePublicRequestHeaders() })); app.use(['/action/content/*'], proxy(BASE_URL, { @@ -128,28 +109,12 @@ app.use(['/action/content/*'], proxy(BASE_URL, { console.log('proxyReqPathResolver questionset', originalUrl, require('url').parse(originalUrl).path); return require('url').parse(originalUrl).path; }, - proxyReqOptDecorator: function (proxyReqOpts, srcReq) { - console.log('proxyReqOptDecorator 1') - // you can update headers - proxyReqOpts.headers['Content-Type'] = 'application/json'; - proxyReqOpts.headers['user-id'] = 'content-editor'; - proxyReqOpts.headers['Cookie'] = PORTAL_COOKIES; - return proxyReqOpts; - } + proxyReqOptDecorator: decoratePublicRequestHeaders() })); -app.use(['/content/preview/*', '/content-plugins/*', '/assets/public/*'], proxy(BASE_URL, { +app.use(['/assets/public/*'], proxy(BASE_URL, { https: true, proxyReqPathResolver: function(req) { return require('url').parse(`https://${BASE_URL}` + req.originalUrl).path - }, - proxyReqOptDecorator: function(proxyReqOpts, srcReq) { - console.log('proxyReqOptDecorator 5') - // you can update headers - proxyReqOpts.headers['Content-Type'] = 'application/json'; - proxyReqOpts.headers['user-id'] = 'content-editor'; - proxyReqOpts.headers['Cookie'] = PORTAL_COOKIES; - proxyReqOpts.headers['authorization'] = `Bearer ${API_AUTH_TOKEN}`; - return proxyReqOpts; } })); http.createServer(app).listen(app.get('port'), 3000); \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties index 9f8441c8f..30752dd1d 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,7 +1,7 @@ sonar.projectName=sunbird-questionset-editor sonar.language=ts sonar.sources=projects/questionset-editor-library/src -sonar.exclusions=projects/questionset-editor-library/src/lib/assets/**,projects/questionset-editor-library/src/lib/**/*.spec.ts,projects/questionset-editor-library/src/lib/**/*.spec.data.ts,projects/questionset-editor-library/src/lib/**/*.module.ts +sonar.exclusions=projects/questionset-editor-library/src/lib/assets/**,projects/questionset-editor-library/src/lib/**/*.spec.ts,projects/questionset-editor-library/src/lib/**/*.spec.data.ts,projects/questionset-editor-library/src/lib/**/*.module.ts,projects/questionset-editor-library/src/lib/services/config/label.config.json sonar.javascript.lcov.reportPaths=projects/questionset-editor-library/coverage/lcov.info sonar.projectKey=Sunbird-inQuiry_editor sonar.host.url=https://sonarcloud.io diff --git a/src/app/data.ts b/src/app/data.ts index e49cd1c67..a6dbfed69 100644 --- a/src/app/data.ts +++ b/src/app/data.ts @@ -9,7 +9,7 @@ export const questionSetEditorConfig = { lastName: 'User', orgIds: ['01309282781705830427'] }, - identifier: 'do_21385722788845158418', + identifier: 'do_2138624108252774401216', authToken: ' ', sid: 'iYO2K6dOSdA0rwq7NeT1TDzS-dbqduvV', did: '7e85b4967aebd6704ba1f604f20056b6', @@ -43,13 +43,13 @@ export const questionSetEditorConfig = { l1: 'do_113140468925825024117', l2: 'do_113140468926914560125' }, - host: '', + host: 'https://dev.inquiry.sunbird.org', defaultLicense: 'CC BY 4.0', endpoint: '/data/v3/telemetry', env: 'questionset_editor', framework: 'inquiry_k-12', cloudStorageUrls: ['https://s3.ap-south-1.amazonaws.com/ekstep-public-qa/', 'https://ekstep-public-qa.s3-ap-south-1.amazonaws.com/', - 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/'], + 'https://sunbirddev.blob.core.windows.net/sunbird-content-dev/', 'https://sunbirddevbbpublic.blob.core.windows.net/sunbird-content-staging/'], board: 'CBSE', medium: ['English'], gradeLevel: ['Class 1'], @@ -113,6 +113,12 @@ export const questionSetEditorConfig = { }, correctionComments: false, sourcingResourceStatus: true, + cloudStorage: { + provider: 'azure', + presigned_headers: { + 'x-ms-blob-type': 'BlockBlob' + } + } }, config: { mode: 'edit', // edit / review / read / sourcingReview // orgReview @@ -128,7 +134,6 @@ export const questionSetEditorConfig = { primaryCategory: 'Practice Question Set', isRoot: true, iconClass: 'fa fa-book', - publicStorageAccount: 'https://dockstorage.blob.core.windows.net/', hideSubmitForReviewBtn: false, children: { Question: [ @@ -444,7 +449,6 @@ export const observationEditorConfig = { primaryCategory: 'Observation', isRoot: true, iconClass: 'fa fa-book', - publicStorageAccount: 'https://dockstorage.blob.core.windows.net/', children: { Question: [ 'Multiple Choice Question', @@ -636,7 +640,6 @@ export const surveyEditorConfig = { isRoot: true, iconClass: 'fa fa-book', enablePagination: true, - publicStorageAccount: 'https://dockstorage.blob.core.windows.net/', children: { Question: [ 'Multiple Choice Question', @@ -829,7 +832,6 @@ export const observationRubricsEditorConfig = { isRoot: true, iconClass: 'fa fa-book', enablePagination:true, - publicStorageAccount: 'https://dockstorage.blob.core.windows.net/', children: { Question: [ 'Multiple Choice Question', diff --git a/web-component-examples/vanilla-js/3rdpartylicenses.txt b/web-component-examples/vanilla-js/3rdpartylicenses.txt index 56c04f432..8bd345943 100644 --- a/web-component-examples/vanilla-js/3rdpartylicenses.txt +++ b/web-component-examples/vanilla-js/3rdpartylicenses.txt @@ -315,32 +315,6 @@ For more information, please see http://creativecommons.org/publicdomain/zero/1.0/. -export-to-csv -MIT -MIT License - -Copyright for portions of project export-to-csv are held by Javier Telio , 2016 as part of project Angular2Csv. -All other copyright for project export-to-csv are held by Alex Caza , 2017. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - extend MIT The MIT License (MIT) diff --git a/web-component-examples/vanilla-js/index.html b/web-component-examples/vanilla-js/index.html index 48e35c8c7..19b447bb2 100644 --- a/web-component-examples/vanilla-js/index.html +++ b/web-component-examples/vanilla-js/index.html @@ -23,181 +23,189 @@