diff --git a/backend/package-lock.json b/backend/package-lock.json index f7f18f75..006bf2e6 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -47,7 +47,8 @@ "strip-ansi": "^6.0.0", "uuid": "^8.3.2", "winston": "^3.5.0", - "winston-daily-rotate-file": "^4.5.0" + "winston-daily-rotate-file": "^4.5.0", + "yup": "^1.4.0" }, "devDependencies": { "@babel/cli": "^7.16.8", @@ -5286,6 +5287,11 @@ "node": ">=6.1" } }, + "node_modules/property-expr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", + "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -6152,6 +6158,11 @@ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", "dev": true }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6181,6 +6192,11 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" + }, "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -6659,6 +6675,28 @@ "engines": { "node": ">=10" } + }, + "node_modules/yup": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz", + "integrity": "sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==", + "dependencies": { + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/backend/package.json b/backend/package.json index 8b3b21c0..4e5b3dc0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -54,7 +54,8 @@ "strip-ansi": "^6.0.0", "uuid": "^8.3.2", "winston": "^3.5.0", - "winston-daily-rotate-file": "^4.5.0" + "winston-daily-rotate-file": "^4.5.0", + "yup": "^1.4.0" }, "devDependencies": { "@babel/cli": "^7.16.8", diff --git a/backend/src/components/sdc/sdc.js b/backend/src/components/sdc/sdc.js index dbc711ab..42f07ce1 100644 --- a/backend/src/components/sdc/sdc.js +++ b/backend/src/components/sdc/sdc.js @@ -161,7 +161,7 @@ async function getSDCSchoolCollectionStudentPaginated(req, res) { if(req.query.assignedStudentID) { search.push({ condition: null, - searchCriteriaList: [{ key: 'assignedStudentId', value: req.query.assignedStudentID, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.UUID }] + searchCriteriaList: [{ key: 'assignedStudentId', value: req.query.assignedStudentID.join(','), operation: FILTER_OPERATION.IN_NOT_DISTINCT, valueType: VALUE_TYPE.UUID }] }); } @@ -888,6 +888,17 @@ async function downloadSdcReport(req, res) { } } + +async function moveSld (req, res) { + try { + await postData(`${config.get('sdc:schoolCollectionStudentURL')}/move-sld`, req.body); + return res.status(HttpStatus.OK).json(''); + } catch (e) { + logApiError(e, 'Error attempting to move sld record(s)'); + return errorResponse(res); + } +} + async function getDistrictHeadcounts(req, res) { try { const params = { @@ -945,5 +956,6 @@ module.exports = { getSdcDistrictCollections, downloadSdcReport, updateBandCode, + moveSld, getDistrictHeadcounts }; diff --git a/backend/src/components/validator.js b/backend/src/components/validator.js new file mode 100644 index 00000000..abb59d2f --- /dev/null +++ b/backend/src/components/validator.js @@ -0,0 +1,16 @@ +const validate = (schema) => async (req, res, next) => { + try { + await schema.validate({ + body: req.body, + query: req.query, + params: req.params, + }, { + stripUnknown: false + }); + next(); + } catch (e) { + return res.status(400).send(e.message); + } +}; + +module.exports = validate; diff --git a/backend/src/routes/sdc.js b/backend/src/routes/sdc.js index 9895e844..66a39152 100644 --- a/backend/src/routes/sdc.js +++ b/backend/src/routes/sdc.js @@ -8,7 +8,7 @@ const { getSnapshotFundingDataForSchool, getAllCollectionsForSchool, getActiveCo getIndySdcSchoolCollectionMonitoringByCollectionId, unsubmitSdcDistrictCollection, unsubmitSdcSchoolCollection, getInDistrictDuplicates, getSDCSchoolCollectionStudentPaginatedSlice, getSDCSchoolCollectionStudentPaginated, getSDCSchoolCollectionStudentDetail, updateStudentPEN, checkDuplicatesInCollection, updateAndValidateSdcSchoolCollectionStudent, resolveDuplicates, postProvincialDuplicates, resolveRemainingDuplicates, getInFlightDistrictProvincialDuplicates, getInFlightSchoolProvincialDuplicates, closeCollection, getCollectionPaginated, getSDCSchoolCollectionDetail, downloadSdcReport, - getCollectionByID, getSdcSchoolCollections, getSdcDistrictCollections, updateBandCode, getDistrictHeadcounts + getCollectionByID, getSdcSchoolCollections, getSdcDistrictCollections, updateBandCode, moveSld, getDistrictHeadcounts } = require('../components/sdc/sdc'); const {getCachedSDCData} = require('../components/sdc/sdc-cache'); const constants = require('../util/constants'); @@ -16,6 +16,8 @@ const {getCodes} = require('../components/utils'); const PERMISSION = perm.PERMISSION; const permUtils = require('../components/permissionUtils'); const auth = require('../components/auth'); +const validate = require('../components/validator'); +const { moveSldSchema } = require('../validations/sdc'); //cached code table calls router.get('/band-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCachedSDCData(constants.CACHE_KEYS.SDC_BAND_CODES, 'sdc:bandCodesURL')); @@ -63,6 +65,7 @@ router.post('/sdcSchoolCollectionStudent/:sdcSchoolCollectionStudentID/update-pe //update student router.post('/sdcSchoolCollectionStudent', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, updateAndValidateSdcSchoolCollectionStudent); +router.post('/move-sld', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), validate(moveSldSchema), extendSession, moveSld); //district collection router.post('/sdcDistrictCollection/:sdcDistrictCollectionID/unsubmit', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, permUtils.isValidUUIDParam('sdcDistrictCollectionID'), unsubmitSdcDistrictCollection); diff --git a/backend/src/util/constants.js b/backend/src/util/constants.js index 5a626ada..5987b9cf 100644 --- a/backend/src/util/constants.js +++ b/backend/src/util/constants.js @@ -106,6 +106,10 @@ const FILTER_OPERATION = Object.freeze( * Ends with filter operation. */ ENDS_WITH: 'ends_with', + /** + * In operation that does not include the DISTINCT condition + */ + IN_NOT_DISTINCT: 'in_not_distinct' } ); const CONDITION = Object.freeze( diff --git a/backend/src/validations/sdc.js b/backend/src/validations/sdc.js new file mode 100644 index 00000000..b71ca93c --- /dev/null +++ b/backend/src/validations/sdc.js @@ -0,0 +1,14 @@ +const { object, string, array } = require('yup'); + +const moveSldSchema = object({ + body: object({ + toStudentPen: string().nonNullable(), + sdcSchoolCollectionIdsToUpdate: array().of(string().nonNullable()) + }).noUnknown(), + params: object().noUnknown(), + query: object().noUnknown(), +}).noUnknown(); + +module.exports = { + moveSldSchema +}; diff --git a/frontend/src/components/CompareStudents.vue b/frontend/src/components/CompareStudents.vue index 0c87738a..8ff53ebb 100644 --- a/frontend/src/components/CompareStudents.vue +++ b/frontend/src/components/CompareStudents.vue @@ -5,35 +5,35 @@ id="compareClearBtn" text="Clear" secondary - @click-action="[cancel, clearError]" + @click-action="[cancel(), clearError()]" /> diff --git a/frontend/src/components/common/CompareDemographicModal.vue b/frontend/src/components/common/CompareDemographicModal.vue index deccff81..b6c413c5 100644 --- a/frontend/src/components/common/CompareDemographicModal.vue +++ b/frontend/src/components/common/CompareDemographicModal.vue @@ -17,10 +17,11 @@ - - - @@ -113,15 +71,11 @@ import CompareDemographicsCommon from './CompareDemographicsCommon.vue'; import {deepCloneObject} from '@/utils/common'; import alertMixin from '@/mixins/alertMixin'; import staleStudentRecordMixin from '@/mixins/staleStudentRecordMixin'; -import CompareDemographicsCommonV2 from '@/components/common/CompareDemographicsCommonV2.vue'; -import {mapState} from 'pinia'; -import {appStore} from '@/store/modules/app'; export default { name: 'CompareDemographicModal', components: { CompareDemographicsCommon, - CompareDemographicsCommonV2, PrimaryButton, TertiaryButton }, @@ -140,7 +94,7 @@ export default { required: true } }, - emits: ['update:selectedRecords','closeCompare'], + emits: ['update:selectedRecords','closeCompare', 'refresh-sld-data'], data() { return { compareModalOpen: false, @@ -148,7 +102,6 @@ export default { }; }, computed: { - ...mapState(appStore, ['config']), studentRecords: { get: function() { return this.selectedRecords; @@ -156,9 +109,6 @@ export default { set: function(value) { this.$emit('update:selectedRecords', value); } - }, - useEdxRelease() { - return !this.config.DISABLE_SDC_FUNCTIONALITY; } }, created() { diff --git a/frontend/src/components/common/CompareDemographicsCommon.vue b/frontend/src/components/common/CompareDemographicsCommon.vue index 3d100bea..2e491243 100644 --- a/frontend/src/components/common/CompareDemographicsCommon.vue +++ b/frontend/src/components/common/CompareDemographicsCommon.vue @@ -1,1094 +1,56 @@