diff --git a/services/backend/src/config/organizationConstants.ts b/services/backend/src/config/organizationConstants.ts index 72eccc89c2..1c7c2ce3c4 100644 --- a/services/backend/src/config/organizationConstants.ts +++ b/services/backend/src/config/organizationConstants.ts @@ -1,4 +1,4 @@ -// Linter was not having any of this so neeeded to add resolveJsonModule:true to tsconfig +// Linter was not having any of this so needed to add resolveJsonModule:true to tsconfig // export const { facultyCodes, ignoredFacultyCodes, magicFacultyCode } = require('../environment/organizationConfig.json') export { facultyCodes, ignoredFacultyCodes, magicFacultyCode } from '../environment/organizationConfig.json' diff --git a/services/backend/src/events.ts b/services/backend/src/events.ts index 15172246e5..2d43271e4e 100644 --- a/services/backend/src/events.ts +++ b/services/backend/src/events.ts @@ -8,8 +8,8 @@ import { computeLanguageCenterData, LANGUAGE_CENTER_REDIS_KEY } from './services import { findStudentsCloseToGraduation, CLOSE_TO_GRADUATION_REDIS_KEY } from './services/populations/closeToGraduation' import { redisClient } from './services/redis' import { getCurrentSemester } from './services/semesters' -import { combinedStudyprogrammes, isRelevantProgramme } from './services/studyProgramme/studyProgrammeHelpers' -import { updateBasicView, updateStudytrackView } from './services/studyProgramme/studyProgrammeUpdates' +import { combinedStudyProgrammes, isRelevantProgramme } from './services/studyProgramme/studyProgrammeHelpers' +import { updateBasicView, updateStudyTrackView } from './services/studyProgramme/studyProgrammeUpdates' import { findAndSaveTeachers } from './services/teachers/top' import { deleteOutdatedUsers } from './services/userService' import logger from './util/logger' @@ -61,10 +61,10 @@ const refreshProgrammesAndFaculties = async () => { export const refreshProgramme = async (code: string) => { await updateBasicView(code, '') - await updateStudytrackView(code, '') - const combinedProgramme = combinedStudyprogrammes[code] || '' + await updateStudyTrackView(code, '') + const combinedProgramme = combinedStudyProgrammes[code] || '' await updateBasicView(code, combinedProgramme) - await updateStudytrackView(code, combinedProgramme) + await updateStudyTrackView(code, combinedProgramme) } export const refreshProgrammes = async () => { diff --git a/services/backend/src/routes/studyProgramme.ts b/services/backend/src/routes/studyProgramme.ts index 5966c3a6e9..93af236529 100644 --- a/services/backend/src/routes/studyProgramme.ts +++ b/services/backend/src/routes/studyProgramme.ts @@ -5,8 +5,8 @@ import { setBasicStats, getGraduationStats, setGraduationStats, - getStudytrackStats, - setStudytrackStats, + getStudyTrackStats, + setStudyTrackStats, } from '../services/analyticsService' import { getCreditsProduced } from '../services/providerCredits' import { getProgrammeName } from '../services/studyProgramme' @@ -15,9 +15,9 @@ import { getStudyProgrammeCoursesForStudyTrack, getStudyProgrammeStatsForColorizedCoursesTable, } from '../services/studyProgramme/studyProgrammeCourses' -import { getGraduationStatsForStudytrack } from '../services/studyProgramme/studyProgrammeGraduations' -import { updateBasicView, updateStudytrackView } from '../services/studyProgramme/studyProgrammeUpdates' -import { getStudyRightsInProgramme } from '../services/studyProgramme/studyRightFinders' +import { getGraduationStatsForStudyTrack } from '../services/studyProgramme/studyProgrammeGraduations' +import { updateBasicView, updateStudyTrackView } from '../services/studyProgramme/studyProgrammeUpdates' +import { getStudyRightsInProgramme, getStudyTracksForProgramme } from '../services/studyProgramme/studyRightFinders' import { getStudyTrackStatsForStudyProgramme } from '../services/studyProgramme/studyTrackStats' import { Graduated, SpecialGroups, YearType } from '../types' import logger from '../util/logger' @@ -89,7 +89,7 @@ router.get('/:id/graduationstats', async (req: GetStatsRequest, res: Response) = return res.json(data) } - const updatedStats = await getGraduationStatsForStudytrack({ + const updatedStats = await getGraduationStatsForStudyTrack({ studyProgramme: code, combinedProgramme, settings: { @@ -142,7 +142,7 @@ router.get('/:id/studytrackstats', async (req: GetStudyTrackStatsRequest, res: R } logInfoForGrafana(code, combinedProgramme) - const data = await getStudytrackStats(code, combinedProgramme, graduated, specialGroups) + const data = await getStudyTrackStats(code, combinedProgramme, graduated, specialGroups) if (data) { return res.json(data) } @@ -158,7 +158,7 @@ router.get('/:id/studytrackstats', async (req: GetStudyTrackStatsRequest, res: R studyRightsOfProgramme, }) if (updated) { - await setStudytrackStats(updated, graduated, specialGroups) + await setStudyTrackStats(updated, graduated, specialGroups) } return res.json(updated) }) @@ -173,6 +173,19 @@ router.get('/:id/colorizedtablecoursestats', async (req: Request, res: Response) } }) +router.get('/:id/studytracks', async (req: Request, res: Response) => { + const code = req.params.id + if (!code) { + return res.status(422).end() + } + try { + const data = await getStudyTracksForProgramme(code) + return res.json(data) + } catch (error) { + logger.error({ message: `Failed to get study tracks for study programme ${code}`, meta: `${error}` }) + } +}) + interface GetUpdateViewRequest extends Request { query: { combined_programme: string @@ -202,7 +215,7 @@ router.get('/:id/update_studytrackview', async (req: GetUpdateViewRequest, res: return res.status(400).json({ error: 'Missing code' }) } try { - const result = await updateStudytrackView(code, combinedProgramme) + const result = await updateStudyTrackView(code, combinedProgramme) return res.json(result) } catch (error) { const message = `Failed to update study track stats for programme ${code}${combinedProgramme ? `+${combinedProgramme}` : ''}` @@ -229,7 +242,7 @@ router.get('/:id/evaluationstats', async (req: GetEvaluationStatsRequest, res: R const combinedProgramme = '' let gradData = await getGraduationStats(code, combinedProgramme, yearType, specialGroups) if (!gradData) { - const updatedStats = await getGraduationStatsForStudytrack({ + const updatedStats = await getGraduationStatsForStudyTrack({ studyProgramme: code, combinedProgramme, settings: { @@ -243,7 +256,7 @@ router.get('/:id/evaluationstats', async (req: GetEvaluationStatsRequest, res: R } } - let progressData = await getStudytrackStats(code, combinedProgramme, graduated, specialGroups) + let progressData = await getStudyTrackStats(code, combinedProgramme, graduated, specialGroups) if (!progressData) { const studyRightsOfProgramme = await getStudyRightsInProgramme(code, false, true) const updated = await getStudyTrackStatsForStudyProgramme({ @@ -256,7 +269,7 @@ router.get('/:id/evaluationstats', async (req: GetEvaluationStatsRequest, res: R studyRightsOfProgramme, }) if (updated) { - await setStudytrackStats(updated, graduated, specialGroups) + await setStudyTrackStats(updated, graduated, specialGroups) progressData = updated } } diff --git a/services/backend/src/services/analyticsService.ts b/services/backend/src/services/analyticsService.ts index 4939c0ea2a..d5d92be55b 100644 --- a/services/backend/src/services/analyticsService.ts +++ b/services/backend/src/services/analyticsService.ts @@ -17,7 +17,7 @@ const createRedisKeyForCreditStats = (id: string, yearType: YearType, specialGro const createRedisKeyForGraduationStats = (id: string, yearType: YearType, specialGroups: SpecialGroups) => { return `GRADUATION_STATS_${id}_${yearType}_${specialGroups}` } -const createRedisKeyForStudytrackStats = (id: string, graduated: Graduated, specialGroups: SpecialGroups) => { +const createRedisKeyForStudyTrackStats = (id: string, graduated: Graduated, specialGroups: SpecialGroups) => { return `STUDYTRACK_STATS_${id}_${graduated}_${specialGroups}` } @@ -34,8 +34,8 @@ export const getBasicStats = async ( yearType: YearType, specialGroups: SpecialGroups ) => { - const searchkey = combinedProgramme ? `${id}-${combinedProgramme}` : id - const redisKey = createRedisKeyForBasicStats(searchkey, yearType, specialGroups) + const searchKey = combinedProgramme ? `${id}-${combinedProgramme}` : id + const redisKey = createRedisKeyForBasicStats(searchKey, yearType, specialGroups) const dataFromRedis = await redisClient.get(redisKey) if (!dataFromRedis) { return null @@ -102,8 +102,8 @@ export const getGraduationStats = async ( yearType: YearType, specialGroups: SpecialGroups ) => { - const searchkey = combinedProgramme ? `${id}-${combinedProgramme}` : id - const redisKey = createRedisKeyForGraduationStats(searchkey, yearType, specialGroups) + const searchKey = combinedProgramme ? `${id}-${combinedProgramme}` : id + const redisKey = createRedisKeyForGraduationStats(searchKey, yearType, specialGroups) const dataFromRedis = await redisClient.get(redisKey) if (!dataFromRedis) { return null @@ -129,14 +129,14 @@ export const setGraduationStats = async (data, yearType: YearType, specialGroups return dataToRedis } -export const getStudytrackStats = async ( +export const getStudyTrackStats = async ( id: string, combinedProgramme: string | null, graduated: Graduated, specialGroups: SpecialGroups ) => { - const searchkey = combinedProgramme ? `${id}-${combinedProgramme}` : id - const redisKey = createRedisKeyForStudytrackStats(searchkey, graduated, specialGroups) + const searchKey = combinedProgramme ? `${id}-${combinedProgramme}` : id + const redisKey = createRedisKeyForStudyTrackStats(searchKey, graduated, specialGroups) const dataFromRedis = await redisClient.get(redisKey) if (!dataFromRedis) { return null @@ -144,9 +144,9 @@ export const getStudytrackStats = async ( return JSON.parse(dataFromRedis) } -export const setStudytrackStats = async (data, graduated: Graduated, specialGroups: SpecialGroups) => { +export const setStudyTrackStats = async (data, graduated: Graduated, specialGroups: SpecialGroups) => { const { id } = data - const redisKey = createRedisKeyForStudytrackStats(id, graduated, specialGroups) + const redisKey = createRedisKeyForStudyTrackStats(id, graduated, specialGroups) const dataToRedis = { ...data, status: 'DONE', diff --git a/services/backend/src/services/faculty/facultyGraduationTimes.ts b/services/backend/src/services/faculty/facultyGraduationTimes.ts index 0c41453787..9b4ab9209f 100644 --- a/services/backend/src/services/faculty/facultyGraduationTimes.ts +++ b/services/backend/src/services/faculty/facultyGraduationTimes.ts @@ -1,8 +1,8 @@ import { cloneDeep, omit } from 'lodash' import { DegreeProgrammeType, Name } from '../../types' -import { getGraduationStats, getStudytrackStats, setGraduationStats, setStudytrackStats } from '../analyticsService' -import { getGraduationStatsForStudytrack, GraduationTimes } from '../studyProgramme/studyProgrammeGraduations' +import { getGraduationStats, getStudyTrackStats, setGraduationStats, setStudyTrackStats } from '../analyticsService' +import { getGraduationStatsForStudyTrack, GraduationTimes } from '../studyProgramme/studyProgrammeGraduations' import { getMedian } from '../studyProgramme/studyProgrammeHelpers' import { getStudyRightsInProgramme } from '../studyProgramme/studyRightFinders' import { @@ -25,7 +25,7 @@ export const programmeTypes = { } as const const getStatsByGraduationYear = async (facultyProgrammes: ProgrammesOfOrganization) => { - const newStats: Array>> = [] + const newStats: Array>> = [] const medians: Record = {} const programmes: { medians: Record> } = { medians: {} } @@ -38,7 +38,7 @@ const getStatsByGraduationYear = async (facultyProgrammes: ProgrammesOfOrganizat newStats.push(statsFromRedis) continue } - const updatedStats = await getGraduationStatsForStudytrack({ + const updatedStats = await getGraduationStatsForStudyTrack({ studyProgramme: programme.code, combinedProgramme: '', settings: { @@ -136,7 +136,7 @@ const getStatsByStartYear = async (facultyProgrammes: ProgrammesOfOrganization) if (programme.degreeProgrammeType == null || !(programme.degreeProgrammeType in programmeTypes)) { continue } - const statsFromRedis = await getStudytrackStats(programme.code, '', 'GRADUATED_INCLUDED', 'SPECIAL_EXCLUDED') + const statsFromRedis = await getStudyTrackStats(programme.code, '', 'GRADUATED_INCLUDED', 'SPECIAL_EXCLUDED') if (statsFromRedis) { newStats.push(statsFromRedis) continue @@ -152,7 +152,7 @@ const getStatsByStartYear = async (facultyProgrammes: ProgrammesOfOrganization) }, studyRightsOfProgramme, }) - await setStudytrackStats(updatedStats, 'GRADUATED_INCLUDED', 'SPECIAL_EXCLUDED') + await setStudyTrackStats(updatedStats, 'GRADUATED_INCLUDED', 'SPECIAL_EXCLUDED') newStats.push(updatedStats) } diff --git a/services/backend/src/services/faculty/facultyStudentProgress.ts b/services/backend/src/services/faculty/facultyStudentProgress.ts index 8ce9e1f28e..c94e65b694 100644 --- a/services/backend/src/services/faculty/facultyStudentProgress.ts +++ b/services/backend/src/services/faculty/facultyStudentProgress.ts @@ -3,7 +3,7 @@ import moment from 'moment' import { rootOrgId } from '../../config' import { Graduated, SpecialGroups, Unarray } from '../../types' -import { getStudytrackStats, setStudytrackStats } from '../analyticsService' +import { getStudyTrackStats, setStudyTrackStats } from '../analyticsService' import { getYearsArray } from '../studyProgramme/studyProgrammeHelpers' import { getStudyRightsInProgramme } from '../studyProgramme/studyRightFinders' import { getStudyTrackStatsForStudyProgramme } from '../studyProgramme/studyTrackStats' @@ -114,7 +114,7 @@ export const combineFacultyStudentProgress = async ( ) { continue } - const statsFromRedis = await getStudytrackStats(studyProgramme, null, graduated, specialGroups) + const statsFromRedis = await getStudyTrackStats(studyProgramme, null, graduated, specialGroups) if (statsFromRedis) { statsOfProgrammes.push(statsFromRedis) } else { @@ -128,7 +128,7 @@ export const combineFacultyStudentProgress = async ( studyRightsOfProgramme, }) statsOfProgrammes.push(updatedStats) - setStudytrackStats(updatedStats, graduated, specialGroups) + setStudyTrackStats(updatedStats, graduated, specialGroups) } } diff --git a/services/backend/src/services/faculty/facultyStudents.ts b/services/backend/src/services/faculty/facultyStudents.ts index f64e65cac3..f69a505113 100644 --- a/services/backend/src/services/faculty/facultyStudents.ts +++ b/services/backend/src/services/faculty/facultyStudents.ts @@ -1,5 +1,5 @@ import { Unarray } from '../../types' -import { getStudytrackStats, setStudytrackStats } from '../analyticsService' +import { getStudyTrackStats, setStudyTrackStats } from '../analyticsService' import { getPercentage, tableTitles } from '../studyProgramme/studyProgrammeHelpers' import { getStudyRightsInProgramme } from '../studyProgramme/studyRightFinders' import { getStudyTrackStatsForStudyProgramme } from '../studyProgramme/studyTrackStats' @@ -65,7 +65,7 @@ export const combineFacultyStudents = async ( const newStats: StudyTrackStats[] = [] for (const studyProgramme of programmeCodes) { - const statsFromRedis = await getStudytrackStats(studyProgramme, null, graduated, specialGroups) + const statsFromRedis = await getStudyTrackStats(studyProgramme, null, graduated, specialGroups) if (statsFromRedis) { newStats.push(statsFromRedis) if (!years.length) { @@ -82,7 +82,7 @@ export const combineFacultyStudents = async ( }, studyRightsOfProgramme, }) - setStudytrackStats(updatedStats, graduated, specialGroups) + setStudyTrackStats(updatedStats, graduated, specialGroups) if (!years.length) { years = updatedStats.years } diff --git a/services/backend/src/services/faculty/facultyThesisWriters.ts b/services/backend/src/services/faculty/facultyThesisWriters.ts index 6ad4b912a7..a9bb6f7296 100644 --- a/services/backend/src/services/faculty/facultyThesisWriters.ts +++ b/services/backend/src/services/faculty/facultyThesisWriters.ts @@ -2,7 +2,7 @@ import { cloneDeep } from 'lodash' import { DegreeProgrammeType } from '../../types' import { getGraduationStats, setGraduationStats } from '../analyticsService' -import { getGraduationStatsForStudytrack } from '../studyProgramme/studyProgrammeGraduations' +import { getGraduationStatsForStudyTrack } from '../studyProgramme/studyProgrammeGraduations' import { getDegreeProgrammesOfFaculty, ProgrammesOfOrganization } from './faculty' type ProgrammeName = { @@ -25,7 +25,7 @@ const calculateCombinedStats = async ( ] const programmeTableStats: Record>> = {} - const newStats: Array>> = [] + const newStats: Array>> = [] const combinedTableStats: Array> = [] let years: Array = [] const programmeNames: Record = {} @@ -47,7 +47,7 @@ const calculateCombinedStats = async ( newStats.push(statsFromRedis) continue } - const updatedStats = await getGraduationStatsForStudytrack({ + const updatedStats = await getGraduationStatsForStudyTrack({ studyProgramme: programme.code, combinedProgramme: '', settings: { diff --git a/services/backend/src/services/programmeModules.ts b/services/backend/src/services/programmeModules.ts index 74f4aeb9db..bcc338e4c6 100644 --- a/services/backend/src/services/programmeModules.ts +++ b/services/backend/src/services/programmeModules.ts @@ -5,7 +5,7 @@ import { ProgrammeModule } from '../models' import { ExcludedCourse } from '../models/kone' import { Name } from '../types' import logger from '../util/logger' -import { combinedStudyprogrammes } from './studyProgramme/studyProgrammeHelpers' +import { combinedStudyProgrammes } from './studyProgramme/studyProgrammeHelpers' export const getCurriculumVersions = async (code: string) => { try { @@ -134,8 +134,8 @@ const getCoursesAndModulesForProgramme = async (code: string, periodIds: string) export const getCoursesAndModules = async (code: string, periodIds: string) => { const defaultProgrammeCourses = await getCoursesAndModulesForProgramme(code, periodIds) - if (code in combinedStudyprogrammes) { - const secondProgramme = combinedStudyprogrammes[code as keyof typeof combinedStudyprogrammes] + if (code in combinedStudyProgrammes) { + const secondProgramme = combinedStudyProgrammes[code as keyof typeof combinedStudyProgrammes] const secondProgrammeCourses = await getCoursesAndModulesForProgramme(secondProgramme, periodIds) return { defaultProgrammeCourses, secondProgrammeCourses } } diff --git a/services/backend/src/services/studyProgramme/studyProgrammeGraduations.ts b/services/backend/src/services/studyProgramme/studyProgrammeGraduations.ts index 16d50a69fd..e5e575fe8b 100644 --- a/services/backend/src/services/studyProgramme/studyProgrammeGraduations.ts +++ b/services/backend/src/services/studyProgramme/studyProgrammeGraduations.ts @@ -292,7 +292,7 @@ const getProgrammesBeforeOrAfter = async (studyprogramme: string, queryParameter return null } -export const getGraduationStatsForStudytrack = async ({ +export const getGraduationStatsForStudyTrack = async ({ studyProgramme, combinedProgramme, settings, diff --git a/services/backend/src/services/studyProgramme/studyProgrammeHelpers.ts b/services/backend/src/services/studyProgramme/studyProgrammeHelpers.ts index 7cbcdd93c2..dfc263e159 100644 --- a/services/backend/src/services/studyProgramme/studyProgrammeHelpers.ts +++ b/services/backend/src/services/studyProgramme/studyProgrammeHelpers.ts @@ -78,7 +78,7 @@ export const getStartDate = (isAcademicYear: boolean) => { } // In the object programmes should be {bachelorCode: masterCode} -export const combinedStudyprogrammes = { KH90_001: 'MH90_001' } as const +export const combinedStudyProgrammes = { KH90_001: 'MH90_001' } as const // There are 9 course_unit_types // 1. urn:code:course-unit-type:regular diff --git a/services/backend/src/services/studyProgramme/studyProgrammeUpdates.ts b/services/backend/src/services/studyProgramme/studyProgrammeUpdates.ts index f07483d3b1..a3e435edb9 100644 --- a/services/backend/src/services/studyProgramme/studyProgrammeUpdates.ts +++ b/services/backend/src/services/studyProgramme/studyProgrammeUpdates.ts @@ -1,7 +1,7 @@ -import { setBasicStats, setCreditStats, setGraduationStats, setStudytrackStats } from '../analyticsService' +import { setBasicStats, setCreditStats, setGraduationStats, setStudyTrackStats } from '../analyticsService' import { computeCreditsProduced } from '../providerCredits' import { getBasicStatsForStudytrack } from './studyProgrammeBasics' -import { getGraduationStatsForStudytrack } from './studyProgrammeGraduations' +import { getGraduationStatsForStudyTrack } from './studyProgrammeGraduations' import { getStudyRightsInProgramme } from './studyRightFinders' import { getStudyTrackStatsForStudyProgramme } from './studyTrackStats' @@ -29,7 +29,7 @@ export const updateBasicView = async (code: string, combinedProgramme: string) = await setCreditStats(creditStats, isAcademicYear, includeAllSpecials) } - const graduationStats = await getGraduationStatsForStudytrack({ + const graduationStats = await getGraduationStatsForStudyTrack({ studyProgramme: code, combinedProgramme, settings: { isAcademicYear, includeAllSpecials }, @@ -41,7 +41,7 @@ export const updateBasicView = async (code: string, combinedProgramme: string) = return 'OK' } -export const updateStudytrackView = async (code: string, combinedProgramme: string) => { +export const updateStudyTrackView = async (code: string, combinedProgramme: string) => { const graduatedOptions = ['GRADUATED_INCLUDED', 'GRADUATED_EXCLUDED'] as const const specialGroupOptions = ['SPECIAL_INCLUDED', 'SPECIAL_EXCLUDED'] as const const studyRightsOfProgramme = await getStudyRightsInProgramme(code, false, true) @@ -57,7 +57,7 @@ export const updateStudytrackView = async (code: string, combinedProgramme: stri }, studyRightsOfProgramme, }) - await setStudytrackStats(stats, graduated, specialGroup) + await setStudyTrackStats(stats, graduated, specialGroup) } } diff --git a/services/backend/src/services/studyProgramme/studyRightFinders.ts b/services/backend/src/services/studyProgramme/studyRightFinders.ts index ec12d9beab..e82402ea27 100644 --- a/services/backend/src/services/studyProgramme/studyRightFinders.ts +++ b/services/backend/src/services/studyProgramme/studyRightFinders.ts @@ -85,8 +85,8 @@ export const getStudyTracksForProgramme = async (studyProgramme: string) => { ) } -export const getSISStudyRightsOfStudents = async (studentNumbers: string[]) => - ( +export const getSISStudyRightsOfStudents = async (studentNumbers: string[]) => { + return ( await SISStudyRight.findAll({ where: { studentNumber: { @@ -96,3 +96,4 @@ export const getSISStudyRightsOfStudents = async (studentNumbers: string[]) => attributes: ['id', 'studentNumber', 'extentCode', 'semesterEnrollments', 'startDate', 'endDate'], }) ).map(studyRight => studyRight.toJSON()) +} diff --git a/services/backend/src/services/studyProgramme/studyTrackStats.ts b/services/backend/src/services/studyProgramme/studyTrackStats.ts index cc707f25f6..be8b494635 100644 --- a/services/backend/src/services/studyProgramme/studyTrackStats.ts +++ b/services/backend/src/services/studyProgramme/studyTrackStats.ts @@ -344,9 +344,13 @@ const getMainStatsByTrackAndYear = async ( : {} for (const studyRight of studyRightsOfProgramme) { - if (!studyRight.semesterEnrollments || !studyRight.student) continue + if (!studyRight.semesterEnrollments || !studyRight.student) { + continue + } const studyRightElement = studyRight.studyRightElements.find(element => element.code === studyProgramme) - if (!studyRightElement) continue + if (!studyRightElement) { + continue + } if (!includeGraduated && studyRightElement.graduated) { continue } @@ -475,7 +479,7 @@ export const getStudyTrackStatsForStudyProgramme = async ({ const since = getStartDate(isAcademicYear) const years = getYearsArray(since.getFullYear(), isAcademicYear, includeYearsCombined) - const studytrackOptions = await getStudyTracksForProgramme(studyProgramme) + const studyTracks = await getStudyTracksForProgramme(studyProgramme) const doCombo = studyProgramme.startsWith('MH') && !['MH30_001', 'MH30_003'].includes(studyProgramme) const stats = await getMainStatsByTrackAndYear( @@ -497,7 +501,7 @@ export const getStudyTrackStatsForStudyProgramme = async ({ years, ...stats, doCombo, - studytrackOptions, + studyTracks, includeGraduated: settings.graduated, populationTitles: [...tableTitles.studytracksStart, ...graduatedTitles, ...tableTitles.studytracksEnd], } diff --git a/services/frontend/src/common/InfoToolTips/populationStatistics.js b/services/frontend/src/common/InfoToolTips/populationStatistics.js index 4de475ed99..84970a5226 100644 --- a/services/frontend/src/common/InfoToolTips/populationStatistics.js +++ b/services/frontend/src/common/InfoToolTips/populationStatistics.js @@ -95,6 +95,7 @@ export const populationStatisticsToolTips = { - **Class of**: lukuvuosi, jolloin opiskelija on ilmoittautunut ensimmäisen kerran (läsnä- tai poissaolevaksi) ohjelmaan. Ilmoittautumisen opinto-oikeus voi olla ensi- tai toissijainen. - **Study programme**: haluttu koulutusohjelma. Kiinnitetyt (suosikeiksi valitut) ohjelmat näkyvät ensimmäisinä valikossa. Kiinnitys on mahdollista koulutusohjelmalistauksessa. + - **Study track**: (valinnainen) koulutusohjelman opintosuunta. Valittavissa vain, jos ohjelmalla on opintosuuntia. `, StudentsGuidanceGroups: `**Students** diff --git a/services/frontend/src/components/PopulationSearch/PopulationSearchForm.jsx b/services/frontend/src/components/PopulationSearch/PopulationSearchForm.jsx index ba53c333e2..4995ecde23 100644 --- a/services/frontend/src/components/PopulationSearch/PopulationSearchForm.jsx +++ b/services/frontend/src/components/PopulationSearch/PopulationSearchForm.jsx @@ -5,7 +5,7 @@ import { useEffect, useState } from 'react' import Datetime from 'react-datetime' import { useDispatch, useSelector } from 'react-redux' import { useHistory, useLocation } from 'react-router-dom' -import { Button, Form, Icon, Message } from 'semantic-ui-react' +import { Button, Form, Message } from 'semantic-ui-react' import { createPinnedFirstComparator, isNewStudyProgramme, textAndDescriptionSearch } from '@/common' import { useSearchHistory } from '@/common/hooks' @@ -16,6 +16,7 @@ import { YEAR_DATE_FORMAT } from '@/constants/date' import { useGetAuthorizedUserQuery } from '@/redux/auth' import { getPopulationStatistics, clearPopulations, useGetProgrammesQuery } from '@/redux/populations' import { clearSelected } from '@/redux/populationSelectedStudentCourses' +import { useGetStudyTracksQuery } from '@/redux/studyProgramme' import { useGetStudyProgrammePinsQuery } from '@/redux/studyProgrammePins' import { formatQueryParamsToArrays } from '@/shared/util' import { momentFromFormat, reformatDate } from '@/util/timeAndDate' @@ -57,6 +58,9 @@ export const PopulationSearchForm = ({ onProgress }) => { }, } : programmes + const { data: studyTracks = {}, isLoading: studyTracksAreLoading } = useGetStudyTracksQuery({ + id: query.studyRights.programme, + }) const { data: studyProgrammePins } = useGetStudyProgrammePinsQuery() const pinnedProgrammes = studyProgrammePins?.studyProgrammes || [] @@ -78,10 +82,9 @@ export const PopulationSearchForm = ({ onProgress }) => { const sameSemesters = isEqual(previousQuery.semesters, query.semesters) const sameStudentStatuses = isEqual(previousQuery.studentStatuses, query.studentStatuses) const sameYears = isEqual(previousQuery.years, query.years) - const sameStudyrights = isEqual(previousQuery.studyRights, query.studyRights) - const sameTag = previousQuery.tag === query.tag - - return sameStudyrights && sameMonths && sameYear && sameSemesters && sameStudentStatuses && sameYears && sameTag + const sameStudyRights = isEqual(previousQuery.studyRights, query.studyRights) + const sameTag = query.tag === previousQuery.tag + return sameStudyRights && sameMonths && sameYear && sameSemesters && sameStudentStatuses && sameYears && sameTag } const fetchPopulation = async query => { @@ -119,7 +122,18 @@ export const PopulationSearchForm = ({ onProgress }) => { const handleProgrammeChange = (_event, { value: programme }) => { setQuery({ ...query, - studyRights: programme === '' ? {} : { programme }, + studyRights: + programme === '' + ? { ...query.studyRights, studyTrack: undefined, programme: undefined } + : { ...query.studyRights, studyTrack: undefined, programme }, + }) + } + + const handleStudyTrackChange = (_event, { value: studyTrack }) => { + setQuery({ + ...query, + studyRights: + studyTrack === '' ? { ...query.studyRights, studyTrack: undefined } : { ...query.studyRights, studyTrack }, }) } @@ -151,7 +165,7 @@ export const PopulationSearchForm = ({ onProgress }) => { const getSearchHistoryTextFromQuery = () => { const { studyRights, semesters, months, year, studentStatuses } = query const studyRightsText = `${getTextIn(studyProgrammes[studyRights.programme].name)} ${Object.values(studyRights) - .filter(studyright => studyright) + .filter(studyRight => studyRight) .join(', ')}` const timeText = `${semesters.join(', ')}/${year}-${parseInt(year, 10) + 1}, ${months} months` const studentStatusesText = @@ -261,10 +275,6 @@ export const PopulationSearchForm = ({ onProgress }) => { } const renderStudyProgrammeSelector = () => { - const { studyRights } = query - if (programmesAreLoading) { - return - } if (Object.values(studyProgrammes).length === 0 && !programmesAreLoading) { return ( { ) } - let programmesToRender - if (Object.values(studyProgrammes).length !== 0) { - let sortedStudyProgrammes = sortBy(studyProgrammes, programme => getTextIn(programme.name)) - if (filterProgrammes) { - sortedStudyProgrammes = sortedStudyProgrammes.filter(programme => isNewStudyProgramme(programme.code)) - } - programmesToRender = sortedStudyProgrammes.map(({ code, name }) => ({ - code, - description: code, - icon: pinnedProgrammes.includes(code) ? 'pin' : '', - name, - text: getTextIn(name), - value: code, - })) - } + const studyProgrammesAvailable = Object.values(studyProgrammes).length > 0 && !programmesAreLoading + const programmesToRender = studyProgrammesAvailable + ? sortBy(studyProgrammes, programme => getTextIn(programme.name)) + .filter(programme => !filterProgrammes || isNewStudyProgramme(programme.code)) + .map(({ code, name }) => ({ + code, + description: code, + icon: pinnedProgrammes.includes(code) ? 'pin' : '', + name, + text: getTextIn(name), + value: code, + })) + : [] const pinnedFirstComparator = createPinnedFirstComparator(pinnedProgrammes) return ( @@ -299,6 +307,7 @@ export const PopulationSearchForm = ({ onProgress }) => { clearable closeOnChange data-cy="select-study-programme" + disabled={!studyProgrammesAvailable} fluid noResultsMessage="No selectable study programmes" onChange={handleProgrammeChange} @@ -308,7 +317,44 @@ export const PopulationSearchForm = ({ onProgress }) => { selectOnBlur={false} selectOnNavigation={false} selection - value={studyRights.programme} + value={query.studyRights.programme} + /> + + ) + } + + const renderStudyTrackSelector = () => { + const studyTracksAvailable = Object.values(studyTracks).length > 1 && !studyTracksAreLoading + const studyTracksToRender = studyTracksAvailable + ? Object.keys(studyTracks) + .filter(studyTrack => studyTrack !== query.studyRights.programme) + .map(studyTrack => ({ + code: studyTrack, + description: studyTrack, + icon: null, + text: getTextIn(studyTracks[studyTrack]), + value: studyTrack, + })) + : [] + + return ( + + + ) @@ -318,30 +364,17 @@ export const PopulationSearchForm = ({ onProgress }) => { return null } - let invalidQuery = false - let errorMessage - - if (!query.studyRights.programme) { - invalidQuery = true - errorMessage = 'Select study programme' - } + const invalidQuery = !query.studyRights.programme return (
{renderEnrollmentDateSelector()} {renderStudyProgrammeSelector()} - + {renderStudyTrackSelector()} See class - { - item.date = new Date(item.date) - return item - })} - updateItem={updateItemInSearchHistory} - /> + ) } diff --git a/services/frontend/src/components/SearchHistory/index.jsx b/services/frontend/src/components/SearchHistory/index.jsx index ddfb1cff7f..68b89e6d63 100644 --- a/services/frontend/src/components/SearchHistory/index.jsx +++ b/services/frontend/src/components/SearchHistory/index.jsx @@ -1,7 +1,7 @@ import { sortBy } from 'lodash' import moment from 'moment' import { useState } from 'react' -import { Form, Header, Icon, Segment } from 'semantic-ui-react' +import { Form, Header, HeaderContent, Icon, Segment } from 'semantic-ui-react' import { DISPLAY_DATE_FORMAT_DEV } from '@/constants/date' @@ -27,9 +27,9 @@ export const SearchHistory = ({ disabled, handleSearch, header = 'Previous searc return ( -
+
- {header} + {header}
Something went wrong, please try refreshing the page. + if (isError) { + return

Something went wrong, please try refreshing the page.

+ } const noData = stats.isSuccess && stats.mainStatsByYear && !stats.mainStatsByYear.Total.length - if (noData) return

There is no data available for the selected programme between 2017-2022

+ if (noData) { + return

There is no data available for the selected programme between 2017-2022

+ } + const infoTextGraduationTimes = studyProgramme.includes('MH') ? 'AverageGraduationTimesStudyTracksMaster' : 'AverageGraduationTimesStudyTracks' @@ -88,8 +92,7 @@ export const StudyTrackOverview = ({ const studyTrackStatsGraduationStats = { basic: {}, combo: {} } // One of the study track options is always the study programme itself - const studyProgrammeHasStudyTracks = - Object.keys(stats?.data?.studytrackOptions || {}).length > 1 && track === studyProgramme + const programmeHasStudyTracks = Object.keys(stats?.data?.studyTracks || {}).length > 1 && track === studyProgramme const calculateStudyTrackStats = combo => { const studyTrackStatsGraduationStats = Object.entries(stats.data.graduationTimes) @@ -127,7 +130,7 @@ export const StudyTrackOverview = ({ return { studyTrackStatsGraduationStats, studyTrackStatsClassSizes } } - if (studyProgrammeHasStudyTracks && Object.keys(stats?.data?.graduationTimes || {}).length > 1) { + if (programmeHasStudyTracks && Object.keys(stats?.data?.graduationTimes || {}).length > 1) { studyTrackStatsGraduationStats.basic = calculateStudyTrackStats() if (stats?.data?.doCombo) { studyTrackStatsGraduationStats.combo = calculateStudyTrackStats(true) @@ -140,7 +143,7 @@ export const StudyTrackOverview = ({ ) : ( <> - +
@@ -211,7 +214,7 @@ export const StudyTrackOverview = ({ setValue={setShowMedian} value={showMedian} /> - {studyProgrammeHasStudyTracks ? ( + {programmeHasStudyTracks ? (
{stats?.data.doCombo && ( `/v2/studyprogrammes/${id}/basicstats?year_type=${yearType}&special_groups=${specialGroups}&combined_programme=${combinedProgramme}`, }), + getColorizedTableCourseStats: builder.query({ + query: ({ id }) => `/v2/studyprogrammes/${id}/colorizedtablecoursestats`, + }), getCreditStats: builder.query({ query: ({ codes, isAcademicYear, specialGroups }) => `/v2/studyprogrammes/creditstats?codes=${JSON.stringify( codes )}&isAcademicYear=${isAcademicYear}&includeSpecials=${specialGroups}`, }), + getEvaluationStats: builder.query({ + query: ({ id, yearType, specialGroups, graduated }) => + `/v2/studyprogrammes/${id}/evaluationstats?year_type=${yearType}&special_groups=${specialGroups}&graduated=${graduated}`, + }), getGraduationStats: builder.query({ query: ({ id, yearType, specialGroups, combinedProgramme }) => `/v2/studyprogrammes/${id}/graduationstats?year_type=${yearType}&special_groups=${specialGroups}&combined_programme=${combinedProgramme}`, }), - getStudytrackStats: builder.query({ + getStudyTrackStats: builder.query({ query: ({ id, graduated, specialGroups, combinedProgramme }) => `/v2/studyprogrammes/${id}/studytrackstats?graduated=${graduated}&special_groups=${specialGroups}&combined_programme=${combinedProgramme}`, }), + getProgrammeCoursesStats: builder.query({ + query: ({ id, academicyear, combinedProgramme }) => + `/v2/studyprogrammes/${id}/coursestats?academicyear=${academicyear}&combined_programme=${combinedProgramme}`, + }), + getStudyTracks: builder.query({ + query: ({ id }) => `/v2/studyprogrammes/${id}/studytracks`, + }), updateBasicView: builder.query({ query: ({ id, combinedProgramme }) => `/v2/studyprogrammes/${id}/update_basicview?combined_programme=${combinedProgramme}`, @@ -31,31 +45,21 @@ const studyProgrammeApi = RTKApi.injectEndpoints({ query: ({ id, combinedProgramme }) => `/v2/studyprogrammes/${id}/update_studytrackview?combined_programme=${combinedProgramme}`, }), - getProgrammeCoursesStats: builder.query({ - query: ({ id, academicyear, combinedProgramme }) => - `/v2/studyprogrammes/${id}/coursestats?academicyear=${academicyear}&combined_programme=${combinedProgramme}`, - }), - getEvaluationStats: builder.query({ - query: ({ id, yearType, specialGroups, graduated }) => - `/v2/studyprogrammes/${id}/evaluationstats?year_type=${yearType}&special_groups=${specialGroups}&graduated=${graduated}`, - }), - getColorizedTableCourseStats: builder.query({ - query: ({ id }) => `/v2/studyprogrammes/${id}/colorizedtablecoursestats`, - }), }), overrideExisting: false, }) export const { useGetBasicStatsQuery, + useGetColorizedTableCourseStatsQuery, useGetCreditStatsQuery, + useGetEvaluationStatsQuery, useGetGraduationStatsQuery, - useGetStudytrackStatsQuery, + useGetStudyTrackStatsQuery, + useGetProgrammeCoursesStatsQuery, + useGetStudyTracksQuery, useUpdateBasicViewQuery, useUpdateStudyTrackViewQuery, - useGetProgrammeCoursesStatsQuery, - useGetEvaluationStatsQuery, - useGetColorizedTableCourseStatsQuery, } = studyProgrammeApi const getFilteredAndFormattedStudyProgrammes = (getTextIn, studyProgrammes) => {