From ae60a384452a92ac801f026c2bdaf8c57a799df6 Mon Sep 17 00:00:00 2001 From: Valtteri Kantanen Date: Mon, 16 Dec 2024 16:02:20 +0200 Subject: [PATCH] Handle cases where a student has multiple citizenships --- services/backend/src/models/student.ts | 9 ------ .../getStudentsIncludeCoursesBetween.ts | 4 +-- .../src/services/populations/shared.ts | 8 ++--- .../studyProgramme/studyRightFinders.ts | 2 +- .../studyProgramme/studyTrackStats.ts | 20 +++++++----- .../src/common/InfoToolTips/filters.ts | 5 +++ .../src/common/InfoToolTips/studyProgramme.ts | 2 +- .../FilterView/filters/citizenship.jsx | 31 ++++++++++++------- .../StudentTable/GeneralTab/GeneralTab.jsx | 11 ++++--- .../StudentTable/GeneralTab/index.jsx | 4 +-- .../StudyTrackDataTable.jsx | 4 +-- .../FacultyStudentDataTable.tsx | 4 +-- .../src/db/models/student.js | 3 -- .../sis-updater-worker/src/updater/mapper.js | 4 --- 14 files changed, 53 insertions(+), 58 deletions(-) diff --git a/services/backend/src/models/student.ts b/services/backend/src/models/student.ts index 60e3965222..b72d4190ad 100644 --- a/services/backend/src/models/student.ts +++ b/services/backend/src/models/student.ts @@ -60,15 +60,6 @@ export class Student extends Model> { @Column(DataType.JSONB) citizenships!: Name[] - @Column(DataType.STRING) - home_country_fi!: string - - @Column(DataType.STRING) - home_country_sv!: string - - @Column(DataType.STRING) - home_country_en!: string - @Column(DataType.STRING) gender_code!: GenderCode diff --git a/services/backend/src/services/populations/getStudentsIncludeCoursesBetween.ts b/services/backend/src/services/populations/getStudentsIncludeCoursesBetween.ts index d7f92ffa6e..9f2458afc2 100644 --- a/services/backend/src/services/populations/getStudentsIncludeCoursesBetween.ts +++ b/services/backend/src/services/populations/getStudentsIncludeCoursesBetween.ts @@ -97,9 +97,7 @@ const getStudents = async (studentNumbers: string[]) => { 'firstnames', 'lastname', 'studentnumber', - 'home_country_en', - 'home_country_fi', - 'home_country_sv', + 'citizenships', 'dateofuniversityenrollment', 'creditcount', 'abbreviatedname', diff --git a/services/backend/src/services/populations/shared.ts b/services/backend/src/services/populations/shared.ts index fc678366f5..f43e6a4d23 100644 --- a/services/backend/src/services/populations/shared.ts +++ b/services/backend/src/services/populations/shared.ts @@ -159,9 +159,7 @@ const formatStudentForPopulationStatistics = ( tags, birthdate, sis_person_id, - home_country_fi, - home_country_sv, - home_country_en, + citizenships, } = student const criteriaCoursesBySubstitutions = criteria?.allCourses @@ -271,9 +269,7 @@ const formatStudentForPopulationStatistics = ( birthdate, studyplans, sis_person_id, - home_country_en, - home_country_fi, - home_country_sv, + citizenships, criteriaProgress: toProgressCriteria(), curriculumVersion: getCurriculumVersion(correctStudyplan[0]?.curriculum_period_id), transferredStudyright, diff --git a/services/backend/src/services/studyProgramme/studyRightFinders.ts b/services/backend/src/services/studyProgramme/studyRightFinders.ts index 2d64795a1f..61878981e9 100644 --- a/services/backend/src/services/studyProgramme/studyRightFinders.ts +++ b/services/backend/src/services/studyProgramme/studyRightFinders.ts @@ -35,7 +35,7 @@ export const getStudyRightsInProgramme = async ( if (includeStudentsAndCredits) { include.push({ model: Student, - attributes: ['gender_code', 'home_country_en'], + attributes: ['gender_code', 'citizenships'], include: [ { model: Credit, diff --git a/services/backend/src/services/studyProgramme/studyTrackStats.ts b/services/backend/src/services/studyProgramme/studyTrackStats.ts index 71001da326..e27f32d5fe 100644 --- a/services/backend/src/services/studyProgramme/studyTrackStats.ts +++ b/services/backend/src/services/studyProgramme/studyTrackStats.ts @@ -267,16 +267,20 @@ const getMainStatsByTrackAndYear = async ( } } - const studentHomeCountry = studyRight.student.home_country_en + const studentCitizenships = studyRight.student.citizenships - if (studentHomeCountry === 'Finland') { - yearlyStats[year][programmeOrStudyTrack].finnish += 1 - } else { - yearlyStats[year][programmeOrStudyTrack].otherCountries += 1 - if (!yearlyStats[year][programmeOrStudyTrack].otherCountriesCounts[studentHomeCountry]) { - yearlyStats[year][programmeOrStudyTrack].otherCountriesCounts[studentHomeCountry] = 0 + for (const citizenship of studentCitizenships) { + const country = citizenship.en + if (!country) { + continue + } + if (country === 'Finland') { + yearlyStats[year][programmeOrStudyTrack].finnish += 1 + } else { + yearlyStats[year][programmeOrStudyTrack].otherCountries += 1 + yearlyStats[year][programmeOrStudyTrack].otherCountriesCounts[country] ??= 0 + yearlyStats[year][programmeOrStudyTrack].otherCountriesCounts[country] += 1 } - yearlyStats[year][programmeOrStudyTrack].otherCountriesCounts[studentHomeCountry] += 1 } if (!hasGraduated) return diff --git a/services/frontend/src/common/InfoToolTips/filters.ts b/services/frontend/src/common/InfoToolTips/filters.ts index 925c2b6e58..8113cc5624 100644 --- a/services/frontend/src/common/InfoToolTips/filters.ts +++ b/services/frontend/src/common/InfoToolTips/filters.ts @@ -37,4 +37,9 @@ export const filterToolTips = { Valitse opiskelijoita sen mukaan, minä vuonna heidän ensimmäinen opinto-oikeutensa yliopistolla alkoi (ei siis ainoastaan tarkasteltavan tutkinnon oikeus). Avoimia opinto-oikeuksia ei oteta huomioon. `, }, + citizenship: { + label: null, + short: + 'Rajaa opiskelijoita kansalaisuuden perusteella. Tulokseen sisältyvät opiskelijat, joilla on valitun valtion kansalaisuus, riippumatta siitä, onko heillä myös muita kansalaisuuksia.', + }, } diff --git a/services/frontend/src/common/InfoToolTips/studyProgramme.ts b/services/frontend/src/common/InfoToolTips/studyProgramme.ts index 804b8db6cf..4ef87584a2 100644 --- a/services/frontend/src/common/InfoToolTips/studyProgramme.ts +++ b/services/frontend/src/common/InfoToolTips/studyProgramme.ts @@ -117,7 +117,7 @@ export const studyProgrammeToolTips: Record = { Lukukausi-ilmoittautumisissa huomioidaan vain se opiskeluoikeus, joka liittyy tarkasteltavaan koulutusohjelmaan. Vaikka opiskelija olisi ilmoittautunut toiseen opiskeluoikeuteen läsnäolevaksi, mutta hän on laiminlyönyt ilmoittautumisen tarkasteltavaan koulutusohjelmaan liittyvään opiskeluoikeuteen, hänet lasketaan ryhmään ”Inactive”. - Paksummilla pystyviivoilla eroteltujen kategorioiden (**Current status**, **Gender**, **Countries**) sarakkeet tuottavat yhteenlaskettuna kentän **All** arvon. + Paksummilla pystyviivoilla eroteltujen kategorioiden (**Current status**, **Gender**) sarakkeet tuottavat yhteenlaskettuna kentän **All** arvon. **Citizenships**-kategoriat (**Finland** ja **Other**) voivat tuottaa yhteenlaskettuna suuremman tuloksen, koska opiskelijoilla voi olla useampi kuin yksi kansalaisuus. Jos sivun yläosassa on valittuna ”All study rights” (oletus), mukana ovat myös ohjelmaan siirtyneet ja ohjelmasta pois siirtyneet opiskelijat. diff --git a/services/frontend/src/components/FilterView/filters/citizenship.jsx b/services/frontend/src/components/FilterView/filters/citizenship.jsx index 8deb057fa8..c2dd8c766e 100644 --- a/services/frontend/src/components/FilterView/filters/citizenship.jsx +++ b/services/frontend/src/components/FilterView/filters/citizenship.jsx @@ -1,6 +1,7 @@ import { orderBy } from 'lodash' import { Form, Dropdown } from 'semantic-ui-react' +import { filterToolTips } from '@/common/InfoToolTips' import { useLanguage } from '@/components/LanguagePicker/useLanguage' import { createFilter } from './createFilter' @@ -8,22 +9,26 @@ const CitizenshipFilterCard = ({ options, onOptionsChange, withoutSelf }) => { const { getTextIn } = useLanguage() const { selected } = options - const dropdownOptions = withoutSelf().reduce((citizenships, student) => { - const { home_country_en: homeCountryEn, home_country_fi: homeCountryFi, home_country_sv: homeCountrySv } = student + const dropdownOptions = withoutSelf().reduce((options, student) => { + for (const citizenship of student.citizenships) { + const countryName = citizenship.en + if (options.some(option => option.value === countryName)) { + continue + } + const count = withoutSelf().filter(student => + student.citizenships.some(citizenship => citizenship.en === countryName) + ).length + const country = getTextIn(citizenship) - if (citizenships.every(option => option.value !== homeCountryEn)) { - const count = withoutSelf().filter(student => student.home_country_en === homeCountryEn).length - const homeCountry = getTextIn({ fi: homeCountryFi, en: homeCountryEn, sv: homeCountrySv }) - - citizenships.push({ - key: homeCountryEn, - value: homeCountryEn, - text: `${homeCountry} (${count})`, + options.push({ + key: countryName, + value: countryName, + text: `${country} (${count})`, count, }) } - return citizenships + return options }, []) const sortedDropdownOptions = orderBy(dropdownOptions, ['count', 'text'], ['desc', 'asc']) @@ -56,10 +61,12 @@ export const citizenshipFilter = createFilter({ selected: '', }, + info: filterToolTips.citizenship, + isActive: ({ selected }) => selected !== '', filter(student, { selected }) { - return selected === student.home_country_en + return student.citizenships.some(citizenship => citizenship.en === selected) }, component: CitizenshipFilterCard, diff --git a/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/GeneralTab.jsx b/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/GeneralTab.jsx index 504165175d..1214212187 100644 --- a/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/GeneralTab.jsx +++ b/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/GeneralTab.jsx @@ -534,11 +534,12 @@ export const GeneralTab = ({ title: 'Gender', getRowVal: student => getGender(student.gender_code), }, - citizenship: { - key: 'citizenship', - title: 'Citizenship', - getRowVal: student => - getTextIn({ fi: student.home_country_fi, en: student.home_country_en, sv: student.home_country_sv }), + citizenships: { + key: 'citizenships', + title: 'Citizenships', + getRowVal: student => student.citizenships.map(getTextIn).sort(), + formatValue: value => value.join(', '), + filterType: 'multi', }, option: (isBachelorsProgramme || isMastersProgramme) && { key: 'option', diff --git a/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/index.jsx b/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/index.jsx index d52f0af0a7..5d2ca706c7 100644 --- a/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/index.jsx +++ b/services/frontend/src/components/PopulationStudents/StudentTable/GeneralTab/index.jsx @@ -41,7 +41,7 @@ export const GeneralTabContainer = ({ studyGuidanceGroup, variant, ...props }) = const columns = ['credits.since', 'programme', 'startYear'] if (studyGuidanceGroup?.tags?.studyProgramme) columns.push( - 'citizenship', + 'citizenships', 'credits.hops', 'curriculumPeriod', 'endDate', @@ -81,7 +81,7 @@ export const GeneralTabContainer = ({ studyGuidanceGroup, variant, ...props }) = coursePopulation: ['enrollmentDate', 'gradeForSingleCourse', 'language', 'passDate', 'programme', 'startYear'], population: [ 'admissionType', - 'citizenship', + 'citizenships', 'credits.hops', 'credits.studyright', 'curriculumPeriod', diff --git a/services/frontend/src/components/StudyProgramme/StudyTrackOverview/StudyTrackDataTable.jsx b/services/frontend/src/components/StudyProgramme/StudyTrackOverview/StudyTrackDataTable.jsx index 4a66276e99..846fd75339 100644 --- a/services/frontend/src/components/StudyProgramme/StudyTrackOverview/StudyTrackDataTable.jsx +++ b/services/frontend/src/components/StudyProgramme/StudyTrackOverview/StudyTrackDataTable.jsx @@ -336,10 +336,10 @@ export const StudyTrackDataTable = ({ - Countries + Citizenships } /> diff --git a/services/frontend/src/pages/Faculties/StudentsByStartingYearTab/FacultyStudentDataTable.tsx b/services/frontend/src/pages/Faculties/StudentsByStartingYearTab/FacultyStudentDataTable.tsx index a491781ff0..a738400899 100644 --- a/services/frontend/src/pages/Faculties/StudentsByStartingYearTab/FacultyStudentDataTable.tsx +++ b/services/frontend/src/pages/Faculties/StudentsByStartingYearTab/FacultyStudentDataTable.tsx @@ -120,7 +120,7 @@ export const FacultyStudentDataTable = ({ setVisible(arrayToModify) } const infoText = - "Hover over 'Other' cell to see from which countries students are coming. Shown only for study programmes." + "Hover over 'Other' cell to see which citizenships (other than Finland) students have. Shown only for study programmes." return (
@@ -133,7 +133,7 @@ export const FacultyStudentDataTable = ({ Gender - Countries + Citizenships diff --git a/updater/sis-updater-worker/src/db/models/student.js b/updater/sis-updater-worker/src/db/models/student.js index e9b32315bb..9c69290a3d 100644 --- a/updater/sis-updater-worker/src/db/models/student.js +++ b/updater/sis-updater-worker/src/db/models/student.js @@ -21,9 +21,6 @@ Student.init( secondary_email: { type: STRING }, national_student_number: { type: STRING }, citizenships: { type: JSONB }, - home_country_fi: { type: STRING }, - home_country_sv: { type: STRING }, - home_country_en: { type: STRING }, gender_code: { type: STRING }, sis_person_id: { type: STRING }, hasPersonalIdentityCode: { type: BOOLEAN }, diff --git a/updater/sis-updater-worker/src/updater/mapper.js b/updater/sis-updater-worker/src/updater/mapper.js index 9a31f234a2..1f01bc7c68 100644 --- a/updater/sis-updater-worker/src/updater/mapper.js +++ b/updater/sis-updater-worker/src/updater/mapper.js @@ -89,7 +89,6 @@ const studentMapper = (attainments, studyRights, attainmentsToBeExluced) => stud const gender_code = parseGender(gender_urn) const citizenships = (student.citizenships ?? []).map(countryUrn => getCountry(countryUrn).name) - const home_country = student.citizenships ? getCountry(student.citizenships[0]) : null const studyRightsOfStudent = studyRights.filter(SR => SR.person_id === id) @@ -120,9 +119,6 @@ const studentMapper = (attainments, studyRights, attainmentsToBeExluced) => stud creditcount: calculateTotalCreditsFromAttainments(attainmentsOfStudent), dateofuniversityenrollment, citizenships, - home_country_fi: home_country ? home_country.name.fi : null, - home_country_sv: home_country ? home_country.name.sv : null, - home_country_en: home_country ? home_country.name.en : null, sis_person_id: id, hasPersonalIdentityCode: has_personal_identity_code, }