Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First commit for tasks: EAC-8 & EAC-12. #2072

Merged
merged 5 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
179 changes: 179 additions & 0 deletions frontend/src/components/assessments/AssessmentSessions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<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 {
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
Loading