Skip to content

Commit

Permalink
Merge pull request #2072 from bcgov/feature/EAC-8
Browse files Browse the repository at this point in the history
First commit for tasks: EAC-8 & EAC-12.
  • Loading branch information
sumathi-thirumani authored Oct 16, 2024
2 parents 44e159a + 0ea2041 commit c53a333
Show file tree
Hide file tree
Showing 15 changed files with 575 additions and 10 deletions.
2 changes: 2 additions & 0 deletions backend/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const edxRouter = require('./routes/edx-router');
const instituteRouter = require('./routes/institute');
const sdcRouter = require('./routes/sdc');
const cacheRouter = require('./routes/cache-router');
const easRouter = require('./routes/eas');
const promMid = require('express-prometheus-middleware');
const Redis = require('./util/redis/redis-client');
Redis.init(); // call the init to initialize appropriate client, and reuse it across the app.
Expand Down Expand Up @@ -214,6 +215,7 @@ apiRouter.use('/edx', edxRouter);
apiRouter.use('/institute', instituteRouter);
apiRouter.use('/sdc', sdcRouter);
apiRouter.use('/cache', cacheRouter);
apiRouter.use('/eas', easRouter);
// Prevent unhandled errors from crashing application
process.on('unhandledRejection', err => {
log.error(err.stack);
Expand Down
43 changes: 43 additions & 0 deletions backend/src/components/eas/eas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';
const { logApiError, getData, errorResponse, handleExceptionResponse } = require('../utils');
const HttpStatus = require('http-status-codes');
const utils = require('../utils');

const config = require('../../config');

async function getAssessmentSessions(req, res) {
try {
const url = `${config.get('server:eas:assessmentSessionsURL')}`;
const data = await getData(url);
return res.status(200).json(data);
} catch (e) {
logApiError(e, 'getAssessmentSessions', 'Error occurred while attempting to GET assessment sessions.');
return handleExceptionResponse(e, res);
}
}

async function updateAssessmentSession(req, res) {
if (req.params.sessionID !== req.body.sessionID) {
return res.status(HttpStatus.BAD_REQUEST).json({
message: 'The sessionID in the URL didn\'t match the sessionID in the request body.'
});
}
try {
const userInfo = utils.getUser(req);
const payload = {
sessionID: req.body.sessionID,
activeFromDate: req.body.activeFromDate,
activeUntilDate: req.body.activeUntilDate,
updateUser: userInfo.idir_username
};
const result = await utils.putData(`${config.get('server:eas:assessmentSessionsURL')}/${req.body.sessionID}`, payload, utils.getUser(req).idir_username);
return res.status(HttpStatus.OK).json(result);
} catch (e) {
logApiError(e, 'updateAssessmentSession', 'Error occurred while attempting to save the changes to the assessment session.');
return errorResponse(res);
}
}
module.exports = {
getAssessmentSessions,
updateAssessmentSession
};
4 changes: 2 additions & 2 deletions backend/src/components/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ function formatNumberOfCourses(value) {
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') {
} else if(e.message === '403' || e.status === 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'){
} else if(e.message === '401' || e.status === 401){
return res.status(HttpStatus.UNAUTHORIZED).json({
status: HttpStatus.UNAUTHORIZED,
message: 'Token is not valid'
Expand Down
7 changes: 6 additions & 1 deletion backend/src/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,8 @@ nconf.defaults({
bannerColor: process.env.BANNER_COLOR,
webSocketURL: process.env.WEB_SOCKET_URL,
disableSdcFunctionality: process.env.DISABLE_SDC_FUNCTIONALITY === 'true',
edxURL: process.env.EDX_URL
edxURL: process.env.EDX_URL,
disableEASFunctionality: process.env.DISABLE_EAS_FUNCTIONALITY ? process.env.DISABLE_EAS_FUNCTIONALITY === 'true' : true
},
sdc: {
rootURL: process.env.SDC_API_URL,
Expand Down Expand Up @@ -214,6 +215,10 @@ nconf.defaults({
programEligibilityTypeCodesURL: process.env.SDC_API_URL + '/program-eligibility-issue-codes',
zeroFteReasonCodesURL: process.env.SDC_API_URL + '/zero-fte-reason-codes',
sdcDuplicateURL: process.env.SDC_API_URL + '/sdc-duplicate'
},
eas:{
rootURL: process.env.EAS_URL,
assessmentSessionsURL: process.env.EAS_URL+ '/sessions',
}
});
module.exports = nconf;
3 changes: 2 additions & 1 deletion backend/src/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ async function getConfig(req, res) {
BANNER_COLOR: frontendConfig.bannerColor,
WEB_SOCKET_URL: frontendConfig.webSocketURL,
DISABLE_SDC_FUNCTIONALITY: frontendConfig.disableSdcFunctionality,
EDX_URL: frontendConfig.edxURL
EDX_URL: frontendConfig.edxURL,
DISABLE_EAS_FUNCTIONALITY: 'disableEASFunctionality' in frontendConfig ? frontendConfig.disableEASFunctionality : true
};
return res.status(HttpStatus.OK).json(frontConfig);
}
Expand Down
15 changes: 15 additions & 0 deletions backend/src/routes/eas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const passport = require('passport');
const express = require('express');
const router = express.Router();
const { getAssessmentSessions, updateAssessmentSession } = require('../components/eas/eas');
const utils = require('../components/utils');
const extendSession = utils.extendSession();
const permUtils = require('../components/permissionUtils');
const perm = require('../util/Permission');

const PERMISSION = perm.PERMISSION;

router.get('/assessment-sessions', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.MANAGE_EAS_SESSIONS_PERMISSION), extendSession, getAssessmentSessions);
router.put('/assessment-sessions/:sessionID', passport.authenticate('jwt', {session: false}, undefined), permUtils.checkUserHasPermission(PERMISSION.MANAGE_EAS_SESSIONS_PERMISSION), extendSession, updateAssessmentSession);

module.exports = router;
3 changes: 2 additions & 1 deletion backend/src/util/Permission.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ const PERMISSION = Object.freeze(
STUDENT_DATA_COLLECTION: 'STUDENT_DATA_COLLECTION',
REPORTS_SDC_PUBLIC_SCHOOLS_PERMISSION: 'REPORTS_SDC_PUBLIC_SCHOOLS_PERMISSION',
REPORTS_SDC_INDEPENDENT_SCHOOLS_PERMISSION: 'REPORTS_SDC_INDEPENDENT_SCHOOLS_PERMISSION',
REPORTS_SDC_HEADCOUNTS_PERMISSION: 'REPORTS_SDC_HEADCOUNTS_PERMISSION'
REPORTS_SDC_HEADCOUNTS_PERMISSION: 'REPORTS_SDC_HEADCOUNTS_PERMISSION',
MANAGE_EAS_SESSIONS_PERMISSION:'MANAGE_EAS_SESSIONS_PERMISSION'
}
);

Expand Down
180 changes: 180 additions & 0 deletions frontend/src/components/assessments/AssessmentSessions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<template>
<v-container class="containerSetup mb-5">
<v-row class="pr-4">
<v-col class="pb-0 mt-4">
<h2>Open Assessment Sessions</h2>
</v-col>
</v-row>
<v-row
v-for="(sessions, index) in activeSessions"
:key="index"
>
<v-col
v-for="session in sessions"
:key="session.sessionid"
cols="5"
>
<SessionCard
:session="session"
:handle-open-editor="() => openEditSessionSheet(session)"
/>
</v-col>
</v-row>
<v-divider class="py-6 mt-6" />
<v-row>
<v-icon icon="mdi-history pt-3" />
<h2 class="pl-2">Assessment Session History</h2>
</v-row>
<v-row>
<v-data-table
id="session-history-dataTable"
v-model:items-per-page="itemsPerPage"
:page="pageNumber"
:items="historicalSessions"
:items-length="historicalSessions?.length"
:search="search"
:headers="headers"
:items-per-page-options="[
{ value: 5, title: '5' },
{ value: 10, title: '10' },
{ value: 25, title: '25' },
{ value: 50, title: '50' },
{ value: 100, title: '100' },
{ value: -1, title: 'All' }
]"
:hover="true"
class="fill-height"
style="border-radius: 0"
>
<template #top>
<v-text-field
v-model="search"
clearable
hide-details="auto"
label="Search"
class="pa-4"
/>
</template>
</v-data-table>
</v-row>
<v-dialog
v-model="editSessionSheet"
:inset="true"
:no-click-animation="true"
:scrollable="true"
:persistent="true"
max-width="40%"
min-height="35%"
>
<EditSession
v-if="editSessionSheet"
:session="editSession"
:on-success-handler="sessionEditSuccess"
:close-handler="() => (editSessionSheet = false)"
/>
</v-dialog>
</v-container>
</template>

<script>
import SessionCard from './sessions/SessionCard.vue';
import EditSession from './sessions/SessionEdit.vue';
import ApiService from '../../common/apiService';
import { Routes } from '../../utils/constants';
import { LocalDate } from '@js-joda/core';
import moment from 'moment';
export default {
name: 'AssessmentSessions',
components: {
SessionCard,
EditSession,
},
data() {
return {
topN: 4,
search: null,
currentYear: LocalDate.now().year(),
itemsPerPage: 5,
pageNumber: 1,
allsessions: [],
headers: [
{ title: 'Session ID', key: 'courseSession' },
{ title: 'Month', key: 'courseMonth' },
{ title: 'Year', key: 'courseYear' },
{ title: 'Open Date', key: 'activeFromDate' },
{ title: 'Close Date', key: 'activeUntilDate' },
],
editSessionSheet: false,
editSession: null,
headerSearchParams: {},
headerSortParams: {},
};
},
computed: {
activeSessions() {
const orderedSessions = [];
const allsessions = this.allsessions
.filter((session, index) => index < this.topN)
.map((session) => {
return {
...session,
courseMonth: this.formatMonth(session.courseMonth)
};
});
allsessions.sort((a, b) => a.courseSession - b.courseSession);
for (let i = 0; i < allsessions.length; i += 2) {
orderedSessions.push(allsessions.slice(i, i + 2));
}
return orderedSessions;
},
historicalSessions() {
return this.allsessions
.filter((session, index) => index >= this.topN)
.map((session) => {
return {
...session,
activeFromDate: this.formattoDate(session.activeFromDate),
activeUntilDate: this.formattoDate(session.activeUntilDate),
courseMonth: this.formatMonth(session.courseMonth),
};
});
},
sessionHeaderSlotName() {
return `column.${this.sessionid}`;
},
},
created() {
this.getAllAssessmentSessions();
},
methods: {
getAllAssessmentSessions() {
this.loading = true;
ApiService.apiAxios
.get(`${Routes.eas.GET_ASSESSMENT_SESSIONS}`, {})
.then((response) => {
this.allsessions = response.data;
})
.catch((error) => {
console.error(error);
})
.finally(() => {
this.loading = false;
});
},
sessionEditSuccess() {
this.getAllAssessmentSessions();
},
openEditSessionSheet(session) {
this.editSession = session;
this.editSessionSheet = !this.editSessionSheet;
},
formattoDate(date) {
return moment(JSON.stringify(date), 'YYYY-MM-DDTHH:mm:ss').format('YYYY/MM/DD');
},
formatMonth(month) {
return moment(month, 'MM').format('MMMM');
}
},
};
</script>
Loading

0 comments on commit c53a333

Please sign in to comment.