diff --git a/backend/src/components/sdc/sdc.js b/backend/src/components/sdc/sdc.js index d7e96020d..95b01c63a 100644 --- a/backend/src/components/sdc/sdc.js +++ b/backend/src/components/sdc/sdc.js @@ -1,10 +1,11 @@ 'use strict'; -const { logApiError, getData, errorResponse, postData} = require('../utils'); +const { logApiError, getData, errorResponse, postData, stripNumberFormattingNumberOfCourses, formatNumberOfCourses, handleExceptionResponse} = require('../utils'); const HttpStatus = require('http-status-codes'); const config = require('../../config'); const utils = require('../utils'); const cacheService = require('../cache-service'); const { FILTER_OPERATION, VALUE_TYPE, CONDITION, ENROLLED_PROGRAM_TYPE_CODE_MAP, DUPLICATE_TYPE_CODES} = require('../../util/constants'); +const {createMoreFiltersSearchCriteria} = require('../studentFilters'); async function getSnapshotFundingDataForSchool(req, res) { try { @@ -99,6 +100,33 @@ async function getSDCSchoolCollectionStudentPaginated(req, res) { searchCriteriaList: createSearchCriteria(req.query.searchParams) }); + if(req.query.searchParams['tabFilter']) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: createTabFilter(req.query.searchParams['tabFilter']) + }); + } + + if (req.query.searchParams['multiFieldName']) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: createMultiFieldNameSearchCriteria(req.query.searchParams['multiFieldName']) + }); + } + if (req.query.searchParams['penLocalIdNumber']) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: createLocalIdPenSearchCriteria(req.query.searchParams['penLocalIdNumber']) + }); + } + + if (req.query.searchParams['moreFilters']) { + let criteriaArray = createMoreFiltersSearchCriteria(req.query.searchParams['moreFilters']); + criteriaArray.forEach(criteria => { + search.push(criteria); + }); + } + if(req.query.searchParams['assignedPen']) { search.push({ condition: CONDITION.AND, @@ -117,6 +145,10 @@ async function getSDCSchoolCollectionStudentPaginated(req, res) { }; let data = await getData(`${config.get('sdc:schoolCollectionStudentURL')}/paginated`, params); + if (req?.query?.returnKey) { + let result = data?.content.map((student) => student[req?.query?.returnKey]); + return res.status(HttpStatus.OK).json(result); + } if(req?.query?.tableFormat){ data.content = data?.content.map(toTableRow); @@ -140,6 +172,35 @@ async function getSDCSchoolCollectionStudentPaginated(req, res) { } } +function createTabFilter(searchParams) { + let searchCriteriaList = []; + let tableKey = 'sdcStudentEnrolledProgramEntities.enrolledProgramCode'; + + if (searchParams.label === 'FRENCH_PR') { + searchCriteriaList.push({ key: tableKey, operation: FILTER_OPERATION.IN, value: '05,08,11,14', valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + if (searchParams.label === 'CAREER_PR') { + searchCriteriaList.push({ key: tableKey, operation: FILTER_OPERATION.IN, value: '40,41,42,43', valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + if (searchParams.label === 'ELL_PR') { + searchCriteriaList.push({ key: tableKey, operation: FILTER_OPERATION.IN, value: '17', valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + if(searchParams.label === 'INDSUPPORT_PR') { + searchCriteriaList.push({ key: 'bandCode', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + searchCriteriaList.push({ key: 'nativeAncestryInd', value: 'Y', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + searchCriteriaList.push({ key: tableKey, operation: FILTER_OPERATION.IN_LEFT_JOIN, value: '29,33,36', valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + if (searchParams.label === 'SPECIALED_PR') { + searchCriteriaList.push({ key: 'specialEducationCategoryCode', operation: FILTER_OPERATION.IN, value: 'A,B,C,D,E,F,G,H,K,P,Q,R', valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + if (searchParams.label === 'REFUGEE') { + searchCriteriaList.push({ key: 'schoolFundingCode', operation: FILTER_OPERATION.IN, value: '16', valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + + return searchCriteriaList; + +} + function toTableRow(student) { let bandCodesMap = cacheService.getAllActiveBandCodesMap(); let careerProgramCodesMap = cacheService.getActiveCareerProgramCodesMap(); @@ -234,6 +295,51 @@ function createAssignedPENSearchCriteria(searchParams) { return searchCriteriaList; } +function createMultiFieldNameSearchCriteria(nameString) { + const nameParts = nameString.split(/\s+/); + const fieldNames = [ + 'legalFirstName', + 'legalMiddleNames', + 'legalLastName', + 'usualFirstName', + 'usualMiddleNames', + 'usualLastName' + ]; + + const searchCriteriaList = []; + for (const part of nameParts) { + for (const fieldName of fieldNames) { + searchCriteriaList.push({ + key: fieldName, + operation: FILTER_OPERATION.CONTAINS_IGNORE_CASE, + value: `%${part}%`, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + } + } + return searchCriteriaList; +} + +function createLocalIdPenSearchCriteria(value) { + let searchCriteriaList = []; + searchCriteriaList.push({ + key: 'studentPen', + operation: FILTER_OPERATION.EQUAL, + value: value, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + searchCriteriaList.push({ + key: 'localID', + operation: FILTER_OPERATION.EQUAL, + value: value, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + return searchCriteriaList; +} + async function getSDCSchoolCollectionStudentDetail(req, res) { try { let sdcSchoolCollectionStudentData = await getData(`${config.get('sdc:schoolCollectionStudentURL')}/${req.params.sdcSchoolCollectionStudentID}`); @@ -368,6 +474,60 @@ async function checkDuplicatesInCollection(req, res) { } } +async function updateAndValidateSdcSchoolCollectionStudent(req, res) { + try { + if(req.body.sdcSchoolCollectionStudentID) { + let sdcSchoolCollectionStudentID = req.body.sdcSchoolCollectionStudentID; + let currentStudent = await getData(`${config.get('sdc:schoolCollectionStudentURL')}/${sdcSchoolCollectionStudentID}`); + if (req.body.updateDate !== currentStudent.updateDate) { + throw new Error(HttpStatus.CONFLICT.toString()); + } + } + + const payload = req.body; + payload.createDate = null; + payload.createUser = null; + payload.updateDate = null; + payload.updateUser = 'ADMIN/' + req.session.passport.user.id; + + if (payload?.enrolledProgramCodes) { + payload.enrolledProgramCodes = payload.enrolledProgramCodes.join(''); + } + + if (payload?.numberOfCourses) { + payload.numberOfCourses = stripNumberFormattingNumberOfCourses(payload.numberOfCourses); + } + + payload.sdcSchoolCollectionStudentValidationIssues = null; + payload.sdcSchoolCollectionStudentEnrolledPrograms = null; + + const data = await postData(config.get('sdc:schoolCollectionStudentURL'), payload); + + if (data?.enrolledProgramCodes) { + data.enrolledProgramCodes = data?.enrolledProgramCodes.match(/.{1,2}/g); + } + + if (data?.numberOfCourses) { + data.numberOfCourses = formatNumberOfCourses(data?.numberOfCourses); + } + return res.status(HttpStatus.OK).json(data); + } catch (e) { + if (e.message === '409' || e.status === '409' || e.status === 409) { + return res.status(HttpStatus.CONFLICT).json({ + status: HttpStatus.CONFLICT, + message: 'The student you are attempting to update is already being saved by another user. Please refresh your screen and try again.' + }); + } else if (e.status === 400 && e.data.message === 'SdcSchoolCollectionStudent was not saved to the database because it would create provincial duplicate.') { + return res.status(HttpStatus.CONFLICT).json({ + status: HttpStatus.CONFLICT, + message: 'Student was not saved because it would create provincial duplicate.' + }); + } + return handleExceptionResponse(e, res); + } + +} + async function resolveDuplicates(req, res) { try { let sdcDuplicateID = req.body.duplicate.sdcDuplicateID; @@ -421,6 +581,7 @@ module.exports = { getSDCSchoolCollectionStudentDetail, getInDistrictDuplicates, updateStudentPEN, + updateAndValidateSdcSchoolCollectionStudent, checkDuplicatesInCollection, resolveDuplicates }; diff --git a/backend/src/components/studentFilters.js b/backend/src/components/studentFilters.js new file mode 100644 index 000000000..bf90bf035 --- /dev/null +++ b/backend/src/components/studentFilters.js @@ -0,0 +1,706 @@ +'use strict'; +const log = require('./logger'); +const { FILTER_OPERATION, VALUE_TYPE, CONDITION, ENROLLED_PROGRAM_TYPE_CODE_MAP, STUDENT_TYPE_CODES} = require('../util/constants'); +const cacheService = require('./cache-service'); + +function createMoreFiltersSearchCriteria(searchFilter = []) { + let searchCriteriaList = []; + let studentTypeFilterList = []; + let fteFilterList = []; + let supportBlockList = []; + let careerProgramFundingList = []; + let careerCodeList = []; + let careerProgramsList = []; + let frenchProgramsList = []; + let englishProgramsList = []; + let frenchProgramFundingList = []; + let indigenousProgramList = []; + let ancestryList = []; + let indigenousProgramsFundingList = []; + let bandCodeList = []; + let spedCodeList = []; + let spedFundingList = []; + let ellList = []; + let ellFunding = []; + let refugeeFunding = []; + let fundingTypeList = []; + let courseRangeList = []; + let penLocalIdNameFilter = []; + let schoolNameNumberFilter = []; + for (const [key, filter] of Object.entries(searchFilter)) { + let pValue = filter ? filter.map(filter => filter.value) : null; + if (key === 'studentType' && pValue) { + studentTypeFilterList = createStudentTypeFilter(pValue); + } + if (key === 'fte' && pValue) { + fteFilterList = createFteFilter(pValue); + } + if (key === 'careerCode' && pValue) { + careerCodeList = careerCodeFilter(pValue); + } + if (key === 'careerPrograms' && pValue) { + careerProgramsList = createCareerProgramFilter(pValue); + } + if (key === 'frenchProgram' && pValue) { + frenchProgramsList = createFrenchProgramFilter(pValue); + } + if (key === 'englishProgram' && pValue) { + englishProgramsList = createEnglishProgramFilter(pValue); + } + if( key === 'indigenousPrograms' && pValue) { + indigenousProgramList = createIndigenousProgramFilter(pValue); + } + if (key === 'support' && pValue) { + supportBlockList = createSupportBlockFilter(pValue); + } + if(key === 'careerProgramsFunding' && pValue) { + careerProgramFundingList = createCareerProgramFundingfilter(pValue); + } + if(key === 'ancestry' && pValue) { + ancestryList = createAncestryFilter(pValue); + } + if(key === 'indigenousProgramsFunding' && pValue) { + indigenousProgramsFundingList = createIndigenousFundingFilter(pValue); + } + if(key === 'frenchFunding' && pValue) { + frenchProgramFundingList = createFrenchFundingFilter(pValue); + } + if(key === 'sped' && pValue) { + spedCodeList = createSpedFilter(pValue); + } + if(key === 'spedFunding' && pValue) { + spedFundingList = createSpedFundingFilter(pValue); + } + if(key === 'ellYears' && pValue) { + ellList = createEllYearsFilter(pValue); + } + if(key === 'ellFunding' && pValue) { + ellFunding = createEllFundingFilter(pValue); + } + if(key === 'refugeeFunding' && pValue) { + refugeeFunding = createRefugeeFundingFilter(pValue); + } + if (key === 'warnings' && pValue) { + validateWarningFilter(pValue); + searchCriteriaList.push({ key: 'sdcStudentValidationIssueEntities.validationIssueSeverityCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + if (key === 'grade' && pValue) { + validateGradeFilter(pValue); + searchCriteriaList.push({ key: 'enrolledGradeCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + if (key === 'fteZero' && pValue) { + validateFteZeroFilter(pValue); + searchCriteriaList.push({ key: 'fteZeroReasonCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + if (key === 'fundingType' && pValue) { + fundingTypeList = createFundingTypeFilter(pValue); + } + if (key === 'bandResidence' && pValue) { + bandCodeList.push({ key: 'bandCode', value: pValue.toString(), operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + if (key === 'bandCode' && pValue) { + if (pValue.toString() === 'true') { + bandCodeList.push({ key: 'bandCode', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } else if (pValue.toString() === 'false') { + bandCodeList.push({ key: 'bandCode', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + } + if (key === 'numberOfCoursesDec' && pValue) { + courseRangeList = createCourseRangeFilter(pValue); + } + if (key === 'penLocalIdName' && pValue) { + let nameCriteria = createMultiFieldNameSearchCriteria(pValue.toString()); + let penCriteria = createLocalIdPenSearchCriteria(pValue.toString()); + penLocalIdNameFilter = [...nameCriteria, ...penCriteria]; + } + if (key === 'schoolNameNumber' && pValue) { + let schoolNameNumberCriteria = createSchoolNameNumberSearchCriteria(pValue.toString()); + schoolNameNumberFilter = [...schoolNameNumberCriteria]; + } + } + const search = []; + if (searchCriteriaList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: searchCriteriaList + }); + } + if (fundingTypeList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: fundingTypeList + }); + } + if (studentTypeFilterList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: studentTypeFilterList + }); + } + if (fteFilterList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: fteFilterList + }); + } + if (supportBlockList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: supportBlockList + }); + } + if (careerProgramFundingList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: careerProgramFundingList + }); + } + if (careerCodeList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: careerCodeList + }); + } + if (careerProgramsList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: careerProgramsList + }); + } + if (frenchProgramsList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: frenchProgramsList + }); + } + if (englishProgramsList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: englishProgramsList + }); + } + if (frenchProgramFundingList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: frenchProgramFundingList + }); + } + if(indigenousProgramList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: indigenousProgramList + }); + } + if(ancestryList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: ancestryList + }); + } + if(indigenousProgramsFundingList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: indigenousProgramsFundingList + }); + } + if(bandCodeList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: bandCodeList + }); + } + if(spedCodeList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: spedCodeList + }); + } + if(spedFundingList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: spedFundingList + }); + } + if(ellFunding.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: ellFunding + }); + } + if(refugeeFunding.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: refugeeFunding + }); + } + if(ellList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: ellList + }); + } + if(courseRangeList.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: courseRangeList + }); + } + if (penLocalIdNameFilter.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: penLocalIdNameFilter + }); + } + if (schoolNameNumberFilter.length > 0) { + search.push({ + condition: CONDITION.AND, + searchCriteriaList: schoolNameNumberFilter + }); + } + return search; +} + +function validateFteZeroFilter(filters) { + let fteZeroCategories = [ + 'OUTOFPROV', + 'NOMROLL', + 'TOOYOUNG', + 'INDYADULT', + 'INACTIVE', + 'DISTDUP', + 'AUTHDUP' + ]; + if (filters.length > 0) { + if (filters.every(value => fteZeroCategories.includes(cat => value === cat))) { + log.error('Invalid zero fte reason code.'); + throw new Error('400'); + } + } +} + +function validateGradeFilter(filterGrades = []) { + const activeGradeCodes = cacheService.getActiveEnrolledGradeCodes(); + if (filterGrades.length > 0) { + if (filterGrades.every(value => activeGradeCodes.includes(grade => value === grade.enrolledGradeCode))) { + log.error('Invalid grade filter.'); + throw new Error('400'); + } + } +} + +function validateFundingTypeFilter(filterFunding = []) { + const activeFundingCodes = cacheService.getActiveSchoolFundingCodes(); + if (filterFunding.length > 0) { + if (filterFunding.every(value => activeFundingCodes.includes(code => code !== 'No Funding' && value === code.schoolFundingCode))) { + log.error('Invalid funding code filter.'); + throw new Error('400'); + } + } +} + +function validateCareerCodeFilter(filterCareerCodes = []) { + const activeFundingCodes = cacheService.getActiveCareerProgramCodes(); + if (filterCareerCodes.length > 0) { + if (filterCareerCodes.every(value => activeFundingCodes.includes(code => value === code.careerProgramCode))) { + log.error('Invalid career code filter.'); + throw new Error('400'); + } + } +} + +function validateEnrolledProgramFilter(filterGrades = []) { + const activeFundingCodes = cacheService.getActiveEnrolledProgramCodes(); + if (filterGrades.length > 0) { + if (filterGrades.every(value => activeFundingCodes.includes(code => value === code.enrolledProgramCode))) { + log.error('Invalid enrolled program filter.'); + throw new Error('400'); + } + } +} + +function validateWarningFilter(filters = []) { + let allowedWarningValues = [ + 'FUNDING_WARNING', + 'INFO_WARNING' + ]; + if (filters.length > 0) { + if (filters.every(value => allowedWarningValues.includes(cat => value === cat))) { + log.error('Invalid warning filter.'); + throw new Error('400'); + } + } +} + +function validateSpedCodes(filters = []) { + const activeSpedCodes = cacheService.getActiveSpecialEducationCodes(); + if (filters.length > 0) { + if (filters.every(value => activeSpedCodes.includes(code => value === code.specialEducationCategoryCode))) { + log.error('Invalid special education filter.'); + throw new Error('400'); + } + } +} + +function createFundingTypeFilter(pValue) { + let fundingTypeList = []; + validateFundingTypeFilter(pValue); + + if (pValue.includes('14')) { + fundingTypeList.push({ key: 'schoolFundingCode', value: '14', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + if (pValue.includes('20')) { + fundingTypeList.push({ key: 'schoolFundingCode', value: '20', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + if (pValue.includes('16')) { + fundingTypeList.push({ key: 'schoolFundingCode', value: '16', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + if (pValue.includes('No Funding')) { + fundingTypeList.push({ key: 'schoolFundingCode', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + return fundingTypeList; +} + +function createEllYearsFilter(pValue) { + let ellList = []; + if(pValue.includes('ell1Between5')) { + ellList.push({ key: 'yearsInEll', value: 1, operation: FILTER_OPERATION.GREATER_THAN_OR_EQUAL_TO, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.AND }); + ellList.push({ key: 'yearsInEll', value: 5, operation: FILTER_OPERATION.LESS_THAN_OR_EQUAL_TO, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.AND }); + } + if(pValue.includes('ellGtEq6')) { + ellList.push({ key: 'yearsInEll', value: 6, operation: FILTER_OPERATION.GREATER_THAN_OR_EQUAL_TO, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.OR }); + } + return ellList; +} + +function createFrenchFundingFilter(pValue) { + let frenchProgramFundingList = []; + + if (pValue.toString() === 'true') { + frenchProgramFundingList.push({ key: 'frenchProgramNonEligReasonCode', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } else if (pValue.toString() === 'false') { + frenchProgramFundingList.push({ key: 'frenchProgramNonEligReasonCode', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + + return frenchProgramFundingList; +} + +function createIndigenousFundingFilter(pValue) { + let indigenousProgramsFundingList = []; + + if (pValue.toString() === 'true') { + indigenousProgramsFundingList.push({ key: 'indigenousSupportProgramNonEligReasonCode', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } else if (pValue.toString() === 'false') { + indigenousProgramsFundingList.push({ key: 'indigenousSupportProgramNonEligReasonCode', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + + return indigenousProgramsFundingList; +} + +function createSpedFundingFilter(pValue) { + let spedFundingList = []; + + if (pValue.toString() === 'true') { + spedFundingList.push({ key: 'specialEducationNonEligReasonCode', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } else if (pValue.toString() === 'false') { + spedFundingList.push({ key: 'specialEducationNonEligReasonCode', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + + return spedFundingList; +} + +function createSpedFilter(pValue) { + let spedCodeList = []; + validateSpedCodes(pValue); + if (pValue.includes('noSpedCode')) { + const notInValues = cacheService.getActiveSpecialEducationCodes().map(x=>x.specialEducationCategoryCode).filter(value => !pValue.includes(value) && pValue!=='noSpedCode'); + if(notInValues?.length > 0) { //if all filters are selected then it is equivalent to no filters being selected + spedCodeList.push({ key: 'specialEducationCategoryCode', value: notInValues.toString(), operation: FILTER_OPERATION.NOT_IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + } else { + spedCodeList.push({ key: 'specialEducationCategoryCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + return spedCodeList; +} + +function createEllFundingFilter(pValue) { + let ellFundingList = []; + + if (pValue.toString() === 'true') { + ellFundingList.push({ key: 'ellNonEligReasonCode', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } else if (pValue.toString() === 'false') { + ellFundingList.push({ key: 'ellNonEligReasonCode', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + + return ellFundingList; +} + +function createRefugeeFundingFilter(pValue) { + let refugeeFundingList = []; + + if (pValue.toString() === 'true') { + refugeeFundingList.push({ + key: 'sdcStudentValidationIssueEntities.validationIssueCode', + value: 'REFUGEEINPREVCOL', + operation: FILTER_OPERATION.NONE_IN, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.AND + }); + refugeeFundingList.push({ + key: 'sdcStudentValidationIssueEntities.validationIssueCode', + value: 'REFUGEEISADULT', + operation: FILTER_OPERATION.NONE_IN, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.AND + }); + } else if (pValue.toString() === 'false') { + refugeeFundingList.push({ + key: 'sdcStudentValidationIssueEntities.validationIssueCode', + value: 'REFUGEEINPREVCOL', + operation: FILTER_OPERATION.IN, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + refugeeFundingList.push({ + key: 'sdcStudentValidationIssueEntities.validationIssueCode', + value: 'REFUGEEISADULT', + operation: FILTER_OPERATION.IN, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + } + + return refugeeFundingList; +} + +function createAncestryFilter(pValue) { + let ancestryList = []; + + if (pValue.toString() === 'true') { + ancestryList.push({ key: 'nativeAncestryInd', value: 'Y', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } else if (pValue.toString() === 'false') { + ancestryList.push({ key: 'nativeAncestryInd', value: 'N', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + + return ancestryList; +} + +function createCareerProgramFundingfilter(pValue) { + let careerProgramFundingList = []; + if (pValue.toString() === 'isCareerFundingEligible') { + careerProgramFundingList.push({ key: 'careerProgramNonEligReasonCode', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } else if (pValue.toString() === 'isNotCareerFundingEligible') { + careerProgramFundingList.push({ key: 'careerProgramNonEligReasonCode', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } + return careerProgramFundingList; +} + +function createSupportBlockFilter(pValue) { + let supportBlockList = []; + + if (pValue.toString() === 'hasSupportBlocks') { + supportBlockList.push({ key: 'supportBlocks', value: '0', operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + supportBlockList.push({ key: 'supportBlocks', value: null, operation: FILTER_OPERATION.NOT_EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.AND }); + } else if (pValue.toString() === 'noSupportBlocks') { + supportBlockList.push({ key: 'supportBlocks', value: '0', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + supportBlockList.push({ key: 'supportBlocks', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + + return supportBlockList; +} + +function createIndigenousProgramFilter(pValue) { + let indigenousProgramList = []; + + validateEnrolledProgramFilter(pValue); + if (pValue.includes('noIndigenousPrograms')) { + const notInValues = ENROLLED_PROGRAM_TYPE_CODE_MAP.INDIGENOUS_ENROLLED_PROGRAM_CODES.filter(value => !pValue.includes(value) && pValue!=='noIndigenousPrograms'); + if(notInValues?.length > 0) { //if all filters are selected then it is equivalent to no filters being selected + indigenousProgramList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: notInValues.toString(), operation: FILTER_OPERATION.NONE_IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + } else { + indigenousProgramList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + return indigenousProgramList; +} + +function createFrenchProgramFilter(pValue) { + let frenchProgramsList = []; + + validateEnrolledProgramFilter(pValue); + if (pValue.includes('noFrenchPrograms')) { + const notInValues = ENROLLED_PROGRAM_TYPE_CODE_MAP.FRENCH_ENROLLED_PROGRAM_CODES.filter(value => !pValue.includes(value) && pValue!=='noFrenchPrograms'); + if(notInValues?.length > 0) { //if all filters are selected then it is equivalent to no filters being selected + frenchProgramsList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: notInValues.toString(), operation: FILTER_OPERATION.NONE_IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + } else { + frenchProgramsList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + return frenchProgramsList; +} + +function createEnglishProgramFilter(pValue) { + let englishProgramsList = []; + + validateEnrolledProgramFilter(pValue); + if (pValue.includes('noEnglish17')) { + englishProgramsList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: ENROLLED_PROGRAM_TYPE_CODE_MAP.ENGLISH_ENROLLED_PROGRAM_CODES.toString(), operation: FILTER_OPERATION.NONE_IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } else { + englishProgramsList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + return englishProgramsList; +} + +function createCareerProgramFilter(pValue) { + let careerProgramsList = []; + + validateEnrolledProgramFilter(pValue); + if (pValue.includes('noCareerPrograms')) { + const notInValues = ENROLLED_PROGRAM_TYPE_CODE_MAP.CAREER_ENROLLED_PROGRAM_CODES.filter(value => !pValue.includes(value) && pValue!=='noCareerPrograms'); + if(notInValues?.length > 0) { //if all filters are selected then it is equivalent to no filters being selected + careerProgramsList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: notInValues.toString(), operation: FILTER_OPERATION.NONE_IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + } else { + careerProgramsList.push({ key: 'sdcStudentEnrolledProgramEntities.enrolledProgramCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + return careerProgramsList; +} + +function careerCodeFilter(pValue) { + let careerCodeList = []; + + validateCareerCodeFilter(pValue); + if (pValue.includes('noCareerCodes')) { + const notInValues = cacheService.getActiveCareerProgramCodes().map(x=>x.careerProgramCode).filter(value => !pValue.includes(value) && pValue!=='noCareerCodes'); + if(notInValues?.length > 0) { //if all filters are selected then it is equivalent to no filters being selected + careerCodeList.push({ key: 'careerProgramCode', value: notInValues.toString(), operation: FILTER_OPERATION.NOT_IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + } else { + careerCodeList.push({ key: 'careerProgramCode', value: pValue.toString(), operation: FILTER_OPERATION.IN, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR }); + } + return careerCodeList; +} + +function createFteFilter(pValue) { + let fteFilterList = []; + + if (pValue.includes('fteEq0')) { + fteFilterList.push({ key: 'fte', value: 0, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.OR }); + } + if (pValue.includes('fteLt1')) { + fteFilterList.push({ key: 'fte', value: 1, operation: FILTER_OPERATION.LESS_THAN, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.OR }); + } + if (pValue.includes('fteGt0')) { + fteFilterList.push({ key: 'fte', value: 0, operation: FILTER_OPERATION.GREATER_THAN, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.OR }); + } + + return fteFilterList; +} + +function createCourseRangeFilter(pValue) { + let courseRangeList = []; + + courseRangeList.push({key:'numberOfCoursesDec', value: pValue[0][1], operation: FILTER_OPERATION.LESS_THAN_OR_EQUAL_TO, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.AND}); + courseRangeList.push({key:'numberOfCoursesDec', value: pValue[0][0], operation: FILTER_OPERATION.GREATER_THAN_OR_EQUAL_TO, valueType: VALUE_TYPE.INTEGER, condition: CONDITION.AND}); + if(pValue[0][0] === '0'){ + courseRangeList.push({key:'numberOfCoursesDec', value: null, operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.STRING, condition: CONDITION.OR}); + } + return courseRangeList; +} + +function createStudentTypeFilter(pValue) { + let studentTypeFilterList = []; + if(pValue.includes(STUDENT_TYPE_CODES.SCHOOL_AGED) && pValue.includes(STUDENT_TYPE_CODES.ADULT) && pValue.includes(STUDENT_TYPE_CODES.PRESCHOOL_AGED)){ + return studentTypeFilterList; + } + else if(pValue.includes(STUDENT_TYPE_CODES.SCHOOL_AGED) && pValue.includes(STUDENT_TYPE_CODES.ADULT)){ + studentTypeFilterList.push({ key: 'isSchoolAged', value: 'true', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.OR }); + studentTypeFilterList.push({ key: 'isAdult', value: 'true', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.OR }); + } + else if(pValue.includes(STUDENT_TYPE_CODES.SCHOOL_AGED) && pValue.includes(STUDENT_TYPE_CODES.PRESCHOOL_AGED)){ + studentTypeFilterList.push({ key: 'isAdult', value: 'false', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.OR }); + } + else if(pValue.includes(STUDENT_TYPE_CODES.ADULT) && pValue.includes(STUDENT_TYPE_CODES.PRESCHOOL_AGED)){ + studentTypeFilterList.push({ key: 'isSchoolAged', value: 'false', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.OR }); + } + else if (pValue.includes('isSchoolAged')) { + studentTypeFilterList.push({ key: 'isSchoolAged', value: 'true', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.OR }); + } + else if (pValue.includes('isAdult')) { + studentTypeFilterList.push({ key: 'isAdult', value: 'true', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.OR }); + } + else if (pValue.includes('isUnderSchoolAged')) { + studentTypeFilterList.push({ key: 'isSchoolAged', value: 'false', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.OR }); + studentTypeFilterList.push({ key: 'isAdult', value: 'false', operation: FILTER_OPERATION.EQUAL, valueType: VALUE_TYPE.BOOLEAN, condition: CONDITION.AND }); + } + return studentTypeFilterList; +} + +function createMultiFieldNameSearchCriteria(nameString) { + const nameParts = nameString.split(/\s+/); + const fieldNames = [ + 'legalFirstName', + 'legalMiddleNames', + 'legalLastName', + 'usualFirstName', + 'usualMiddleNames', + 'usualLastName' + ]; + + const searchCriteriaList = []; + for (const part of nameParts) { + for (const fieldName of fieldNames) { + searchCriteriaList.push({ + key: fieldName, + operation: FILTER_OPERATION.CONTAINS_IGNORE_CASE, + value: `%${part}%`, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + } + } + return searchCriteriaList; +} + +function createLocalIdPenSearchCriteria(value) { + let searchCriteriaList = []; + searchCriteriaList.push({ + key: 'studentPen', + operation: FILTER_OPERATION.EQUAL, + value: value, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + searchCriteriaList.push({ + key: 'assignedPen', + operation: FILTER_OPERATION.EQUAL, + value: value, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + searchCriteriaList.push({ + key: 'localID', + operation: FILTER_OPERATION.EQUAL, + value: value, + valueType: VALUE_TYPE.STRING, + condition: CONDITION.OR + }); + return searchCriteriaList; +} + +function createSchoolNameNumberSearchCriteria(value) { + const searchSchoolCriteriaList = []; + + searchSchoolCriteriaList.push({ + key: 'sdcSchoolCollection.schoolID', + operation: FILTER_OPERATION.EQUAL, + value: value, + valueType: VALUE_TYPE.UUID, + condition: CONDITION.AND + }); + + return searchSchoolCriteriaList; +} + +module.exports = { + createMoreFiltersSearchCriteria +}; diff --git a/backend/src/components/utils.js b/backend/src/components/utils.js index 5906fe7ae..581ba12f9 100644 --- a/backend/src/components/utils.js +++ b/backend/src/components/utils.js @@ -65,6 +65,55 @@ function addTokenToHeader(params, token) { return params; } +function stripNumberFormattingNumberOfCourses(value) { + if (!value) return '0000'; + return value.replace('.', ''); +} + +function formatNumberOfCourses(value) { + if (!value) return '00.00'; + + let formatted = ''; + switch (value.length) { + case 1: + formatted = `0${value}.00`; + break; + case 2: + formatted = `${value}.00`; + break; + case 3: + formatted = `0${value.slice(0, 1)}.${value.slice(1)}`; + break; + case 4: + formatted = `${value.slice(0, 2)}.${value.slice(2)}`; + break; + default: + formatted = '00.00'; + } + return formatted; +} + +function handleExceptionResponse(e, res) { + if (e.message === '404' || e.status === '404' || e.status === 404) { + return res.status(HttpStatus.NOT_FOUND).json(); + } else if(e.message === '403') { + return res.status(HttpStatus.FORBIDDEN).json({ + status: HttpStatus.FORBIDDEN, + message: 'You do not have permission to access this information' + }); + } else if(e.message === '401'){ + return res.status(HttpStatus.UNAUTHORIZED).json({ + status: HttpStatus.UNAUTHORIZED, + message: 'Token is not valid' + }); + } else { + return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ + message: 'INTERNAL SERVER ERROR', + code: HttpStatus.INTERNAL_SERVER_ERROR + }); + } +} + async function deleteData(url) { try { const delConfig = { @@ -328,11 +377,6 @@ function forwardGet(apiName, urlKey, extraPath, handleResponse) { }; } -function stripNumberFormattingNumberOfCourses(value) { - if (!value) return '0000'; - return value.replace('.', ''); -} - const utils = { getUser(req) { const thisSession = req.session; @@ -536,7 +580,9 @@ const utils = { forwardGet, isPdf, isImage, - stripNumberFormattingNumberOfCourses + stripNumberFormattingNumberOfCourses, + formatNumberOfCourses, + handleExceptionResponse }; module.exports = utils; diff --git a/backend/src/config/index.js b/backend/src/config/index.js index 003613d01..54a39a679 100644 --- a/backend/src/config/index.js +++ b/backend/src/config/index.js @@ -205,6 +205,8 @@ nconf.defaults({ duplicateResolutionCodesURL: process.env.SDC_API_URL + '/duplicate-resolution-codes', schoolFundingCodesURL: process.env.SDC_API_URL + '/funding-codes', programDuplicateTypeCodesURL: process.env.SDC_API_URL + '/program-duplicate-type-codes', + programEligibilityTypeCodesURL: process.env.SDC_API_URL + '/program-eligibility-issue-codes', + zeroFteReasonCodesURL: process.env.SDC_API_URL + '/zero-fte-reason-codes', } }); module.exports = nconf; diff --git a/backend/src/routes/sdc.js b/backend/src/routes/sdc.js index fd3db13bd..cd93b6885 100644 --- a/backend/src/routes/sdc.js +++ b/backend/src/routes/sdc.js @@ -6,7 +6,7 @@ const perm = require('../util/Permission'); const extendSession = utils.extendSession(); const { getSnapshotFundingDataForSchool, getAllCollectionsForSchool, getActiveCollection, getSdcDistrictCollectionMonitoringByCollectionId, getIndySdcSchoolCollectionMonitoringByCollectionId, unsubmitSdcDistrictCollection, unsubmitSdcSchoolCollection, getInDistrictDuplicates, - getSDCSchoolCollectionStudentPaginated, getSDCSchoolCollectionStudentDetail, updateStudentPEN, checkDuplicatesInCollection, resolveDuplicates + getSDCSchoolCollectionStudentPaginated, getSDCSchoolCollectionStudentDetail, updateStudentPEN, checkDuplicatesInCollection, updateAndValidateSdcSchoolCollectionStudent, resolveDuplicates } = require('../components/sdc/sdc'); const {getCachedSDCData} = require('../components/sdc/sdc-cache'); const constants = require('../util/constants'); @@ -24,6 +24,11 @@ router.get('/home-language-spoken-codes', passport.authenticate('jwt', {session: router.get('/school-funding-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCachedSDCData(constants.CACHE_KEYS.SDC_SCHOOL_FUNDING_CODES, 'sdc:schoolFundingCodesURL')); router.get('/specialEducation-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCachedSDCData(constants.CACHE_KEYS.SDC_SPECIAL_ED_CODES, 'sdc:specialEdCodesURL')); router.get('/duplicate-resolution-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCachedSDCData(constants.CACHE_KEYS.SDC_DUPLICATE_RESOLUTION_CODES, 'sdc:duplicateResolutionCodesURL')); + +router.get('/validation-issue-type-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCodes('sdc:validationIssueTypeCodesURL', constants.CACHE_KEYS.SDC_VALIDATION_ISSUE_TYPE_CODES, null, true)); +router.get('/program-eligibility-issue-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCodes('sdc:programEligibilityTypeCodesURL', constants.CACHE_KEYS.SDC_PROGRAM_ELIGIBILITY_TYPE_CODES, null, true)); +router.get('/zero-fte-reason-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCodes('sdc:zeroFteReasonCodesURL', constants.CACHE_KEYS.SDC_ZERO_FTE_REASON_CODES, null, true)); + //end cached code table calls router.get('/district-collection-status-codes', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getCachedSDCData(constants.CACHE_KEYS.SDC_DISTRICT_COLLECTION_STATUS_CODES, 'sdc:districtCollectionStatusCodesURL')); @@ -43,6 +48,9 @@ router.get('/collection/:collectionID/duplicates', passport.authenticate('jwt', router.get('/sdcSchoolCollectionStudent/:sdcSchoolCollectionStudentID', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, getSDCSchoolCollectionStudentDetail); router.post('/sdcSchoolCollectionStudent/:sdcSchoolCollectionStudentID/update-pen/:penCode', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, updateStudentPEN); +//update student +router.post('/sdcSchoolCollectionStudent', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, updateAndValidateSdcSchoolCollectionStudent); + //district collection router.post('/sdcDistrictCollection/:sdcDistrictCollectionID/unsubmit', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, unsubmitSdcDistrictCollection); router.post('/sdcDistrictCollection/resolve-district-duplicates/:sdcDuplicateID/:type', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.STUDENT_DATA_COLLECTION), extendSession, resolveDuplicates); diff --git a/backend/src/util/constants.js b/backend/src/util/constants.js index 57374b51b..7e4d26cd0 100644 --- a/backend/src/util/constants.js +++ b/backend/src/util/constants.js @@ -31,7 +31,10 @@ cacheKeys = { SDC_HOME_LANGUAGE_SPOKEN_CODES: 'sdc_home_language_spoken_codes', SDC_SCHOOL_FUNDING_CODES: 'sdc_school_funding_code', SDC_SPECIAL_ED_CODES: 'sdc_special_ed_codes', - SDC_DUPLICATE_RESOLUTION_CODES: 'sdc_duplicate_resolution_codes' + SDC_DUPLICATE_RESOLUTION_CODES: 'sdc_duplicate_resolution_codes', + SDC_VALIDATION_ISSUE_TYPE_CODES: 'sdc_validation_issue_type_codes', + SDC_PROGRAM_ELIGIBILITY_TYPE_CODES: 'sdc_program_eligibility_type_codes', + SDC_ZERO_FTE_REASON_CODES: 'sdc_zero_fte_reason_codes', }; const CACHE_KEYS = Object.freeze(cacheKeys); @@ -66,6 +69,10 @@ const FILTER_OPERATION = Object.freeze( * In filter operation. */ IN: 'in', + /** + * Filter to return when none of the child records includes the values + */ + NONE_IN: 'none_in', /** * Not in filter operation. */ @@ -194,6 +201,12 @@ const DUPLICATE_TYPE_CODES = Object.freeze({ PROGRAM: 'PROGRAM' }); +const STUDENT_TYPE_CODES = Object.freeze({ + SCHOOL_AGED: 'isSchoolAged', + ADULT: 'isAdult', + PRESCHOOL_AGED: 'isUnderSchoolAged' +}); + module.exports = { FILTER_OPERATION, CONDITION, @@ -208,5 +221,6 @@ module.exports = { WS_CREATE_SCHOOL_TOPIC, WS_NEW_SECURE_MESSAGE_TOPIC, ENROLLED_PROGRAM_TYPE_CODE_MAP, - DUPLICATE_TYPE_CODES + DUPLICATE_TYPE_CODES, + STUDENT_TYPE_CODES }; diff --git a/backend/src/util/redis/redis-utils.js b/backend/src/util/redis/redis-utils.js index bd4c73942..85986284e 100644 --- a/backend/src/util/redis/redis-utils.js +++ b/backend/src/util/redis/redis-utils.js @@ -79,7 +79,6 @@ const redisUtil = { createSagaRecord, removeEventRecordFromRedis, getSagaEventsByRedisKey, - getRedLock() { if (redLock) { return redLock; // reusable red lock object. diff --git a/frontend/src/common/apiService.js b/frontend/src/common/apiService.js index 6695af7e6..71a03d612 100644 --- a/frontend/src/common/apiService.js +++ b/frontend/src/common/apiService.js @@ -51,6 +51,7 @@ function getCodes(url) { return await apiAxios.get(url, query); } catch (e) { console.log(`Failed to get from Nodejs API - ${e}`); + console.log('failing on', url); throw e; } }; @@ -117,9 +118,12 @@ export default { getAllActiveInstituteProvinceCodes: getCodes(`${Routes.cache.PROVINCES_URL}?active=true`), getAllActiveInstituteCountryCodes: getCodes(`${Routes.cache.COUNTRIES_URL}?active=true`), getAllFundingGroups: getCodes(`${Routes.institute.FUNDING_DATA_URL}`), + getAllProgramEligibilityTypeCodes:getCodes(`${Routes.sdc.SDC_PROGRAM_ELIGIBILITY_TYPE_CODES}`), + getAllZeroFteReasonCodes:getCodes(`${Routes.sdc.SDC_ZERO_FTE_REASON_CODES}`), getAllCollectionTypeCodes: getCodes(`${Routes.sdc.COLLECTION_TYPE_CODES_URL}`), getAllDistrictCollectionStatusCodes:getCodes(`${Routes.sdc.SDC_DISTRICT_COLLECTION_STATUS_CODES}`), getAllSchoolCollectionStatusCodes:getCodes(`${Routes.sdc.SDC_SCHOOL_COLLECTION_STATUS_CODES}`), + getAllValidationIssueTypeCodes:getCodes(`${Routes.sdc.SDC_VALIDATION_ISSUE_TYPE_CODES}`), getAllActiveBandCodes:getCodes(`${Routes.sdc.SDC_BAND_CODES}?active=true`), getAllActiveCareerProgramCodes:getCodes(`${Routes.sdc.SDC_CAREER_PROGRAM_CODES}?active=true`), getAllActiveEnrolledProgramCodes:getCodes(`${Routes.sdc.SDC_ENROLLED_PROGRAM_CODES}?active=true`), diff --git a/frontend/src/components/common/CustomTable.vue b/frontend/src/components/common/CustomTable.vue index fdc5191d1..3d528cec2 100644 --- a/frontend/src/components/common/CustomTable.vue +++ b/frontend/src/components/common/CustomTable.vue @@ -197,7 +197,7 @@ export default { default: false } }, - emits: ['reload', 'openStudentDetails', 'selections'], + emits: ['reload', 'openStudentDetails', 'selections', 'editSelectedRow'], data() { return { masterCheckbox: false, @@ -249,9 +249,9 @@ export default { }); }, methods: { - // rowclicked(props) { - // this.$emit('openStudentDetails', props); - // }, + rowclicked(props) { + this.$emit('editSelectedRow', props); + }, onClick(prop) { let selectedValue = prop.item.raw; if(this.isSelected(selectedValue)) { diff --git a/frontend/src/components/common/Filters.vue b/frontend/src/components/common/Filters.vue index 12dd68255..63ba5c33c 100644 --- a/frontend/src/components/common/Filters.vue +++ b/frontend/src/components/common/Filters.vue @@ -25,6 +25,16 @@ cols="6" class="ml-2" > + + + { + const useAppStore = appStore(); + return useAppStore.addAlertNotification({text: message, alertType: ALERT_NOTIFICATION_TYPES.WARN}); +}; + +export const setSuccessAlert = (message) => { + const useAppStore = appStore(); + return useAppStore.addAlertNotification({text: message, alertType: ALERT_NOTIFICATION_TYPES.SUCCESS}); +}; + +export const setFailureAlert = (message) => { + const useAppStore = appStore(); + return useAppStore.addAlertNotification({text: message, alertType: ALERT_NOTIFICATION_TYPES.ERROR}); +}; diff --git a/frontend/src/components/data-collection/AllStudents/AllStudentsComponent.vue b/frontend/src/components/data-collection/AllStudents/AllStudentsComponent.vue new file mode 100644 index 000000000..3995e494f --- /dev/null +++ b/frontend/src/components/data-collection/AllStudents/AllStudentsComponent.vue @@ -0,0 +1,64 @@ + + + + + + diff --git a/frontend/src/components/data-collection/AllStudents/DetailComponent.vue b/frontend/src/components/data-collection/AllStudents/DetailComponent.vue new file mode 100644 index 000000000..2e2974af2 --- /dev/null +++ b/frontend/src/components/data-collection/AllStudents/DetailComponent.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/frontend/src/components/data-collection/AllStudents/EditStudent.vue b/frontend/src/components/data-collection/AllStudents/EditStudent.vue new file mode 100644 index 000000000..6c15ce30e --- /dev/null +++ b/frontend/src/components/data-collection/AllStudents/EditStudent.vue @@ -0,0 +1,1009 @@ + + + + + + diff --git a/frontend/src/components/data-collection/AllStudents/ViewStudentDetailsComponent.vue b/frontend/src/components/data-collection/AllStudents/ViewStudentDetailsComponent.vue new file mode 100644 index 000000000..06bb3bd0a --- /dev/null +++ b/frontend/src/components/data-collection/AllStudents/ViewStudentDetailsComponent.vue @@ -0,0 +1,216 @@ + + + + + + diff --git a/frontend/src/components/data-collection/CollectionView.vue b/frontend/src/components/data-collection/CollectionView.vue index 0477ffaf0..4f3b95262 100644 --- a/frontend/src/components/data-collection/CollectionView.vue +++ b/frontend/src/components/data-collection/CollectionView.vue @@ -74,6 +74,11 @@ > Resolve Provincial Duplicates + + All Students + + + + @@ -124,6 +136,7 @@ import PenMatch from '@/components/data-collection/PenMatch.vue'; import ProvincialDuplicates from '@/components/data-collection/provincialDuplicates/ProvincialDuplicates.vue'; import DistrictMonitoring from '@/components/data-collection/DistrictMonitoring.vue'; import Spinner from '@/components/common/Spinner.vue'; +import AllStudentsComponent from '@/components/data-collection/AllStudents/AllStudentsComponent.vue'; export default { name: 'CollectionView', @@ -131,6 +144,7 @@ export default { Spinner, DistrictMonitoring, ProvincialDuplicates, + AllStudentsComponent, PenMatch, IndySchoolMonitoring, }, diff --git a/frontend/src/store/modules/sdcCollection.js b/frontend/src/store/modules/sdcCollection.js index 9dc8beec0..28c50df27 100644 --- a/frontend/src/store/modules/sdcCollection.js +++ b/frontend/src/store/modules/sdcCollection.js @@ -19,6 +19,9 @@ export const sdcCollectionStore = defineStore('sdcCollection', { genderCodes: [], homeLanguageSpokenCodesMap: new Map(), homeLanguageSpokenCodes: [], + programEligibilityCodesMap: new Map(), + zeroFteReasonCodesMap: new Map(), + validationIssueTypeCodesMap: new Map(), schoolFundingCodesMap: new Map(), schoolFundingCodes: [], specialEducationCodesMap: new Map(), @@ -43,6 +46,16 @@ export const sdcCollectionStore = defineStore('sdcCollection', { this.schoolCollectionStatusCodesMap.set(schoolCollectionCode.sdcSchoolCollectionStatusCode, schoolCollectionCode); }); }, + setProgramEligibilityCodesMap(programEligibilityCodesMap) { + programEligibilityCodesMap.forEach(issue => { + this.programEligibilityCodesMap.set(issue.programEligibilityIssueTypeCode, issue); + }); + }, + setZeroFteReasonCodesMap(zeroFteReasonCodesMap) { + zeroFteReasonCodesMap.forEach(issue => { + this.zeroFteReasonCodesMap.set(issue.fteZeroReasonCode, issue); + }); + }, setBandCodes(bandCodes) { this.bandCodes = bandCodes.map(item => { return {...item, dropdownText: `${item.description} (${item.bandCode})`}; @@ -120,6 +133,12 @@ export const sdcCollectionStore = defineStore('sdcCollection', { this.specialEducationCodesMap.set(specialEducationCategoryCode.specialEducationCategoryCode, specialEducationCategoryCode); }); }, + setValidationIssueTypeCodes(validationIssueTypeCodes) { + this.validationIssueTypeCodesMap = new Map(); + validationIssueTypeCodes.forEach(validationIssue => { + this.validationIssueTypeCodesMap.set(validationIssue.validationIssueTypeCode, validationIssue); + }); + }, setSelectedIDs(selectedIDs) { this.selectedIDs = selectedIDs; }, @@ -155,6 +174,9 @@ export const sdcCollectionStore = defineStore('sdcCollection', { ... this.homeLanguageSpokenCodesMap.size === 0 ? [ApiService.getAllActiveHomeLanguageSpokenCodes().then((res) => this.setHomeLanguageSpokenCodes(res.data))] : [], ... this.schoolFundingCodesMap.size === 0 ? [ApiService.getAllActiveSchoolFundingCodes().then((res) => this.setSchoolFundingCodes(res.data))] : [], ... this.specialEducationCodesMap.size === 0 ? [ApiService.getAllActiveSpecialEdCodes().then((res) => this.setSpecialEducationCodes(res.data))] : [], + ... this.validationIssueTypeCodesMap.size === 0 ? [ApiService.getAllValidationIssueTypeCodes().then((res) => this.setValidationIssueTypeCodes(res.data))] : [], + ... this.programEligibilityCodesMap.size === 0 ? [ApiService.getAllProgramEligibilityTypeCodes().then((res) => this.setProgramEligibilityCodesMap(res.data))]: [], + ... this.zeroFteReasonCodesMap.size === 0 ? [ApiService.getAllZeroFteReasonCodes().then((res) => this.setZeroFteReasonCodesMap(res.data))] : [], ]; return Promise.all(promises); } diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.js index 158302b7f..0e253b6c7 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.js @@ -195,7 +195,10 @@ let object = { SDC_SCHOOL_FUNDING_CODES: sdcRoot + '/school-funding-codes', SDC_SPECIAL_ED_CODES: sdcRoot + '/specialEducation-codes', SDC_SCHOOL_COLLECTION_STUDENT: sdcRoot + '/sdcSchoolCollectionStudent', - SDC_DUPLICATE_RESOLUTION_CODES: sdcRoot + '/duplicate-resolution-codes' + SDC_DUPLICATE_RESOLUTION_CODES: sdcRoot + '/duplicate-resolution-codes', + SDC_VALIDATION_ISSUE_TYPE_CODES: sdcRoot + '/validation-issue-type-codes', + SDC_PROGRAM_ELIGIBILITY_TYPE_CODES: sdcRoot + '/program-eligibility-issue-codes', + SDC_ZERO_FTE_REASON_CODES: sdcRoot + '/zero-fte-reason-codes', } }; diff --git a/frontend/src/utils/sdc/collectionTableConfiguration.js b/frontend/src/utils/sdc/collectionTableConfiguration.js index 26dda6bb2..a61c5d4d6 100644 --- a/frontend/src/utils/sdc/collectionTableConfiguration.js +++ b/frontend/src/utils/sdc/collectionTableConfiguration.js @@ -1,3 +1,702 @@ +/** + * Filters + */ + +export const WARNING_FILTER = Object.freeze( + { + heading: 'Warnings', + id: 'warnings', + multiple: true, + key: 'warnings', + filterOptions: [ + { + title: 'Has Funding Warnings', + id: 'hasFundingWarning', + value: 'FUNDING_WARNING' + }, + { + title: 'Has Info Warnings', + id: 'hasInfoWarning', + value: 'INFO_WARNING' + } + ] + } +); + +export const STUDENT_TYPE_FILTER = Object.freeze( + { + heading: 'Student Type', + id: 'studentType', + multiple: true, + key: 'studentType', + filterOptions: [ + { + title: 'Adult', + id: 'isAdult', + value: 'isAdult' + }, + { + title: 'School Aged', + id: 'isSchoolAged', + value: 'isSchoolAged' + }, + { + title: 'Preschool Aged', + id: 'isUnderSchoolAged', + value: 'isUnderSchoolAged' + } + ] + }, +); + +export const FTE_FILTER = Object.freeze( + { + heading: 'FTE', + id: 'fte', + multiple: true, + key: 'fte', + filterOptions: [ + { + title: 'FTE = 0', + id: 'fteEq0', + value: 'fteEq0' + }, + { + title: 'FTE < 1', + id: 'fteLt1', + value: 'fteLt1' + }, + { + title: 'FTE > 0', + id: 'fteGt0', + value: 'fteGt0' + } + ] + }, +); + +export const FUNDING_TYPE_FILTER = Object.freeze( + { + heading: 'Funding Type', + id: 'fundingtype', + multiple: true, + key: 'fundingType', + filterOptions: [ + { + title: '14 - Out of Province/International', + id: 'funding14', + value: '14' + }, + { + title: '20 - Living on Reserve', + id: 'funding20', + value: '20' + }, + { + title: '16 - Newcomer Refugee', + id: 'funding16', + value: '16' + }, + { + title: 'No Funding Code', + id: 'noFunding', + value: 'No Funding' + } + ] + }, +); + +export const GRADE_FILTER = Object.freeze( + { + heading: 'Grade', + id: 'grade', + multiple: true, + key: 'grade', + filterOptions: [ + { + title: 'Kind. Half', + id: 'gradeKH', + value: 'KH' + }, + { + title: 'Kind. Full', + id: 'gradeKF', + value: 'KF' + }, + { + title: 'Gr. 1', + id: 'grade1', + value: '01' + }, + { + title: 'Gr. 2', + id: 'grade2', + value: '02' + }, + { + title: 'Gr. 3', + id: 'grade3', + value: '03' + }, + { + title: 'Gr. 4', + id: 'grade4', + value: '04' + }, + { + title: 'Gr. 5', + id: 'grade5', + value: '05' + }, + { + title: 'Gr. 6', + id: 'grade6', + value: '06' + }, + { + title: 'Gr. 7', + id: 'grade7', + value: '07' + }, + { + title: 'Elem. Ungraded', + id: 'gradeEU', + value: 'EU' + }, + { + title: 'Gr. 8', + id: 'grade8', + value: '08' + }, + { + title: 'Gr. 9', + id: 'grade9', + value: '09' + }, + { + title: 'Gr. 10', + id: 'grade10', + value: '10' + }, + { + title: 'Gr. 11', + id: 'grade11', + value: '11' + }, + { + title: 'Gr. 12', + id: 'grade12', + value: '12' + }, + { + title: 'Sec. Ungraded', + id: 'gradeSU', + value: 'SU' + }, + { + title: 'Graduated Adult', + id: 'gradeGA', + value: 'GA' + }, + { + title: 'Home School', + id: 'gradeHS', + value: 'HS' + }, + ] + }, +); + +export const SUPPORT_BLOCKS_FILTER = Object.freeze( + { + heading: 'Support Blocks', + id: 'supportblocks', + multiple: false, + key: 'support', + filterOptions: [ + { + title: 'Has Support Blocks', + id: 'hasSupportBlocks', + value: 'hasSupportBlocks' + }, + { + title: 'No Support Blocks', + id: 'noSupportBlocks', + value: 'noSupportBlocks' + } + ] + } +); + +export const FTE_ZERO_FILTER = Object.freeze( + { + heading: 'Reasons for FTE = 0 ', + id: 'zeroFteReasons', + multiple: true, + key: 'fteZero', + filterOptions: [ + { + title: 'Out of Province International ', + id: 'outOfprov', + value: 'OUTOFPROV' + }, + { + title: 'Nominal Roll Eligible', + id: 'nomRoll', + value: 'NOMROLL' + }, + { + title: 'Student Too Young', + id: 'tooYoung', + value: 'TOOYOUNG' + }, + { + title: 'Graduated Adult', + id: 'indyAdult', + value: 'INDYADULT' + }, + { + title: 'No new active courses', + id: 'inactive', + value: 'INACTIVE' + }, + { + title: 'District already received funding', + id: 'distdup', + value: 'DISTDUP' + }, + { + title: 'Authority already received funding', + id: 'authdup', + value: 'AUTHDUP' + } + ] + } +); + +export const FRENCH_PROGRAMS_FILTER = Object.freeze( + { + heading: 'French Programs', + id: 'frenchPrograms', + multiple: true, + key: 'frenchProgram', + filterOptions: [ + { + title: '11 - Early French Immersion', + id: 'french11', + value: '11' + }, + { + title: '14 - Late French Immersion', + id: 'french14', + value: '14' + }, + { + title: '08 - Core French', + id: 'french08', + value: '08' + } + ] + } +); + +export const ENGLISH_PROGRAMS_FILTER = Object.freeze( + { + heading: 'English Language Learning', + id: 'englishPrograms', + multiple: false, + key: 'englishProgram', + filterOptions: [ + { + title: '17 - English Language Learning', + id: 'english17', + value: '17' + }, + { + title: 'No English Language Learning', + id: 'noEnglish17', + value: 'noEnglish17' + } + ] + } +); + +export const FRENCH_FUNDING_FILTER = Object.freeze( + { + heading: 'French Program Funding Eligibility', + id: 'frenchFunding', + multiple: false, + key: 'frenchFunding', + filterOptions: [ + { + title: 'Funding Eligible', + id: 'frenchFundingElig', + value: 'true' + }, + { + title: 'Not Funding Eligible', + id: 'frenchFundingNotElig', + value: 'false' + } + ] + } +); + +export const CAREER_CODE_FILTER = Object.freeze( + { + heading: 'Career Code', + id: 'careerCode', + multiple: true, + key: 'careerCode', + filterOptions: [ + { + title: 'XA - Business & Applied Business', + id: 'codeXA', + value: 'XA' + }, + { + title: 'XB - Fine Arts, Design, & Media', + id: 'codeXB', + value: 'XB' + }, + { + title: 'XC - Fitness & Recreation', + id: 'codeXC', + value: 'XC' + }, + { + title: 'XD - Health & Human Services', + id: 'codeXD', + value: 'XD' + }, + { + title: 'XE - Liberal Arts & Humanities', + id: 'codeXE', + value: 'XE' + }, + { + title: 'XF - Science & Applied Science ', + id: 'codeXF', + value: 'XF' + }, + { + title: 'XG - Tourism, Hospitality, & Foods', + id: 'codeXG', + value: 'XG' + }, + { + title: 'XH - Trades & Technology', + id: 'codeXH', + value: 'XH' + } + ] + }, +); + +export const CAREER_PROGRAM_FILTER = Object.freeze( + { + heading: 'Career Programs', + id: 'careerPrograms', + multiple: true, + key: 'careerPrograms', + filterOptions: [ + { + title: '40 - Career Preparation', + id: 'career40', + value: '40' + }, + { + title: '41 - Co-Operative Education', + id: 'career41', + value: '41' + }, + { + title: '42 - Youth Work in Trades Program', + id: 'career42', + value: '42' + }, + { + title: '43 - Career Technical or Youth Train in Trades', + id: 'career43', + value: '43' + }, + ] + } +); + +export const CAREER_FUNDING_FILTER = Object.freeze( + { + heading: 'Career Program Funding Eligibility', + id: 'careerFunding', + multiple: false, + key: 'careerProgramsFunding', + filterOptions: [ + { + title: 'Funding Eligible', + id: 'isCareerFundingEligible', + value: 'isCareerFundingEligible' + }, + { + title: 'Not Funding Eligible', + id: 'isNotCareerFundingEligible', + value: 'isNotCareerFundingEligible' + } + ] + } +); + +export const INDIGENOUS_PROGRAM_FILTER = Object.freeze( + { + heading: 'Indigenous Support Programs', + id: 'indigenousPrograms', + multiple: true, + key: 'indigenousPrograms', + filterOptions: [ + { + title: '29 - Language & Culture', + id: 'indigenous29', + value: '29' + }, + { + title: '33 - Support Services', + id: 'indigenous33', + value: '33' + }, + { + title: '36 - Other Approved Programs', + id: 'indigenous36', + value: '36' + }, + ] + } +); + +export const ANCESTRY_FILTER = Object.freeze( + { + heading: 'Indigenous Ancestry', + id: 'ancestry', + multiple: false, + key: 'ancestry', + filterOptions: [ + { + title: 'Has Indigenous Ancestry', + id: 'hasIndiAncestry', + value: 'true' + }, + { + title: 'No Indigenous Ancestry', + id: 'hasNoIndAncestry', + value: 'false' + } + ] + } +); + +export const INDIGENOUS_FUNDING_FILTER = Object.freeze( + { + heading: 'Indigenous Support Program Funding Eligibility', + id: 'indigenousFunding', + multiple: false, + key: 'indigenousProgramsFunding', + filterOptions: [ + { + title: 'Funding Eligible', + id: 'indFundingEligible', + value: 'true' + }, + { + title: 'Not Funding Eligible', + id: 'indFundingNotEligible', + value: 'false' + } + ] + } +); + +export const BAND_FILTER = Object.freeze( + { + heading: 'Band of Residence', + id: 'band', + multiple: true, + key: 'bandCode', + filterOptions: [ + { + title: 'Has Band Code', + id: 'hasBandCode', + value: 'true' + }, + { + title: 'No Band Code', + id: 'hasNoBandCode', + value: 'false' + }, + ] + } +); + +export const SPED_FILTER = Object.freeze( + { + heading: 'Special Education', + id: 'sped', + multiple: true, + key: 'sped', + filterOptions: [ + { + title: 'A - Physically Dependent', + id: 'spedA', + value: 'A' + }, + { + title: 'B - Deafblind', + id: 'spedB', + value: 'B' + }, + { + title: 'C - Moderate to Profound Intellectual Disability', + id: 'spedC', + value: 'C' + }, + { + title: 'D - Physical Disability or Chronic Health Impairment', + id: 'spedD', + value: 'D' + }, + { + title: 'E - Visual Impairment', + id: 'spedE', + value: 'E' + }, + { + title: 'F - Deaf or Hard of Hearing', + id: 'spedF', + value: 'F' + }, + { + title: 'G - Autism Spectrum Disorder', + id: 'spedG', + value: 'G' + }, + { + title: 'H - Intensive Behaviour Intervention/Serious Mental Illness', + id: 'spedH', + value: 'H' + }, + { + title: 'K - Mild Intellectual Disability', + id: 'spedK', + value: 'K' + }, + { + title: 'P - Gifted', + id: 'spedP', + value: 'P' + }, + { + title: 'Q - Learning Disability', + id: 'spedQ', + value: 'Q' + }, + { + title: 'R - Moderate Behaviour Support/Mental Illness', + id: 'spedR', + value: 'R' + }, + ] + } +); + +export const SPED_FUNDING_FILTER = Object.freeze( + { + heading: 'Special Education Funding Eligibility', + id: 'spedFunding', + multiple: false, + key: 'spedFunding', + filterOptions: [ + { + title: 'Funding Eligible', + id: 'spedFundingEligible', + value: 'true' + }, + { + title: 'Not Funding Eligible', + id: 'spedFundingNotEligible', + value: 'false' + } + ] + } +); + +export const ELL_FUNDING_FILTER = Object.freeze( + { + heading: 'English Language Learning Funding Eligibility', + id: 'ellFunding', + multiple: false, + key: 'ellFunding', + filterOptions: [ + { + title: 'Funding Eligible', + id: 'ellFundingEligible', + value: 'true' + }, + { + title: 'Not Funding Eligible', + id: 'ellFundingNotEligible', + value: 'false' + } + ] + } +); + +export const REFUGEE_FUNDING_FILTER = Object.freeze( + { + heading: 'Refugee Funding Eligibility', + id: 'refugeeFunding', + multiple: false, + key: 'refugeeFunding', + filterOptions: [ + { + title: 'Funding Eligible', + id: 'refugeeFundingEligible', + value: 'true' + }, + { + title: 'Not Funding Eligible', + id: 'refugeeFundingNotEligible', + value: 'false' + } + ] + } +); + +export const ELL_YEARS_FILTER = Object.freeze( + { + heading: 'Years in ELL', + id: 'ellYears', + multiple: true, + key: 'ellYears', + filterOptions: [ + { + title: '1-5 years in ELL', + id: 'ell1Between5', + value: 'ell1Between5' + }, + { + title: '6+ years in ELL', + id: 'ellGtEq6', + value: 'ellGtEq6' + } + ] + } +); + +export const COURSE_FILTER = Object.freeze( + { + heading: 'Number of Courses', + id: 'numCourses', + key: 'courses' + } +); + export const MONITORING = Object.freeze( { allowedFilters: { @@ -141,3 +840,101 @@ export const NEW_PEN = Object.freeze( ] } ); + +export const FTE = Object.freeze( + { + tableHeaders: [ + { title: 'District', key: 'districtName' }, + { title: 'School', key: 'schoolName' }, + { title: 'Assigned PEN', key: 'assignedPen', subHeader: { title: 'Local ID', key: 'localID' } }, + { title: 'Legal Surname, Given (Middle)', key: 'legalName', subHeader: { title: 'Usual Surname, Given (Middle)', key: 'usualName' } }, + { title: 'Birthdate', key: 'dob', subHeader: { title: 'Gender', key: 'gender' } }, + { title: 'Adult', key: 'isAdult', subHeader: { title: 'Grad', key: 'isGraduated' } }, + { title: 'Grade', key: 'enrolledGradeCode', subHeader: { title: 'Funding Code', key: 'mappedSchoolFunding' } }, + { title: 'Courses For Grad', key: 'mappedNoOfCourses', subHeader: { title: 'Support Blocks', key: 'supportBlocks' } }, + { title: 'Language Program', key: 'mappedLanguageEnrolledProgram', subHeader: { title: 'Years in ELL', key: 'yearsInELL' } }, + { title: 'Career Program', key: 'mappedCareerProgram', subHeader: { title: 'Career Code', key: 'mappedCareerProgramCode' } }, + { title: 'Indigenous Ancestry', key: 'mappedAncestryIndicator', subHeader: { title: 'Band Code', key: 'mappedBandCode' } }, + { title: 'Indigenous Support Program', key: 'mappedIndigenousEnrolledProgram', subHeader: { title: 'Special Education Category', key: 'mappedSpedCode' } }, + ], + summaryReport: [ + { title: 'Eligible Enrolment & Eligible FTE', endpoint:'enrollment'}, + { title: 'Grade Enrolment & FTE per School', endpoint:'grade-enrollment'} + ], + allowedFilters: { + studentType: STUDENT_TYPE_FILTER, + fte: FTE_FILTER, + grade: GRADE_FILTER, + fundingType: FUNDING_TYPE_FILTER, + warnings: WARNING_FILTER, + courses: COURSE_FILTER, + support: SUPPORT_BLOCKS_FILTER, + fteZero: FTE_ZERO_FILTER, + frenchProgram: { + ...FRENCH_PROGRAMS_FILTER, + filterOptions: [ + ...FRENCH_PROGRAMS_FILTER.filterOptions, + { + title: '05 - Programme Francophone', + id: 'french05', + value: '05' + }, + { + title: 'No French Programs', + id: 'noFrenchProgram', + value: 'noFrenchPrograms' + } + ] + }, + englishProgram: ENGLISH_PROGRAMS_FILTER, + ellYears: ELL_YEARS_FILTER, + careerPrograms: { + ...CAREER_PROGRAM_FILTER, + filterOptions: [ + ...CAREER_PROGRAM_FILTER.filterOptions, + { + title: 'No Career Programs', + id: 'noCareerProgram', + value: 'noCareerPrograms' + } + ] + }, + careerCode: { + ...CAREER_CODE_FILTER, + filterOptions: [ + ...CAREER_CODE_FILTER.filterOptions, + { + title: 'No Career Code', + id: 'noCareerCode', + value: 'noCareerCodes' + } + ] + }, + indigenousPrograms: { + ...INDIGENOUS_PROGRAM_FILTER, + filterOptions: [ + ...INDIGENOUS_PROGRAM_FILTER.filterOptions, + { + title: 'No Indigenous Support Programs', + id: 'noIndigenousPrograms', + value: 'noIndigenousPrograms' + } + ] + }, + bandCode: BAND_FILTER, + ancestry: ANCESTRY_FILTER, + sped: { + ...SPED_FILTER, + filterOptions: [ + ...SPED_FILTER.filterOptions, + { + title: 'No Special Education Category', + id: 'noSpedCategory', + value: 'noSpedCode' + } + ] + } + } + } +); + diff --git a/frontend/src/utils/sdc/sdcValidationFieldMappings.js b/frontend/src/utils/sdc/sdcValidationFieldMappings.js new file mode 100644 index 000000000..7e8075348 --- /dev/null +++ b/frontend/src/utils/sdc/sdcValidationFieldMappings.js @@ -0,0 +1,38 @@ +import * as formRules from '../../utils/institute/formRules'; +import {isValidPEN, checkEnrolledProgramLength} from '../../utils/validation'; + +// contains the mappings for validation field errors used in StepTwoValidateData.vue +// type: refers to the type of user input example input => , select => +// label: refers to label in input field. +// key: refers to the property in sdcSchoolCollectionStudent. +// options: various attributes to add into the input fields. further explanation below +// { +// rules: custom rules for input element +// items: only for type select and multiselect refers to the pinia store where the dropdown items reside +// itemValue: only for type select and multiselect refers to where we grab the value +// } + +export const SDC_VALIDATION_FIELD_MAPPINGS = Object.freeze({ + LOCALID: {label: 'Local ID', key: 'localID', type: 'input', options: {maxlength: '12'}}, + STUDENT_PEN: {label: 'PEN', key: 'studentPen', type: 'input', options: {maxlength: '9', rules: [v => isValidPEN(v) || 'Must be a valid PEN']}}, + LEGAL_FIRST_NAME: {label: 'Legal Given', key: 'legalFirstName', type: 'input', options: {maxlength: '255'}}, + LEGAL_MIDDLE_NAMES: {label: 'Legal Middle', key: 'legalMiddleNames', type: 'input', options: {maxlength: '255'}}, + LEGAL_LAST_NAME: {label: 'Legal Surname', key: 'legalLastName', type: 'input', options: {maxlength: '255', rules: [formRules.required()]}}, + USUAL_FIRST_NAME: {label: 'Usual Given', key: 'usualFirstName', type: 'input', options: {maxlength: '255'}}, + USUAL_MIDDLE_NAMES: {label: 'Usual Middle', key: 'usualMiddleNames', type: 'input', options: {maxlength: '255'}}, + USUAL_LAST_NAME: {label: 'Usual Surname', key: 'usualLastName', type: 'input', options: {maxlength: '255'}}, + DOB: {label: 'Birthdate', key: 'dob', type: 'datePicker', options: {rules: [formRules.required()]}}, + GENDER_CODE: {label: 'Gender', key: 'gender', type: 'select', options: {rules: [formRules.required()], items: 'genderCodes', itemValue: 'genderCode'}}, + SPECIAL_EDUCATION_CATEGORY_CODE: {label: 'Special Education Category', key: 'specialEducationCategoryCode', type: 'select', options: {items: 'specialEducationCodes', itemValue: 'specialEducationCategoryCode'}}, + SCHOOL_FUNDING_CODE: {label: 'Funding Code', key: 'schoolFundingCode', type: 'select', options: {items: 'schoolFundingCodes', itemValue: 'schoolFundingCode'}}, + NATIVE_ANCESTRY_IND: {label: 'Indigenous Ancestry', key: 'nativeAncestryInd', type: 'select', options: {rules: [formRules.required()], items: 'ancestryItems', itemValue:'code'}}, + HOME_LANGUAGE_SPOKEN_CODE: {label: 'Home Language Spoken Code', key: 'homeLanguageSpokenCode', type: 'select', options: {items: 'homeLanguageSpokenCodes', itemValue: 'homeLanguageSpokenCode'}}, + OTHER_COURSES: {label: 'Other Courses', key: 'otherCourses', type: 'select', options: {items: 'otherCoursesValidNumbers', itemValue: 'otherCourses'}}, + SUPPORT_BLOCKS: {label: 'Support Blocks', key: 'supportBlocks', type: 'select', options: {items: 'supportBlocksValidNumbers', itemValue: 'supportBlocks'}}, + ENROLLED_GRADE_CODE: {label: 'Enrolled Grade Codes', key: 'enrolledGradeCode', type: 'select', options: {items: 'enrolledGradeCodes', itemValue: 'enrolledGradeCode'}}, + ENROLLED_PROGRAM_CODE: {label: 'Program Codes', key: 'filteredEnrolledProgramCodes', type: 'multiselect', options: {rules:[v => checkEnrolledProgramLength(v) || 'Select a maximum of 8 Enrolled Programs'], items: 'enrolledProgramCodes', itemValue: 'enrolledProgramCode'}}, + CAREER_PROGRAM_CODE: {label: 'Career Code', key: 'careerProgramCode', type: 'select', options: {items: 'careerProgramCodes', itemValue: 'careerProgramCode'}}, + NUMBER_OF_COURSES: {label: 'Number of Courses', key: 'numberOfCourses', type: 'select', options: {maxlength: '4'}}, + BAND_CODE: {label: 'Band Codes', key: 'bandCode', type: 'select', options: {items: 'bandCodes', itemValue: 'bandCode'}}, + POSTAL_CODE: {label: 'Postal Code', key: 'postalCode', type: 'input', options: {maxlength: '6'}} +}); diff --git a/frontend/src/utils/validation.js b/frontend/src/utils/validation.js index 988377e79..66c2b8367 100644 --- a/frontend/src/utils/validation.js +++ b/frontend/src/utils/validation.js @@ -159,3 +159,10 @@ export function isValidNumber(evt) { export function isValidEmail(value) { return !!(value && /^(?=[A-Za-z0-9][A-Za-z0-9@._%+-]{5,253}$)[A-Za-z0-9._%+-]{1,64}@(?:(?=[A-Za-z0-9-]{1,63}\.)[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*\.){1,8}[A-Za-z]{2,63}$/.test(value)); } + +export function checkEnrolledProgramLength(v) { + if(v === null || v.length === 0) { + return true; + } + return v.length > 0 && v.length <= 8; +} \ No newline at end of file