diff --git a/questiongeneration/index.js b/questiongeneration/index.js new file mode 100644 index 00000000..9106e515 --- /dev/null +++ b/questiongeneration/index.js @@ -0,0 +1,23 @@ +// Imports (express syntax) +const express = require('express'); +const cors = require('cors'); +// Routes: +const questionRoutes = require('./routes/question'); + +// App definition and +const app = express(); +const port = 8010; + +// Middlewares added to the application +app.use(cors()); +app.use(express.json()); + +// Routes middlewares to be used +app.use('/questions', questionRoutes); + +// Start the service +const server = app.listen(port, () => { + console.log(`Question Generation Service listening at http://localhost:${port}`); +}); + +module.exports = server \ No newline at end of file diff --git a/questiongeneration/package-lock.json b/questiongeneration/package-lock.json index a0d8cb2e..048a74b8 100644 --- a/questiongeneration/package-lock.json +++ b/questiongeneration/package-lock.json @@ -12,9 +12,31 @@ "axios": "^1.6.5", "cors": "^2.8.5", "express": "^4.18.2", + "mongoose": "^8.2.0", "node-fetch": "^3.3.2" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", + "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", + "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -70,6 +92,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bson": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", + "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -512,6 +542,14 @@ "node": ">= 0.10" } }, + "node_modules/kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -520,6 +558,11 @@ "node": ">= 0.6" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -563,6 +606,126 @@ "node": ">= 0.6" } }, + "node_modules/mongodb": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", + "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.0.tgz", + "integrity": "sha512-la93n6zCYRbPS+c5N9oTDAktvREy5OT9OCljp1Tah0y3+p8UPMTAoabWaLZMdzYruOtF9/9GRf6MasaZjiZP1A==", + "dependencies": { + "bson": "^6.2.0", + "kareem": "2.5.1", + "mongodb": "6.3.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -668,6 +831,14 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -808,6 +979,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -824,6 +1008,17 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -867,9 +1062,50 @@ "engines": { "node": ">= 8" } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } } }, "dependencies": { + "@mongodb-js/saslprep": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", + "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==", + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "@types/whatwg-url": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", + "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", + "requires": { + "@types/webidl-conversions": "*" + } + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -918,6 +1154,11 @@ "unpipe": "1.0.0" } }, + "bson": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", + "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==" + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1221,11 +1462,21 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, + "kareem": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", + "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==" + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -1254,6 +1505,74 @@ "mime-db": "1.52.0" } }, + "mongodb": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", + "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "requires": { + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^6.2.0", + "mongodb-connection-string-url": "^3.0.0" + } + }, + "mongodb-connection-string-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz", + "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==", + "requires": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "mongoose": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.0.tgz", + "integrity": "sha512-la93n6zCYRbPS+c5N9oTDAktvREy5OT9OCljp1Tah0y3+p8UPMTAoabWaLZMdzYruOtF9/9GRf6MasaZjiZP1A==", + "requires": { + "bson": "^6.2.0", + "kareem": "2.5.1", + "mongodb": "6.3.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "16.0.1" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" + }, + "mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "requires": { + "debug": "4.x" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1321,6 +1640,11 @@ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, "qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -1422,6 +1746,19 @@ "object-inspect": "^1.13.1" } }, + "sift": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz", + "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==" + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "requires": { + "memory-pager": "^1.0.2" + } + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1432,6 +1769,14 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, + "tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "requires": { + "punycode": "^2.3.0" + } + }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1460,6 +1805,20 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==" + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "requires": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + } } } } diff --git a/questiongeneration/package.json b/questiongeneration/package.json index 0915d7b8..29cbee9b 100644 --- a/questiongeneration/package.json +++ b/questiongeneration/package.json @@ -13,6 +13,7 @@ "axios": "^1.6.5", "cors": "^2.8.5", "express": "^4.18.2", + "mongoose": "^8.2.0", "node-fetch": "^3.3.2" } } diff --git a/questiongeneration/routes/question.js b/questiongeneration/routes/question.js new file mode 100644 index 00000000..5f019e94 --- /dev/null +++ b/questiongeneration/routes/question.js @@ -0,0 +1,98 @@ +const express = require('express'); +const router = express.Router(); +const dbService = require('../services/question-data-service'); +const { getRandomCity, getCityPopulation, getPopulationFromRandomCity, shuffleArray } = require('../utils/cityPopulation'); +const { db } = require('../services/question-data-model'); + +// Manejo de la ruta '/question' +router.get('/', async (_req, res) => { + try { + const questions = []; + + for (let i = 0; i < 5; i++) { + const [nombreCiudad, codigoQ] = await getRandomCity(); + const poblacion = await getCityPopulation(codigoQ); + + if (poblacion !== null) { + const questionText = `¿Cuál es la población de ${nombreCiudad.charAt(0).toUpperCase() + nombreCiudad.slice(1)}?`; + const correctAnswer = poblacion; + + // Promise devuelve un array de los resultados de todas las promesas + let options = await Promise.all([ + getPopulationFromRandomCity(), + getPopulationFromRandomCity(), + getPopulationFromRandomCity(), + ]); + + // Filtrar los elementos null y llamar a poblacionAleatoria para reemplazarlos + while (options.some(respuesta => respuesta === null)) { + options = await Promise.all( + options.map(async (respuesta) => { + if (respuesta === null) { + return await getPopulationFromRandomCity(); + } else { + return respuesta; + } + }) + ); + } + + options.push(correctAnswer); + + // Desordenar las opciones + const shuffledOptions = shuffleArray(options); + + // Crear el objeto de pregunta + const newQuestion = { + question: questionText, + options: shuffledOptions, + correctAnswer: correctAnswer, + category: "not defined yet" + }; + + questions.push(newQuestion); + dbService.addQuestion(newQuestion); + } + } + + res.json(questions); + + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Error al procesar la solicitud' }); + } +}); + +// Enter in this url to load sample questions to db: http://localhost:8010/questions/loadSampleData +router.get('/loadSampleData', async (_req, res) => { + dbService.addTestData(); +}); + +//Get random questions from db: http://localhost:8010/questions/getQuestionsFromDb/3 +router.get('/getQuestionsFromDb/:n', async(_req, res) => { + const n = parseInt(_req.params.n, 10); + + //Verify is n is a correct number + if (isNaN(n) || n <= 0) { + return res.status(400).json({ error: 'Parameter "n" must be > 0.' }); + } + + questions = await dbService.getRandomQuestions(n); + res.json(questions); +}); + +//Get random questions from db with category filter: http://localhost:8010/questions/getQuestionsFromDb/2/Geografía +router.get('/getQuestionsFromDb/:n/:category', async(_req, res) => { + const n = parseInt(_req.params.n, 10); + const category = _req.params.category; + + //Verify is n is a correct number + if (isNaN(n) || n <= 0) { + return res.status(400).json({ error: 'Parameter "n" must be > 0.' }); + } + + questions = await dbService.getRandomQuestionsByCategory(n, category); + res.json(questions); +}); + +module.exports = router; diff --git a/questiongeneration/services/question-data-model.js b/questiongeneration/services/question-data-model.js new file mode 100644 index 00000000..ebc210d3 --- /dev/null +++ b/questiongeneration/services/question-data-model.js @@ -0,0 +1,22 @@ +const mongoose = require('mongoose'); + +// Database schema +const questionSchema = new mongoose.Schema({ + question: String, + options: [String], + correctAnswer: String, + categories: [String] +}); + +//Auto generated id +questionSchema.add({ + id: { + type: mongoose.Schema.Types.ObjectId, + auto: true, + unique: true, + }, +}); + +const Question = mongoose.model('Question', questionSchema); + +module.exports = Question \ No newline at end of file diff --git a/questiongeneration/services/question-data-service.js b/questiongeneration/services/question-data-service.js new file mode 100644 index 00000000..0f24847b --- /dev/null +++ b/questiongeneration/services/question-data-service.js @@ -0,0 +1,122 @@ +const mongoose = require('mongoose'); +const Question = require('./question-data-model'); + +//TODO: QUESTION_DATABASE_URI has no value yet +const uri = process.env.QUESTION_DATABASE_URI || 'mongodb://localhost:27017/questionDB'; +mongoose.connect(uri); + +//TODO: when should db call disconnect? + +// Add question to database +async function addQuestion(questionData) { + try { + const newQuestion = new Question(questionData); + + //const savedQuestion = await newQuestion.save(); + await newQuestion.save(); + + //console.log('Added question: ', savedQuestion); + } catch (error) { + console.error('Error adding the question: ', error.message); + } + } + + +// Get random questions TODO: refactor to use common code with get questions by category +async function getRandomQuestions(n) { + try { + // Obtain total number of questions in database + const totalQuestions = await Question.countDocuments(); + + // Check if there are required number of questions + if (totalQuestions < n) { + console.log('Required ', n, ' questions and there are ', totalQuestions); + return; + } + + // Obtain n random indexes + const randomIndexes = []; + while (randomIndexes.length < n) { + const randomIndex = Math.floor(Math.random() * totalQuestions); + if (!randomIndexes.includes(randomIndex)) { + randomIndexes.push(randomIndex); + } + } + + // Obtain n random questions + const randomQuestions = await Question.find().limit(n).skip(randomIndexes[0]); + return randomQuestions; + //console.log('Random questions: ', randomQuestions); + } catch (error) { + console.error('Error obtaining random questions: ', error.message); + } + } + +// Obtaing random questions filtered by category +async function getRandomQuestionsByCategory(n, category) { + try { + // Obtain total number of questions with that category + const totalQuestions = await Question.countDocuments({ category }); + + // Check if there are required number of questions + if (totalQuestions < n) { + console.log('Required ', n, ' questions and there are ', totalQuestions); + return; + } + + // Obtain n random indexes + const randomIndexes = []; + while (randomIndexes.length < n) { + const randomIndex = Math.floor(Math.random() * totalQuestions); + if (!randomIndexes.includes(randomIndex)) { + randomIndexes.push(randomIndex); + } + } + + // Obtain n random questions with that category + const randomQuestions = await Question.find({ category }).limit(n).skip(randomIndexes[0]); + + return randomQuestions; + //console.log('Random questions: ', randomQuestions); + } catch (error) { + console.error('Error obtaining random questions (with category): ', error.message); + } + } + +// Test data +const testQuestions = [ + { + question: '¿Cuál es la capital de Francia?', + options: ['Berlín', 'París', 'Londres', 'Madrid'], + correctAnswer: 'París', + category: 'Geografía' + }, + { + question: '¿En qué año comenzó la Segunda Guerra Mundial?', + options: ['1935', '1938', '1939', '1942'], + correctAnswer: '1939', + category: 'Historia' + }, + { + question: '¿Cuál es el elemento más abundante en la corteza terrestre?', + options: ['Hierro', 'Oxígeno', 'Aluminio', 'Silicio'], + correctAnswer: 'Oxígeno', + category: 'Ciencia' + }, + ]; + + // Add test data to db + async function addTestData() { + try { + await Question.insertMany(testQuestions); + } catch (error) { + console.error('Error in sample data:', error); + } + } + + module.exports = { + addQuestion, + getRandomQuestions, + getRandomQuestionsByCategory, + addTestData +}; \ No newline at end of file diff --git a/questiongeneration/utils/cityPopulation.js b/questiongeneration/utils/cityPopulation.js new file mode 100644 index 00000000..83b17c30 --- /dev/null +++ b/questiongeneration/utils/cityPopulation.js @@ -0,0 +1,85 @@ +const axios = require('axios'); + +async function getRandomCity() { + const consultaSparql = ` + SELECT ?ciudad ?ciudadLabel ?codigoQ + WHERE { + ?ciudad wdt:P31 wd:Q515; + wdt:P17 wd:Q29; + rdfs:label ?ciudadLabel. + SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],es". } + } + `; + + const urlApiWikidata = 'https://query.wikidata.org/sparql'; + const headers = { + 'User-Agent': 'EjemploCiudades/1.0', + 'Accept': 'application/json', + }; + + try { + const response = await axios.get(`${urlApiWikidata}?query=${encodeURIComponent(consultaSparql)}&format=json`, { + headers: headers, + }); + + const datos = await response.data + const ciudades = datos.results.bindings; + + if (ciudades.length > 0) { + const ciudadAleatoria = ciudades[Math.floor(Math.random() * ciudades.length)]; + const nombreCiudad = ciudadAleatoria.ciudadLabel.value; + const codigoQ = ciudadAleatoria.ciudad.value.split('/').pop(); + return [nombreCiudad, codigoQ]; + } else { + return null; + } + + } catch (error) { + console.error(`Error al obtener ciudad aleatoria: ${error.message}`); + return null; + } +} + + +async function getCityPopulation(codigoCiudad) { + const url = `https://www.wikidata.org/w/api.php?action=wbgetclaims&format=json&entity=${codigoCiudad}&property=P1082`; + + try { + //const response = await fetch(url); + const response = await axios.get(url); + const data = await response.data; + + if (data.claims && data.claims.P1082 && data.claims.P1082[0] && data.claims.P1082[0].mainsnak && data.claims.P1082[0].mainsnak.datavalue && data.claims.P1082[0].mainsnak.datavalue.value && data.claims.P1082[0].mainsnak.datavalue.value.amount) { + const populationClaim = data.claims.P1082[0].mainsnak.datavalue.value.amount; + return parseInt(populationClaim); + } else { + return null; + } + } catch (error) { + console.error(`Error al obtener población: ${error.message}`); + return null; + } +} + +// Función para obtener la población de una ciudad aleatoria con reintentos +async function getPopulationFromRandomCity() { + const [nombreCiudad, codigoCiudad] = await getRandomCity(); + const poblacion = await getCityPopulation(codigoCiudad); + return poblacion; +} + +// Función para desordenar un array +function shuffleArray(array) { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; +} + +module.exports = { + getRandomCity, + getCityPopulation, + getPopulationFromRandomCity, + shuffleArray +};