Skip to content

Commit

Permalink
First commit for tasks: EAC-8 & EAC-12.
Browse files Browse the repository at this point in the history
  • Loading branch information
sumathi-thirumani committed Oct 8, 2024
1 parent b75eb8c commit c89a691
Show file tree
Hide file tree
Showing 14 changed files with 564 additions and 8 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 } = 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 errorResponse(res);
}
}

async function updateAssessmentSession(req, res) {
if (req.params.assessmentSessionID !== req.body.assessmentSessionID) {
return res.status(HttpStatus.BAD_REQUEST).json({
message: 'The assessmentSessionID in the URL didn\'t match the assessmentSessionID in the request body.'
});
}
try {
const userInfo = utils.getUser(req);
const payload = {
assessmentSessionID: req.body.assessmentSessionID,
activeFromDate: req.body.activeFromDate,
activeUntilDate: req.body.activeUntilDate,
updateUser: userInfo.idir_username
};
const result = await utils.putData(`${config.get('server:eas:assessmentSessionsURL')}/${req.body.assessmentSessionID}`, 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
};
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 === '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/:assessmentSessionID', 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
171 changes: 171 additions & 0 deletions frontend/src/components/assessments/AssessmentSessions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<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"
: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 {
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) => session.status === 'OPEN')
.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) => session.status !== 'OPEN')
.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>
97 changes: 97 additions & 0 deletions frontend/src/components/assessments/sessions/SessionCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<template>
<v-card
:id="`sessioncard-${session.sessionid}`"
fluid
class="d-flex flex-column mt-4"
height="100%"
>
<v-card-title class="text-wrap pb-0" >
<v-row no-gutters class="pr-4">
<v-col>
<strong class="sessionName"> {{ session.courseMonth }} {{ session.courseYear }} Session </strong>
</v-col>
<v-col cols="1" style="float:right;">
<a class="ml-2" @click="handleOpenEditor">
<v-icon
class="edit-session-small"
color="#1A5A96"
:icon="'mdi-pencil'"
/>
</a>
</v-col>
</v-row>
</v-card-title>
<v-card-text class="mt-2 ml-2 mr-2">
<v-list class="pt-0">
<v-list-item min-height="inherit" class="pl-0">
<v-row class="dates">
<v-col cols="8">
<v-icon small class="mr-1">mdi-calendar</v-icon>
<span id="opendatelabel">Registration Open Date:</span>
</v-col>

<v-col cols="4">
<span id="opendate">
{{ formattoDate(session.activeFromDate) }}
</span>
</v-col>
</v-row>
</v-list-item>
<v-list-item min-height="inherit" class="pl-0">
<v-row>
<v-col cols="8">
<v-icon small class="mr-1">mdi-calendar</v-icon>
<span id="closedatelabel">Registration Close Date:</span>
</v-col>
<v-col cols="4">
<span id="closedate">
{{ formattoDate(session.activeUntilDate) }}
</span>
</v-col>
</v-row>
</v-list-item>
</v-list>
</v-card-text>
<v-spacer />
</v-card>
</template>

<script>
import moment from 'moment';
export default {
name: 'SessionCard',
props: {
session: {
type: Object,
required: true,
},
handleOpenEditor: {
type: Function,
required: true,
},
},
methods: {
formattoDate(date) {
return moment(JSON.stringify(date), 'YYYY-MM-DDTHH:mm:ss').format('YYYY/MM/DD');
},
},
};
</script>

<style scoped>
.dateSubText {
font-style: italic;
font-size: 0.95em;
}
.sessionName {
font-size: 1em;
}
.edit-session-small {
font-size: 25px;
margin-top: -5px;
float: right;
}
</style>
Loading

0 comments on commit c89a691

Please sign in to comment.