Compare commits

...
159 Commits
Author SHA1 Message Date
Valtteri Kantanen c64ebfdf46 [Backend] Type semesters.ts 2024-07-16 12:36:33 +03:00
Valtteri Kantanen 6bd7eed960 [Close to graduation] Export function findStudentsCloseToGraduation to update data 2024-07-16 12:36:33 +03:00
Riku Rauhala 0f65cf014b [Backend] Type faculty.ts 2024-07-16 12:00:37 +03:00
Riku Rauhala 97b5879660 [Backend] Remove console.time 2024-07-16 11:08:45 +03:00
Riku Rauhala a75e333de8 [Backend] Move @types/umzug to dev dependencies 2024-07-16 09:55:00 +03:00
Riku RauhalaandGitHub a97e17affa Merge pull request #4573 from UniversityOfHelsinkiCS/fd-docker-compose-changes-to-volumes
[FD compose] Edited backend and frontend volumes. Added also REACT_APP_SERVICE_PROVIDER env variable to frontend.
2024-07-16 08:58:36 +03:00
Matti Räsänen ddc682850f [FD compose] Edited backend and frontend volumes. Added also REACT_APP_SERVICE_PROVIDER env variable to frontend. 2024-07-16 08:41:05 +03:00
Riku Rauhala f16ee68853 [Backend] Use ExtentCode in facultyHelpers 2024-07-16 08:31:04 +03:00
Riku Rauhala 07d43e9efd [Backend] Use ExtentCode in studyProgrammeGraduations 2024-07-16 08:28:15 +03:00
Riku Rauhala 79119477b3 [Backend] Use ExtentCode in facultyBasics 2024-07-16 08:23:23 +03:00
Riku Rauhala 224feb18d6 [Backend] Refactor graduatedStudyrights 2024-07-16 08:21:55 +03:00
Riku Rauhala d96411fe69 [Backend] Refactor isNewProgramme 2024-07-15 21:47:40 +03:00
Riku Rauhala 843fe84c13 [Backend] Refactor startedStudyrights 2024-07-15 21:31:36 +03:00
Riku Rauhala 5a52e03d52 [Backend] Fix a broken query for inside transfers 2024-07-15 21:02:38 +03:00
Riku Rauhala 26dfa2dcec [Backend] Install Umzug types 2024-07-15 20:25:11 +03:00
Riku Rauhala 75c6fb4739 [Updater] Enable logging colors 2024-07-15 20:25:11 +03:00
Valtteri Kantanen dcdf6f5848 [Updater] Fix education being used as degree programme
It seems that all needed programmes can now be found from `modules` table and there's no need to use `educations`. This also fixes the bug where some study rights got a code like 'KM-TIETOJENK-KOUL' even though it's not a degree programme but an education
2024-07-15 18:22:19 +03:00
Riku Rauhala e32a32ad54 [Backend] Use ExtentCode in facultyGraduationTimes 2024-07-15 16:03:09 +03:00
Riku Rauhala 238557ebfb [Backend] Enable colors for logger 2024-07-15 15:05:14 +03:00
Riku Rauhala ddee43a6a3 [Backend] Add source mapping to tsconfig 2024-07-15 14:25:08 +03:00
Riku Rauhala 42edf79a39 [Faculty programmes] Fix programmes with empty rows 2024-07-15 13:49:08 +03:00
Riku Rauhala 55ee912e56 [Faculty programmes] Hide tables with 0 statistics 2024-07-15 13:13:46 +03:00
Riku Rauhala 71c6622767 [Backend] Move config to src 2024-07-15 12:43:14 +03:00
Valtteri Kantanen 1733879a24 [Backend] Fix type error that happened during a restart 2024-07-15 12:36:54 +03:00
Valtteri KantanenandGitHub 3542e6df57 Merge pull request #4567 from UniversityOfHelsinkiCS/OOD-24
[Updater] Removed organisation id from semester handling
2024-07-15 12:17:28 +03:00
Valtteri Kantanen 0948af41f9 [Updater] Remove unused parameter from getStudyRightSemesterEnrollments 2024-07-15 12:08:21 +03:00
Matti Räsänen 39af9ac937 [FD configs] Edited fd compose frontend volumes 2024-07-15 10:13:18 +03:00
EssKayzandGitHub 787c67a5ff Merge branch 'master' into OOD-24 2024-07-15 10:03:33 +03:00
Riku Rauhala d6c0847f6a [Backend] Refactor open uni format functions 2024-07-12 21:14:53 +03:00
Riku Rauhala febcd20a81 [Backend] Create enum EnrollmentType 2024-07-12 21:05:36 +03:00
Riku Rauhala 34cd554e7a [Backend] Create enum Phase 2024-07-12 20:42:11 +03:00
Riku Rauhala be19c456ba [Backend] Extract study programme format functions 2024-07-12 20:38:51 +03:00
Riku Rauhala 95e6e38271 [Backend] Create enum ElementDetailType 2024-07-12 20:03:51 +03:00
Riku Rauhala db9a4b7e78 [Backend] Create enum PriorityCode 2024-07-12 19:50:58 +03:00
Riku Rauhala 85a52731ad [Backend] Extract faculty transfers 2024-07-12 19:42:31 +03:00
Riku Rauhala a62dfc81b3 Revert "[Backend] Use ExtentCode in all remaining places"
This reverts commit bef2482759.
2024-07-12 19:24:53 +03:00
Riku Rauhala bef2482759 [Backend] Use ExtentCode in all remaining places 2024-07-12 17:36:40 +03:00
Matti Räsänen 2aaa6b590a [Git] Added enviroments to gitignore 2024-07-12 16:10:36 +03:00
EssKayz e64f453386 [FD-Compose] Cleanup nodemod volume bind 2024-07-12 15:52:11 +03:00
Riku Rauhala f7a782aea7 [Backend] Await faculties 2024-07-12 15:35:11 +03:00
Riku Rauhala b32ea59ad5 [Backend] Use acual date types for date attributes 2024-07-12 14:59:06 +03:00
Riku Rauhala 2994c80e97 [Backend] Fix broken import 2024-07-12 14:55:07 +03:00
Riku RauhalaandGitHub b441a688dc Merge pull request #4571 from UniversityOfHelsinkiCS/aws-rds-compatibility
[Updater][Backend] AWS RDS compatibility
2024-07-12 14:46:15 +03:00
EssKayz 547f077538 [backend][migrations] Remove ALTER TABLE owner postgres
This makes it easier for DBA to manage the roles for database
2024-07-12 14:28:35 +03:00
EssKayz ea4a730ec1 [backend][migrations] Remove Extension comment
This comment doesn't mechanically do anything, but requires super user to complete in RDS so no real reason to try to circumvent the problem otherwise
2024-07-12 14:27:12 +03:00
EssKayz b421e8a9eb [updater] allow self-signed certificates for RDS connectins 2024-07-12 14:26:08 +03:00
Riku Rauhala 0b4046b061 [Backend] Increase the usage of ExtentCode 2024-07-12 14:17:34 +03:00
Riku RauhalaandGitHub d213b927a4 Merge pull request #4570 from UniversityOfHelsinkiCS/frontend-service-provider-env-fix
[Frontend] Add REACT_APP_ prefix to SP
2024-07-12 11:25:54 +03:00
EssKayz a3d1b992b5 [Frontend] Add REACT_APP_ prefix to SP 2024-07-12 11:01:49 +03:00
Riku Rauhala d84544d070 [Backend] Use ExtentCode in facultyStudentProgress 2024-07-12 09:05:00 +03:00
Riku Rauhala 14646e3bb2 [Backend] Use ExtentCode in faculty service 2024-07-11 21:47:38 +03:00
Riku Rauhala 64f7bb99ae [Backend] Extract faculty format functions 2024-07-11 21:40:22 +03:00
Riku Rauhala 84cdf29e4c [Backend] Use ExtentCode in facultyHelpers 2024-07-11 21:12:48 +03:00
Riku Rauhala 4262229856 [Backend] Refactor Faculty/Organization logic 2024-07-11 21:02:33 +03:00
Riku Rauhala 5a43e40196 [Backend] Use ExtentCode in facultyHelpers 2024-07-11 20:26:55 +03:00
Riku Rauhala daad45264f [Backend] Use enum ExtentCode in providerCredits service 2024-07-11 20:03:34 +03:00
Riku Rauhala 6e081961cb [Backend] Refactor and type graduationHelpers 2024-07-11 19:31:20 +03:00
Riku Rauhala 93c7c84d87 [Backend] Create enum ExtentCode 2024-07-11 18:55:49 +03:00
Riku Rauhala 539c77fdde Change Renovate rebasing rules 2024-07-11 15:39:16 +03:00
ShootingStar91 022ebb3cc5 Try to fix tsconfig.json 2024-07-11 15:05:13 +03:00
ShootingStar91 0e1e2a5959 Remove npm prune 2024-07-11 14:29:23 +03:00
ShootingStar91 559c659a16 Modify dockerfile after js-files are included by tsc 2024-07-11 14:25:55 +03:00
ShootingStar91 8b27e91a83 AllowJS and fix ts error that it caused 2024-07-11 14:25:27 +03:00
Riku Rauhala b269dfe995 [Backend] Add missing AutoIncrement decorators 2024-07-11 13:59:04 +03:00
Riku Rauhala 880bce2c03 [Backend] Add missing semestercode to Credit model 2024-07-11 13:44:28 +03:00
ShootingStar91 add8e4f7c8 remove sql logging 2024-07-11 13:04:05 +03:00
ShootingStar91 4d4fcef3ef Try fix insidious seuqlize where bug 2024-07-11 13:01:04 +03:00
Riku Rauhala 5bffa46cbf [Backend] Move SIS_DB_URL to conf 2024-07-11 10:09:38 +03:00
Riku Rauhala d73ae52501 [Backend] Rename studentStudentnumber back to student_studentnumber 2024-07-11 10:05:49 +03:00
Riku Rauhala 448d3668fd [Study programme route] Add error handling to Grafana logging 2024-07-10 20:09:31 +03:00
Riku Rauhala d69784af32 [Backend] Add @AutoIncrement decorator to models with bigint primary key 2024-07-10 15:48:32 +03:00
Riku Rauhala 792bf911c0 [Custom population search service] Fix search 2024-07-10 15:47:04 +03:00
Riku Rauhala df87abc0ee [Student service] Fix crashing 2024-07-10 14:21:02 +03:00
Riku Rauhala 9340efe406 [Backend] Create enum GenderCode 2024-07-10 13:50:26 +03:00
Riku Rauhala 3a1e155b11 Merge remote-tracking branch 'origin/master' into tsMerge 2024-07-10 13:12:35 +03:00
Matti Räsänen 5086734db3 [Updater] Removed organisation id from initStartYearToSemesters and getSemester functions. Removed ORG_TO and orgTo prefixes from redis key and localMaps 2024-07-10 12:44:45 +03:00
Riku Rauhala 0a79ac735f [Backend] Remove npm prune from Dockerfile 2024-07-10 11:04:50 +03:00
Riku Rauhala 255ab0de1f [Backend] Include dev dependencies in build process 2024-07-10 10:58:54 +03:00
Riku Rauhala 14ef484b7a [Backend] Add build command 2024-07-10 10:56:05 +03:00
Riku Rauhala 1d26819db9 [Backend] Update Dockerfile 2024-07-10 10:16:21 +03:00
Riku Rauhala bf3a500e58 [Backend] Update Dockerfile 2024-07-10 10:12:59 +03:00
Riku Rauhala 72d846d3b9 [Backend] Update Dockerfile 2024-07-10 10:09:11 +03:00
Matti Räsänen 93ada9edd0 [Docker] removed sis-importer-db-data volume from fd compose file 2024-07-10 08:18:40 +03:00
Matti Räsänen 1f195e0697 Merge branch 'master' into OOD-24 2024-07-10 07:47:10 +03:00
Riku Rauhala aa60d86993 [Population service] Fix TypeScript issues 2024-07-09 18:20:15 +03:00
Riku RauhalaandGitHub b692137207 Merge pull request #4566 from UniversityOfHelsinkiCS/OOD-34
[Updater] Added fallback to abbreviation in function initGradeScaleIdToGradeIdsToGrades
2024-07-09 18:15:02 +03:00
Riku Rauhala cadffa6b03 [Student service] Fix typing issues and update exports 2024-07-09 18:11:07 +03:00
Riku Rauhala f804c3295a Add tsc command 2024-07-09 16:23:05 +03:00
Riku Rauhala 7d8bd40ab3 Install @types/lodash 2024-07-09 16:16:55 +03:00
Riku Rauhala 79a2b2ca2c Ignore dist 2024-07-09 16:15:07 +03:00
EssKayz a8e6f5ac43 [updater] Add configurability of port to knex conn 2024-07-09 16:00:01 +03:00
EssKayz 43c9c1ad85 [fd-compose] Make connection to importer side db 2024-07-09 15:54:13 +03:00
Matti Räsänen 4109a840cb [Updater] Added fallback to abbreviation in function initGradeScaleIdToGradeIdsToGrades 2024-07-09 14:51:18 +03:00
Riku Rauhala e48e5ba853 Format code 2024-07-09 12:21:17 +03:00
Riku Rauhala ddd8ecf5aa Run CI for the branch tsMerge 2024-07-09 11:34:56 +03:00
Riku Rauhala 6f9fdf9f6f Merge remote-tracking branch 'origin/master' into tsMerge 2024-07-09 10:36:03 +03:00
Riku Rauhala dacb0cec03 [Students] Prevent crashing for non-existent student number 2024-07-09 10:33:00 +03:00
Riku Rauhala 1e63739c41 [Bachelor Honours] Use consistent spelling 2024-07-09 10:24:38 +03:00
Riku Rauhala 6adf5cf583 Fix close to graduation 2024-07-08 20:51:57 +03:00
Riku Rauhala fead93529d Merge remote-tracking branch 'origin/master' into tsMerge 2024-07-08 17:13:59 +03:00
Riku Rauhala 524c92f098 Update models 2024-07-08 17:13:51 +03:00
Riku Rauhala f3197f2d75 Update kone and user model imports 2024-07-08 14:45:15 +03:00
Riku Rauhala 2adef388ef Update models
Add missing fields, rearrange to group closely related fields, fix incorrect datatypes
2024-07-08 12:01:03 +03:00
Riku RauhalaandGitHub 26755bbdfe Merge pull request #4563 from UniversityOfHelsinkiCS/OOD-22
[Backend] Added ignoredFacultyCodes to facultyCodes
2024-07-08 11:33:07 +03:00
Matti Räsänen aa71c8cb1f [Backend] Renamed allFacultyCodes to facultyCodes 2024-07-08 10:31:24 +03:00
Riku Rauhala 5badb32fd8 Add types to the User model 2024-07-05 17:49:40 +03:00
Valtteri Kantanen 3a0d16aae8 Fix formatting for BullMQ job durations 2024-07-05 15:14:58 +03:00
Riku Rauhala d0b30e8c38 Add types for kone models 2024-07-05 14:56:32 +03:00
Matti Räsänen d41877fe52 [Backend][analyticsService] Filtering out ignoredFacultyCodes from allFacultyCodes 2024-07-05 13:15:23 +03:00
Riku Rauhala 4f00ca52bd Merge remote-tracking branch 'origin/master' into tsMerge 2024-07-05 09:55:28 +03:00
Riku Rauhala 09fe88bd7d Extract enum CreditTypeCode 2024-07-05 09:25:16 +03:00
Valtteri Kantanen 032c9463dc Add better logging for BullMQ jobs 2024-07-04 16:28:33 +03:00
Valtteri Kantanen 6000ac9f3e Send errors from study programme and faculty refresh jobs to Sentry 2024-07-04 16:28:08 +03:00
Riku Rauhala fa1c23a731 Replace CREDIT_TYPE_CODES with enum CreditTypeCode 2024-07-04 15:40:00 +03:00
Riku Rauhala 3d2dd2b77a Sort imports and format code 2024-07-04 15:40:00 +03:00
Valtteri Kantanen b8e37ce791 [Study programme overview] Fix transferred stats calculation for combined programme 2024-07-04 15:21:40 +03:00
Valtteri Kantanen 6a5f8afd4e Add possibility to remove waiting jobs from BullMQ queue in the Updater view 2024-07-04 14:06:10 +03:00
Matti Räsänen 8dcc9e60a7 [Backend] Updated facultyCodes to organisationConstants 2024-07-04 13:21:13 +03:00
ShootingStar91 1a66308148 Fix faulty columns 2024-07-04 13:05:52 +03:00
Matti Räsänen a1a4f5e426 Merge branch 'master' into OOD-22 2024-07-04 12:42:54 +03:00
Matti Räsänen 892f428ee2 [Backend] Added ignoredFacultyCodes to facultyCodes module 2024-07-04 12:42:28 +03:00
Riku RauhalaandGitHub e630693376 Merge pull request #4562 from UniversityOfHelsinkiCS/OOD-31
[Backend][Updater] Renamed facultyCodes to organisationConstants and moved universityOrgId there
2024-07-04 12:31:46 +03:00
Riku Rauhala 1af902569c [Updater] Rename hyStudyModules to organizationStudyModules 2024-07-04 11:58:29 +03:00
ShootingStar91 ea84c668c7 Fix missing/faulty columns in models 2024-07-04 11:55:47 +03:00
ShootingStar91 fd7a34fd39 Fix broken import in studets service 2024-07-04 11:55:32 +03:00
Valtteri Kantanen 92aa1d5120 [Study programme overview] Only include graduations from correct years 2024-07-04 11:51:32 +03:00
Riku Rauhala ee43ec89ac Use consistent spelling of licentiate 2024-07-04 10:56:40 +03:00
Valtteri Kantanen c88029c5a1 [Population statistics] Try to fix tests 2024-07-03 18:17:52 +03:00
Valtteri Kantanen 3dd40cbec9 Change completed_credits in studyplan table from an integer into a double 2024-07-03 17:17:00 +03:00
Valtteri Kantanen 7d9076317c [Students] Make 'secondary' labels smaller 2024-07-03 15:08:34 +03:00
Valtteri Kantanen bdc3dd19c4 Add indexes to sis_study_rights and sis_study_right_elements tables in sis-db 2024-07-03 15:07:46 +03:00
Matti Räsänen fb728d5796 [Backend][Updater] Removed universityOrgId from organisationConstans and added it to updater config file. Refactored updater to use it from config. 2024-07-03 12:41:25 +03:00
Valtteri Kantanen a20887e5ca [Study programmes] Rename isOldOrObsolete to isRelevantProgramme 2024-07-03 11:54:26 +03:00
Riku Rauhala 1703712e48 [Documentation] Fix a typo 2024-07-03 11:30:52 +03:00
Riku Rauhala 583685f8af [Documentation] Add phase to numeric_types.md 2024-07-03 11:29:48 +03:00
Matti Räsänen ea21b08a9e [Backend][Updater] Renamed facultyCodes to organisationConstants and moved universityOrgId there. Refactored updater to use universityOrgId from new location organisationConstants 2024-07-03 11:22:08 +03:00
Valtteri Kantanen 85c662c939 [Study programme overview] Make getStartDate function simpler, it doesn't need study programme 2024-07-03 11:11:24 +03:00
Valtteri Kantanen 04c78d4c15 [Updater] Remove study track from SISStudyRightElement if it's not in a newer snapshot with the same programme 2024-07-03 10:44:43 +03:00
Valtteri Kantanen a87098210d [Updater] Add missing study track codes to studyTrack field of SISStudyRightElement 2024-07-03 10:43:58 +03:00
Valtteri Kantanen 20cc1c8407 [Study programme overview] Remove extra parameters from functions 2024-07-03 10:42:21 +03:00
Riku Rauhala ad7cb80ca7 [Documentation] Update numeric types
Add missing types and their explanations, format nicely
2024-07-02 16:50:10 +03:00
Valtteri Kantanen dfc6ab4739 [Study programme overview] Fix tests 2024-07-02 16:14:09 +03:00
Valtteri Kantanen 3bbb9916c7 [Study programme overview] Fix tests 2024-07-02 15:59:21 +03:00
Valtteri Kantanen fe822d0bd5 [Study programme overview] Use new models to calculate average graduation times 2024-07-02 15:39:25 +03:00
Valtteri Kantanen f1c8e955c5 [Study programme overview] Remove duplicated code 2024-07-02 11:19:48 +03:00
Valtteri Kantanen f4cf046958 [Close to graduation] Fix semesters present 2024-07-02 11:18:06 +03:00
Valtteri Kantanen 5dfee0c538 [Study programme overview] Fix tests 2024-07-01 17:26:21 +03:00
Valtteri Kantanen 55f2cca6c9 [Study programme overview] Use new models in transferred away/to stats 2024-07-01 17:11:50 +03:00
Valtteri Kantanen 76583491b9 [Study programme overview] Use new models to calculate graduation stats 2024-07-01 15:31:09 +03:00
Valtteri Kantanen 78c1ae84ea [Update] Fix SISStudyRightElements with earlier endDate then startDate 2024-07-01 15:24:46 +03:00
Valtteri Kantanen 5109174b26 [Updater] Add degreeProgrammeType to ProgrammeModules 2024-07-01 12:25:26 +03:00
Valtteri Kantanen 34b297de4e [Updater] Add degreeProgrammeType to SISStudyRightElements 2024-07-01 11:49:31 +03:00
Valtteri Kantanen 9f70c7b8f0 [Study programme overview] Simplify handling of progress criteria 2024-07-01 10:54:06 +03:00
Valtteri Kantanen 630fedc9ab Update local jami-db to postgres 15 2024-07-01 10:54:06 +03:00
Valtteri Kantanen 51a40c75e7 [Students] Fix page crashing 2024-07-01 10:54:06 +03:00
Riku Rauhala a0642b515b [Backend] Extract function isOldOrObsolete
Eliminating copy-paste
2024-07-01 10:29:39 +03:00
arttu fc69f74264 Fix couple type errors 2024-06-28 16:24:41 +03:00
arttu 1298b90604 Merge some more changes 2024-06-28 16:09:20 +03:00
arttu ae273e846d Merge master to ts 2024-06-28 15:55:28 +03:00
192 changed files with 7380 additions and 4259 deletions
+9 -1
View File
@@ -21,6 +21,7 @@
"cypress/no-assigning-return-values": "off",
"cypress/unsafe-to-chain-command": "off",
"default-param-last": "off",
"import/extensions": ["error", "never"],
"import/order": [
"error",
{
@@ -72,5 +73,12 @@
"prettier/prettier": "error",
"quotes": ["error", "single", { "avoidEscape": true }]
},
"ignorePatterns": ["node_modules/", "build/", "**/*migrations*/**"]
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
},
"ignorePatterns": ["build/", "dist/", "instrumented/", "node_modules/", "**/*migrations*/**"]
}
+1
View File
@@ -4,6 +4,7 @@ on:
push:
branches:
- master
- tsMerge
paths:
- '.github/workflows/test.yaml'
- '.github/workflows/staging.yaml'
+6
View File
@@ -16,6 +16,9 @@ pids
*.seed
*.pid.lock
# Production
dist/
# Dependency directories
jspm_packages/
node_modules/
@@ -55,3 +58,6 @@ instrumented/
# pycharm
.idea
# fd specific
environments/
+3 -1
View File
@@ -100,7 +100,9 @@ describe('Population Statistics tests', () => {
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.contains('Students (170)').click()
})
cy.get('[data-cy=student-table-tabs]').contains('Tags').click()
cy.get('[data-cy=student-table-tabs]').within(() => {
cy.contains('Tags').click()
})
cy.contains('No tags defined. You can define them here.').find('a').click()
cy.contains('Tags').click()
cy.contains('Tietojenkäsittelytieteen kandiohjelma')
+13 -11
View File
@@ -131,8 +131,8 @@ describe('Studyprogramme overview', () => {
// [Year, Started studying, Accepted, Graduated, Transferred away, Transferred to]
...years.map(year => [year, 0, 0, 0, 0, 0]),
['2021 - 2022', 0, 0, 0, 0, 0],
['2020 - 2021', 12, 10, 6, 3, 2],
['2019 - 2020', 96, 86, 38, 1, 0],
['2020 - 2021', 12, 10, 6, 1, 2],
['2019 - 2020', 96, 86, 38, 3, 0],
['2018 - 2019', 161, 161, 8, 0, 1],
['2017 - 2018', 159, 171, 1, 0, 0],
]
@@ -153,9 +153,11 @@ describe('Studyprogramme overview', () => {
it('Basic information graphs render', () => {
cy.get('[data-cy=Graph-StudentsOfTheStudyprogramme')
.should('contain', 'Started')
.should('contain', 'Started studying')
.should('contain', 'Accepted')
.should('contain', 'Graduated')
.should('contain', 'Transferred away')
.should('contain', 'Transferred to')
cy.get('[data-cy=Graph-CreditsProducedByTheStudyprogramme')
.should('contain', 'Degree students')
@@ -176,11 +178,11 @@ describe('Studyprogramme overview', () => {
cy.get('[data-cy=GraduationTimeToggle]').click()
cy.get('[data-cy=graduation-times-graphBachelor]').within(() => {
cy.contains('Graduation year')
cy.contains('2019')
cy.contains('17 graduated').trigger('mouseover')
cy.contains('study time: 23 months')
cy.contains('17 graduated on time')
cy.contains('0 graduated max year overtime')
cy.contains('2020')
cy.contains('35 graduated').trigger('mouseover')
cy.contains('median study time: 34 months')
cy.contains('30 graduated on time')
cy.contains('5 graduated max year overtime')
})
cy.get('[data-cy=Graph-ProgrammesBeforeOrAfter')
@@ -206,9 +208,9 @@ describe('Studyprogramme overview', () => {
cy.contains('Graduation year')
cy.contains('2020')
cy.contains('24 graduated').trigger('mouseover')
cy.contains('study time: 22 months')
cy.contains('20 graduated on time')
cy.contains('4 graduated max year overtime')
cy.contains('study time: 22.5 months')
cy.contains('19 graduated on time')
cy.contains('5 graduated max year overtime')
})
cy.get('[data-cy=graduation-times-graphBachelor]').within(() => {
+1 -1
View File
@@ -81,7 +81,7 @@ services:
jami-db:
<<: *defaults
image: postgres:13.15
image: postgres:15.7
environment:
- PGDATA=/data
- POSTGRES_PASSWORD=postgres
+28 -35
View File
@@ -45,8 +45,8 @@ services:
ports:
- 8080:8080
volumes:
- backend-nodemod:/opt/app-root/src/node_modules
- ./services/backend/:/opt/app-root/src
- ./services/backend/src:/opt/app-root/src/src
- ./environments/ha:/opt/app-root/config
frontend:
<<: *defaults
@@ -57,10 +57,9 @@ services:
ports:
- 3000:3000
environment:
SERVICE_PROVIDER: 'Fd'
REACT_APP_SERVICE_PROVIDER: 'Fd'
volumes:
- frontend-nodemod:/opt/app-root/src/node_modules
- ./services/frontend/:/opt/app-root/src
- ./services/frontend/src/:/opt/app-root/src/src
kone-db:
<<: *defaults
@@ -70,6 +69,8 @@ services:
POSTGRES_DB: kone-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
ports:
- 12349:5432
volumes:
- kone-db-data:/data
@@ -97,19 +98,6 @@ services:
ports:
- 12345:5432
sis-importer-db:
<<: *defaults
container_name: sis-importer-db
environment:
PGDATA: /data
POSTGRES_DB: sis-importer-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
volumes:
- sis-importer-db-data:/data
ports:
- 12346:5432
sis-updater-nats:
<<: *defaults
command: '-cid sis-updater-nats -m 8222 -p 4222 --max_channels 1000 --max_age 2h --hb_fail_count 2 --hb_timeout 10s --hb_interval 10s --auth dev --file_slice_max_bytes 0 --file_slice_max_age 60s --max_msgs 0 -store file -dir datastore'
@@ -122,23 +110,24 @@ services:
<<: *build_defaults
context: updater/sis-updater-scheduler
container_name: sis-updater-scheduler
extra_hosts:
- 'host.docker.internal:host-gateway'
depends_on:
- redis
- sis-importer-db
- sis-updater-nats
environment:
REDIS_URI: //redis:6379
SECRET_TOKEN: dev
SIS_IMPORTER_DATABASE: sis-importer-db
SIS_IMPORTER_HOST: sis-importer-db
SIS_IMPORTER_PASSWORD: postgres
SIS_IMPORTER_USER: postgres
SIS_IMPORTER_HOST: ${SIS_IMPORTER_DB_HOST:-host.docker.internal}
SIS_IMPORTER_PORT: ${SIS_IMPORTER_DB_PORT:-15234}
SIS_IMPORTER_DATABASE: importer-db
SIS_IMPORTER_PASSWORD: dev
SIS_IMPORTER_USER: dev
SIS_NATS_TOKEN: dev
SIS_NATS_URI: sis-updater-nats://sis-updater-nats:4222
ports:
- 8082:8082
volumes:
- sis-updater-scheduler-nodemod:/opt/app-root/src/node_modules
- ./updater/sis-updater-scheduler/:/opt/app-root/src
sis-updater-worker:
@@ -149,20 +138,22 @@ services:
depends_on:
- redis
- sis-db
- sis-importer-db
- sis-updater-nats
extra_hosts:
- 'host.docker.internal:host-gateway'
environment:
DB_URL: postgres://postgres@sis-db:5432/sis-db
REDIS_URI: //redis:6379
SIS_IMPORTER_DATABASE: sis-importer-db
SIS_IMPORTER_HOST: sis-importer-db
SIS_IMPORTER_PASSWORD: postgres
SIS_IMPORTER_USER: postgres
SIS_IMPORTER_HOST: ${SIS_IMPORTER_DB_HOST:-host.docker.internal}
SIS_IMPORTER_PORT: ${SIS_IMPORTER_DB_PORT:-15234}
SIS_IMPORTER_DATABASE: importer-db
SIS_IMPORTER_PASSWORD: dev
SIS_IMPORTER_USER: dev
SIS_PASSWORD: postgres
SIS_NATS_TOKEN: dev
SIS_NATS_URI: sis-updater-nats://sis-updater-nats:4222
ROOT_ORG_ID: ${ROOT_ORG_ID}
volumes:
- sis-updater-worker-nodemod:/opt/app-root/src/node_modules
- ./updater/sis-updater-worker/:/opt/app-root/src
user-db:
@@ -179,12 +170,14 @@ services:
- 12347:5432
volumes:
backend-nodemod:
frontend-nodemod:
kone-db-data:
redis-data:
sis-db-data:
sis-importer-db-data:
sis-updater-scheduler-nodemod:
sis-updater-worker-nodemod:
user-db-data:
networks:
default:
name: oodikone
importer:
name: importer_network
external: true
+2 -1
View File
@@ -28,6 +28,7 @@ services:
- user-db
env_file:
- .env
tty: true
environment:
DB_URL_KONE: postgres://postgres@kone-db:5432/kone-db
DB_URL_USER: postgres://postgres@user-db:5432/user-db
@@ -186,7 +187,7 @@ services:
jami-db:
<<: *defaults
image: postgres:13.15
image: postgres:15.7
environment:
- PGDATA=/data
- POSTGRES_PASSWORD=postgres
+82 -25
View File
@@ -1,48 +1,105 @@
# Mystery numbers
# Numeric types
Sis data uses some numeric classifiers that are not self-explanatory and (most) previously needed to be deciphered from existing code.
Some mystery numbers are gathered here for convenience. Feel free to add anything that could be useful. Remember to use the spelling used in code to enable project wide search.
Data used in Oodikone contains some numeric types (or _magic numbers_) that are not self-explanatory. The values of the numeric types are gathered here for convenience and quick reference.
## extentcode
## Extent code
(from **studyright**)
Appears in model Studyright (db: `studyright`) as _extentcode_ (db: `extentcode`) and in model SISStudyRight (db: `sis_study_rights`) as _extentCode_ (db: `extent_code`). Values are defined in the database table `studyright_extents`.
These are actually defined in db table studyright_extents. Tells studyright level or type, eg. bachelor's degree or non-degree studies.
| value | meaning |
| ----: | :-------------------------------------------------- |
| 1 | Bachelor's degree |
| 2 | Master's degree |
| 3 | Licentiate |
| 4 | Doctor |
| 5 | Bachelor's and Master's degree |
| 6 | Continuing education |
| 7 | Exchange studies |
| 9 | Open University studies |
| 13 | Non-degree pedagogical studies for teachers |
| 14 | Contract training |
| 16 | Studies for secondary school students |
| 18 | Specialisation studies |
| 22 | Non-degree programme for special education teachers |
| 23 | Specialist training in medicine and dentistry |
| 31 | Summer and winter school |
| 34 | Exchange studies (postgraduate studies) |
| 99 | Non-Degree studies |
## prioritycode
## Priority code
(from **studyright**)
> [!WARNING]
> Obsolete, to be deprecated
1: Primary
Appears in model Studyright (db: `studyright`) as _prioritycode_ (db: `prioritycode`).
2: Secondary
| value | meaning | notes |
| ----: | :-------- | :------------------------------------------------------------------------------------------ |
| 1 | Primary | |
| 2 | Secondary | |
| 5 | Rescinded | seemingly not in use anymore |
| 6 | Option | Ba+Ma students can add a master programme to their study plan before graduating as bachelor |
| 30 | Graduated | |
5: Rescinded (Seemingly not in use anymore)
## Type
6: Option (Bachelor+master studyright students can add a master programme to their study plan before graduation in bachelor programme: marked as option in Oodikone)
> [!WARNING]
> Obsolete, to be deprecated
30: Graduated
Appears in model ElementDetails (db: `element_details`) as _type_ (db: `type`).
## type
| value | meaning |
| ----: | :----------------------------------------------- |
| 20 | study programme |
| 30 | module, studytrack or kokonaisuus e.g. neurology |
(from **element_details**)
## Phase
20: A study programme
Appears in model SISStudyRightElement (db: `sis_study_right_elements`) as _phase_ (db: `phase`).
30: Module/Studytrack/kokonaisuus? Eg. Neurology
In Finland it is common for new students to be awarded the study right to both a bachelor's and a master's programme when they are admitted to the university. In Oodikone these two-phase study rights are marked with the [extent code](#extent-code) 5. In the study right elements of these study rights, phase 2 marks the second phase of the study right.
## enrollmenttype
For example: Student is awarded the study right in the bachelor's programme in computer science with the extent code 5. The study right element for study right in the bachelor's programme is marked with phase 1. After graduating from the bachelor's programme, the student continues their studies in the master's programme in computer science. The new study right element for the study right in the master's programme is marked with phase 2.
(from **semester_enrollments**)
All study right elements for other study rights (extent code other than 5) are marked with phase 1.
1: Present
The level of the programme (bachelor's, master's etc.) of the study right can be directly checked from degreeProgrammeType (db: `degree_programme_type`) in model SISStudyRightElement (db: `sis_study_right_elements`).
2: Absent
| value | meaning |
| ----: | :------- |
| 1 | any |
| 2 | master's |
3: Inactive (neither 1 or 2 has been declared)
## Enrollment type
## credittypecode
Appears in model SemesterEnrollment (db: `semester_enrollments`) as _enrollmenttype_ (db: `enrollmenttype`).
(from **credit**)
| value | meaning | notes |
| ----: | :------- | :------------------------------- |
| 1 | Present | |
| 2 | Absent | |
| 3 | Inactive | neither 1 or 2 has been declared |
Defined in credit_types db table. Completed/Improved/Transferred/Failed
## Credit type code
Appears in model Credit (db: `credit`) as _credittypecode_ (db: `credittypecode`). Values are defined in the database table `credit_types`.
Completed/Improved/Transferred/Failed
| value | meaning |
| ----: | :--------------- |
| 4 | Completed |
| 7 | Improved (grade) |
| 9 | Transferred |
| 10 | Failed |
## Gender code
Appears in model Student (db: `student`) as _gender_code_ (db: `gender_code`).
| value | meaning |
| ----: | :------ |
| 0 | Unknown |
| 1 | Male |
| 2 | Female |
| 3 | Other |
+5 -11
View File
@@ -1,19 +1,12 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"extends": ["config:recommended"],
"packageRules": [
{
"groupName": "all non-major dependencies",
"groupSlug": "all-minor-patch",
"matchPackagePatterns": [
"*"
],
"matchUpdateTypes": [
"minor",
"patch"
]
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"matchDatasources": ["docker"],
@@ -21,5 +14,6 @@
"versionCompatibility": "^(?<version>[^-]+)(?<compatibility>-.*)?$",
"versioning": "node"
}
]
],
"rebaseWhen": "never"
}
+7 -3
View File
@@ -21,8 +21,12 @@ ENV SENTRY_ENVIRONMENT=$SENTRY_ENVIRONMENT
ARG SENTRY_RELEASE
ENV SENTRY_RELEASE=$SENTRY_RELEASE
COPY ./package* ./
RUN npm ci --omit-dev
COPY ./package*.json ./
RUN npm ci --include=dev
COPY . .
CMD ["node", "index.js"]
RUN npm run build
CMD ["node", "dist/index.js"]
+1 -1
View File
@@ -13,4 +13,4 @@ COPY ./package* ./
RUN npm ci
COPY . .
CMD ["node_modules/.bin/nodemon", "index.js"]
CMD ["node_modules/.bin/tsx", "watch", "--clear-screen=false", "index.js"]
+3
View File
@@ -4,3 +4,6 @@ const logger = require('./src/util/logger')
process.on('unhandledRejection', reason => {
logger.error({ message: 'unhandledRejection: ', meta: reason })
})
process.on('SIGTERM', () => process.exit())
process.on('SIGINT', () => process.exit())
+2811 -11
View File
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -4,6 +4,7 @@
"scripts": {
"start": "node --max_old_space_size=4096 index.js",
"dev": "nodemon --max_old_space_size=4096 index.js",
"build": "tsc",
"rapodiff": "node /opt/app-root/src/src/rapodiff.js"
},
"dependencies": {
@@ -25,14 +26,23 @@
"papaparse": "^5.4.1",
"pg": "^8.11.5",
"redis": "^3.1.2",
"reflect-metadata": "^0.2.2",
"sequelize": "^6.37.3",
"sequelize-typescript": "^2.1.6",
"umzug": "^2.3.0",
"unfuck-utf8-headers-middleware": "^1.0.1",
"winston": "^3.8.2",
"winston-gelf-transporter": "^1.0.2"
},
"devDependencies": {
"nodemon": "^3.1.0"
"@types/lodash": "^4.17.6",
"@types/node": "^20.14.2",
"@types/umzug": "^2.3.7",
"@types/validator": "^13.12.0",
"nodemon": "^3.1.0",
"ts-node": "^10.9.2",
"tsx": "^4.15.1",
"typescript": "^5.4.5"
},
"overrides": {
"winston-gelf-transporter": {
+2 -1
View File
@@ -41,7 +41,7 @@ const serviceProvider = process.env.SERVICE_PROVIDER || 'Toska'
const configLogoutUrl = serviceProvider === 'Toska' ? undefined : process.env.LOGOUT_URL
// Other stuff
const { DB_URL_KONE, DB_URL_USER, SECRET_TOKEN, SIS_UPDATER_URL, CRYPT_KEY } = process.env
const { DB_URL_KONE, DB_URL_USER, SECRET_TOKEN, SIS_DB_URL, SIS_UPDATER_URL, CRYPT_KEY } = process.env
let DB_MAX_CONNECTIONS = parseInt(process.env.DB_MAX_CONNECTIONS, 10)
if (Number.isNaN(DB_MAX_CONNECTIONS)) {
@@ -72,6 +72,7 @@ module.exports = {
USER_PASSWORD,
SIS_PASSWORD,
requiredGroup,
SIS_DB_URL,
SIS_UPDATER_URL,
runningInCI,
sentryRelease,
@@ -1,4 +1,4 @@
const facultyCodes = [
export const facultyCodes = [
'H10',
'H20',
'H30',
@@ -18,9 +18,6 @@ const facultyCodes = [
'Y01',
]
const magicFacultyCode = 'H50'
export const magicFacultyCode = 'H50'
module.exports = {
facultyCodes,
magicFacultyCode,
}
export const ignoredFacultyCodes = ['Y', 'H99', 'Y01', 'H92', 'H930']
@@ -1,6 +1,6 @@
// Map programme codes to "letter indentifier"
const codes = {
export const programmeCodes = {
KH10_001: 'TUK',
MH10_001: 'TUM',
KH20_001: 'ON',
@@ -136,5 +136,3 @@ const codes = {
T923106: 'MATRENA',
T923107: 'DOCS',
}
module.exports = { codes }
@@ -1,19 +1,56 @@
const EventEmitter = require('events')
const Sequelize = require('sequelize')
const Umzug = require('umzug')
import EventEmitter from 'events'
import { Sequelize } from 'sequelize-typescript'
import Umzug from 'umzug'
const conf = require('../conf-backend')
const logger = require('../util/logger')
import conf from '../conf-backend'
import {
Course,
CourseProvider,
CourseType,
Credit,
CreditTeacher,
CreditType,
ElementDetail,
Enrollment,
Organization,
ProgrammeModule,
ProgrammeModuleChild,
Semester,
SemesterEnrollment,
SISStudyRight,
SISStudyRightElement,
Student,
Studyplan,
Studyright,
StudyrightElement,
StudyrightExtent,
Teacher,
Transfer,
} from '../models'
import {
CustomPopulationSearch,
ExcludedCourse,
OpenUniPopulationSearch,
ProgressCriteria,
StudyGuidanceGroupTag,
StudyProgrammePin,
Tag,
TagStudent,
} from '../models/kone'
import { User } from '../models/user'
const { SIS_DB_URL } = process.env
import logger from '../util/logger'
class DbConnection extends EventEmitter {
sequelize: Sequelize
RETRY_ATTEMPTS: number
established: boolean
constructor() {
super()
this.RETRY_ATTEMPTS = 15
this.established = false
this.sequelize = new Sequelize(SIS_DB_URL, {
this.sequelize = new Sequelize(conf.SIS_DB_URL, {
dialect: 'postgres',
pool: {
max: 25,
@@ -23,6 +60,30 @@ class DbConnection extends EventEmitter {
},
logging: false,
password: conf.SIS_PASSWORD,
models: [
Course,
CourseProvider,
CourseType,
Credit,
CreditTeacher,
CreditType,
ElementDetail,
Enrollment,
Organization,
ProgrammeModule,
ProgrammeModuleChild,
Semester,
SemesterEnrollment,
SISStudyRight,
SISStudyRightElement,
Student,
Studyplan,
Studyright,
StudyrightElement,
StudyrightExtent,
Teacher,
Transfer,
],
})
}
@@ -48,9 +109,18 @@ class DbConnection extends EventEmitter {
// Old-style kone + user db connections
const sequelizeKone = new Sequelize(conf.DB_URL_KONE, {
schema: conf.DB_SCHEMA_KONE,
searchPath: conf.DB_SCHEMA_KONE,
logging: false,
password: conf.KONE_PASSWORD,
models: [
CustomPopulationSearch,
ExcludedCourse,
OpenUniPopulationSearch,
ProgressCriteria,
StudyGuidanceGroupTag,
StudyProgrammePin,
Tag,
TagStudent,
],
})
sequelizeKone.query(`SET SESSION search_path to ${conf.DB_SCHEMA_KONE}`)
@@ -58,16 +128,17 @@ sequelizeKone.query(`SET SESSION search_path to ${conf.DB_SCHEMA_KONE}`)
const sequelizeUser = new Sequelize(conf.DB_URL_USER, {
logging: false,
password: conf.USER_PASSWORD,
models: [User],
})
const initializeDatabaseConnection = async () => {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
const rounds = 60
for (const [seq, dbName] of [
[sequelizeKone, 'kone-db'],
[sequelizeUser, 'user-db'],
]) {
] as const) {
logger.info(`Connecting to ${dbName}...`)
for (let round = 1; round <= rounds; round++) {
try {
@@ -96,12 +167,11 @@ const initializeDatabaseConnection = async () => {
params: [seq.getQueryInterface(), Sequelize],
path: `${process.cwd()}/src/database/${migrationsFolder}`,
pattern: /\.js$/,
schema,
},
})
try {
const migrations = await migrator.up()
logger.info({ message: `${dbName} migrations up to date: `, meta: migrations.map(m => m.file) })
logger.info({ message: `${dbName} migrations up to date: `, meta: migrations.map(migration => migration.file) })
} catch (error) {
logger.error({ message: `${dbName} migrations failed`, meta: error })
throw error
@@ -111,9 +181,4 @@ const initializeDatabaseConnection = async () => {
const dbConnections = new DbConnection()
module.exports = {
dbConnections,
sequelizeKone,
sequelizeUser,
initializeDatabaseConnection,
}
export { dbConnections, sequelizeKone, sequelizeUser, initializeDatabaseConnection }
@@ -29,7 +29,6 @@ CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
--
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
SET search_path = public, pg_catalog;
@@ -52,7 +51,6 @@ CREATE TABLE access_groups (
);
ALTER TABLE access_groups OWNER TO postgres;
--
-- Name: access_groups_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -66,7 +64,6 @@ CREATE SEQUENCE access_groups_id_seq
CACHE 1;
ALTER TABLE access_groups_id_seq OWNER TO postgres;
--
-- Name: access_groups_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
@@ -87,7 +84,6 @@ CREATE TABLE affiliations (
);
ALTER TABLE affiliations OWNER TO postgres;
--
-- Name: affiliations_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -101,7 +97,6 @@ CREATE SEQUENCE affiliations_id_seq
CACHE 1;
ALTER TABLE affiliations_id_seq OWNER TO postgres;
--
-- Name: affiliations_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
@@ -122,7 +117,6 @@ CREATE TABLE faculty_programmes (
);
ALTER TABLE faculty_programmes OWNER TO postgres;
--
-- Name: hibernate_sequence; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -136,7 +130,6 @@ CREATE SEQUENCE hibernate_sequence
CACHE 1;
ALTER TABLE hibernate_sequence OWNER TO postgres;
--
-- Name: hy_groups; Type: TABLE; Schema: public; Owner: postgres
@@ -150,7 +143,6 @@ CREATE TABLE hy_groups (
);
ALTER TABLE hy_groups OWNER TO postgres;
--
-- Name: hy_groups_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -164,7 +156,6 @@ CREATE SEQUENCE hy_groups_id_seq
CACHE 1;
ALTER TABLE hy_groups_id_seq OWNER TO postgres;
--
-- Name: hy_groups_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
@@ -183,7 +174,6 @@ CREATE TABLE sequence (
);
ALTER TABLE sequence OWNER TO postgres;
--
-- Name: user_accessgroup; Type: TABLE; Schema: public; Owner: postgres
@@ -198,7 +188,6 @@ CREATE TABLE user_accessgroup (
);
ALTER TABLE user_accessgroup OWNER TO postgres;
--
-- Name: user_accessgroup_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -212,7 +201,6 @@ CREATE SEQUENCE user_accessgroup_id_seq
CACHE 1;
ALTER TABLE user_accessgroup_id_seq OWNER TO postgres;
--
-- Name: user_accessgroup_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
@@ -234,7 +222,6 @@ CREATE TABLE user_affiliation (
);
ALTER TABLE user_affiliation OWNER TO postgres;
--
-- Name: user_affiliation_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -248,7 +235,6 @@ CREATE SEQUENCE user_affiliation_id_seq
CACHE 1;
ALTER TABLE user_affiliation_id_seq OWNER TO postgres;
--
-- Name: user_affiliation_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
@@ -269,7 +255,6 @@ CREATE TABLE user_elementdetails (
);
ALTER TABLE user_elementdetails OWNER TO postgres;
--
-- Name: user_faculties; Type: TABLE; Schema: public; Owner: postgres
@@ -283,7 +268,6 @@ CREATE TABLE user_faculties (
);
ALTER TABLE user_faculties OWNER TO postgres;
--
-- Name: user_hy_group; Type: TABLE; Schema: public; Owner: postgres
@@ -298,7 +282,6 @@ CREATE TABLE user_hy_group (
);
ALTER TABLE user_hy_group OWNER TO postgres;
--
-- Name: user_hy_group_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -312,7 +295,6 @@ CREATE SEQUENCE user_hy_group_id_seq
CACHE 1;
ALTER TABLE user_hy_group_id_seq OWNER TO postgres;
--
-- Name: user_hy_group_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
@@ -332,7 +314,6 @@ CREATE TABLE user_unit (
);
ALTER TABLE user_unit OWNER TO postgres;
--
-- Name: user_unit_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -346,7 +327,6 @@ CREATE SEQUENCE user_unit_id_seq
CACHE 1;
ALTER TABLE user_unit_id_seq OWNER TO postgres;
--
-- Name: user_unit_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
@@ -368,7 +348,6 @@ CREATE TABLE users (
);
ALTER TABLE users OWNER TO postgres;
--
-- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres
@@ -382,7 +361,6 @@ CREATE SEQUENCE users_id_seq
CACHE 1;
ALTER TABLE users_id_seq OWNER TO postgres;
--
-- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres
+8 -24
View File
@@ -1,16 +1,16 @@
const { CronJob } = require('cron')
const { isProduction, runningInCI } = require('./conf-backend')
const { getFaculties } = require('./services/faculty/facultyHelpers')
const { updateFacultyOverview, updateFacultyProgressOverview } = require('./services/faculty/facultyUpdates')
const { computeLanguageCenterData, LANGUAGE_CENTER_REDIS_KEY } = require('./services/languageCenterData')
const { faculties } = require('./services/organisations')
const {
findStudentsCloseToGraduation,
CLOSE_TO_GRADUATION_REDIS_KEY,
} = require('./services/populations/closeToGraduation')
const { redisClient } = require('./services/redis')
const { getCurrentSemester } = require('./services/semesters')
const { combinedStudyprogrammes } = require('./services/studyProgramme/studyProgrammeHelpers')
const { combinedStudyprogrammes, isRelevantProgramme } = require('./services/studyProgramme/studyProgrammeHelpers')
const { updateBasicView, updateStudytrackView } = require('./services/studyProgramme/studyProgrammeUpdates')
const { getAssociations, getProgrammesFromStudyRights, refreshAssociationsInRedis } = require('./services/studyrights')
const { findAndSaveTeachers } = require('./services/topteachers')
@@ -27,38 +27,22 @@ const refreshStudyrightAssociations = async () => {
const refreshFaculties = async () => {
logger.info('Adding jobs to refresh all faculties')
const facultyList = (await faculties()).filter(faculty => !['Y', 'H99', 'Y01', 'H92', 'H930'].includes(faculty.code))
for (const faculty of facultyList) {
const faculties = await getFaculties()
for (const faculty of faculties) {
jobMaker.faculty(faculty.code)
}
}
const refreshFaculty = async code => {
logger.info('Started updating faculty')
const start = new Date().getTime()
try {
await updateFacultyOverview(code, 'ALL')
await updateFacultyProgressOverview(code)
const time = new Date().getTime() - start
logger.info(`Updated faculty ${code} (took ${(time / 1000 / 60).toFixed(2)} minutes)`)
} catch (error) {
logger.error({ message: `Failed to update stats for faculty ${code} with type ALL`, meta: `${error}` })
}
await updateFacultyOverview(code, 'ALL')
await updateFacultyProgressOverview(code)
}
const refreshProgrammes = async () => {
logger.info('Refreshing studyprogramme and studytrack overview statistics for all programmes')
// Filters out old programmes and special ones like Fitech studies. Filters also out the programmes starting with
// 2_ or ending with _2. Those programmes are mapped to correct programme (studentProgrammeModuleFixer.js)
const programmes = await getProgrammesFromStudyRights()
const codes = programmes
.map(programme => programme.code)
.filter(
code =>
(code.includes('KH') && !code.startsWith('2_KH') && !code.endsWith('_2')) ||
(code.includes('MH') && !code.startsWith('2_MH') && !code.endsWith('_2')) ||
/^(T)[0-9]{6}$/.test(code)
)
const codes = programmes.map(programme => programme.code).filter(code => isRelevantProgramme(code))
// Ensure that studyright associations are refreshed before launching jobs, otherwise each job will do it
await getAssociations()
@@ -1,34 +0,0 @@
const { Model, BOOLEAN, DATE, INTEGER, JSONB, STRING } = require('sequelize')
const {
dbConnections: { sequelize },
} = require('../database/connection')
class SISStudyRight extends Model {}
SISStudyRight.init(
{
id: {
type: STRING,
primaryKey: true,
},
startDate: DATE,
endDate: DATE,
studyStartDate: DATE,
cancelled: BOOLEAN,
studentNumber: STRING,
extentCode: INTEGER,
admissionType: STRING,
semesterEnrollments: JSONB,
facultyCode: STRING,
createdAt: DATE,
updatedAt: DATE,
},
{
underscored: true,
sequelize,
tableName: 'sis_study_rights',
}
)
module.exports = SISStudyRight
@@ -0,0 +1,73 @@
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
HasMany,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { ExtentCode } from '../types/extentCode'
import { Organization } from './organization'
import { SISStudyRightElement } from './SISStudyRightElement'
import { Student } from './student'
import { Studyplan } from './studyplan'
@Table({
underscored: true,
tableName: 'sis_study_rights',
})
export class SISStudyRight extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@BelongsTo(() => Organization, { foreignKey: 'facultyCode', targetKey: 'code' })
organization: Organization
@HasMany(() => Studyplan)
studyPlans: Studyplan[]
@HasMany(() => SISStudyRightElement, { foreignKey: 'studyRightId' })
studyRightElements: SISStudyRightElement[]
@Column(DataType.DATE)
startDate: Date
@Column(DataType.DATE)
endDate: Date
@Column(DataType.DATE)
studyStartDate: Date
@Column(DataType.BOOLEAN)
cancelled: boolean
@ForeignKey(() => Student)
@Column(DataType.STRING)
studentNumber: string
@BelongsTo(() => Student)
student: Student
@Column(DataType.INTEGER)
extentCode: ExtentCode
@Column(DataType.STRING)
admissionType: string
@Column(DataType.JSONB)
semesterEnrollments: object
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
@@ -1,33 +0,0 @@
const { Model, BOOLEAN, DATE, INTEGER, JSONB, STRING } = require('sequelize')
const {
dbConnections: { sequelize },
} = require('../database/connection')
class SISStudyRightElement extends Model {}
SISStudyRightElement.init(
{
id: {
type: STRING,
primaryKey: true,
},
startDate: DATE,
endDate: DATE,
graduated: BOOLEAN,
phase: INTEGER,
studyRightId: STRING,
code: STRING,
name: JSONB,
studyTrack: JSONB,
createdAt: DATE,
updatedAt: DATE,
},
{
underscored: true,
sequelize,
tableName: 'sis_study_right_elements',
}
)
module.exports = SISStudyRightElement
@@ -0,0 +1,52 @@
import { BelongsTo, Column, CreatedAt, DataType, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { Phase } from '../types/phase'
import { SISStudyRight } from './SISStudyRight'
@Table({
underscored: true,
tableName: 'sis_study_right_elements',
})
export class SISStudyRightElement extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@BelongsTo(() => SISStudyRight, { foreignKey: 'studyRightId' })
studyRight: SISStudyRight
@Column(DataType.DATE)
startDate: Date
@Column(DataType.DATE)
endDate: Date
@Column(DataType.BOOLEAN)
graduated: boolean
@Column(DataType.INTEGER)
phase: Phase
@Column(DataType.STRING)
studyRightId: string
@Column(DataType.STRING)
code: string
@Column(DataType.JSONB)
name: object
@Column(DataType.JSONB)
studyTrack: object
@Column(DataType.STRING)
degreeProgrammeType: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-67
View File
@@ -1,67 +0,0 @@
const { Model, BOOLEAN, DATE, JSONB, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Course extends Model {}
Course.init(
{
id: {
type: STRING,
primaryKey: true,
},
code: {
type: STRING,
},
name: {
type: JSONB,
},
latest_instance_date: {
type: DATE,
},
is_study_module: {
type: BOOLEAN,
},
coursetypecode: {
type: STRING,
},
startdate: {
type: DATE,
},
enddate: {
type: DATE,
},
max_attainment_date: {
type: DATE,
},
min_attainment_date: {
type: DATE,
},
createdAt: {
field: 'created_at',
type: DATE,
},
updatedAt: {
field: 'updated_at',
type: DATE,
},
substitutions: {
type: JSONB,
},
course_unit_type: {
type: STRING,
},
mainCourseCode: {
field: 'main_course_code',
type: STRING,
},
},
{
underscored: false,
sequelize: dbConnections.sequelize,
modelName: 'course',
tableName: 'course',
}
)
module.exports = Course
+97
View File
@@ -0,0 +1,97 @@
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
DataType,
ForeignKey,
HasMany,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { Credit } from './credit'
import { CourseProvider } from './courseProvider'
import { CourseType } from './courseType'
import { Enrollment } from './enrollment'
import { Organization } from './organization'
@Table({
underscored: false,
modelName: 'course',
tableName: 'course',
})
export class Course extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@ForeignKey(() => Course)
@Column(DataType.STRING)
code: string
@ForeignKey(() => CourseType)
@Column(DataType.STRING)
coursetypecode: string
@BelongsTo(() => CourseType, { foreignKey: 'coursetypecode', targetKey: 'coursetypecode' })
courseType: CourseType
@HasMany(() => Credit, { foreignKey: 'course_id', sourceKey: 'id' })
credits: Credit[]
@HasMany(() => Enrollment, { foreignKey: 'course_id' })
enrollments: Enrollment[]
@BelongsToMany(() => Organization, () => CourseProvider, 'coursecode')
organizations: Organization[]
@Column(DataType.BOOLEAN)
is_study_module: boolean
@Column(DataType.JSONB)
name: object
@Column(DataType.DATE)
latest_instance_date: Date
@Column(DataType.DATE)
startdate: Date
@Column(DataType.DATE)
enddate: Date
@Column(DataType.DATE)
max_attainment_date: Date
@Column(DataType.DATE)
min_attainment_date: Date
@CreatedAt
@Column({
field: 'created_at',
type: DataType.DATE,
})
createdAt: Date
@UpdatedAt
@Column({
field: 'updated_at',
type: DataType.DATE,
})
updatedAt: Date
@Column(DataType.JSONB)
substitutions: object
@Column(DataType.STRING)
course_unit_type: string
@Column({
field: 'main_course_code',
type: DataType.STRING,
})
mainCourseCode: string
}
@@ -1,55 +0,0 @@
const { Model, DATE, JSONB, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class CourseProvider extends Model {}
CourseProvider.init(
{
composite: {
type: STRING,
unique: true,
},
coursecode: {
type: STRING,
references: {
model: 'course',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
shares: {
type: JSONB,
},
organizationcode: {
type: STRING,
references: {
model: 'organization',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'course_provider',
tableName: 'course_providers',
indexes: [
{
unique: true,
fields: ['coursecode', 'organizationcode'],
},
],
}
)
module.exports = CourseProvider
@@ -0,0 +1,40 @@
import { Column, CreatedAt, DataType, ForeignKey, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { Course } from './course'
import { Organization } from './organization'
@Table({
underscored: true,
modelName: 'course_provider',
tableName: 'course_providers',
indexes: [
{
unique: true,
fields: ['coursecode', 'organizationcode'],
},
],
})
export class CourseProvider extends Model {
@PrimaryKey
@Column(DataType.STRING)
composite: string
@ForeignKey(() => Course)
@Column(DataType.STRING)
coursecode: string
@Column(DataType.JSONB)
shares: object
@ForeignKey(() => Organization)
@Column(DataType.STRING)
organizationcode: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-31
View File
@@ -1,31 +0,0 @@
const { Model, DATE, JSONB, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class CourseType extends Model {}
CourseType.init(
{
coursetypecode: {
type: STRING,
primaryKey: true,
},
name: {
type: JSONB,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'course_type',
tableName: 'course_types',
}
)
module.exports = CourseType
+28
View File
@@ -0,0 +1,28 @@
import { Column, CreatedAt, DataType, HasMany, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { Course } from './course'
@Table({
underscored: true,
modelName: 'course_type',
tableName: 'course_types',
})
export class CourseType extends Model {
@PrimaryKey
@Column(DataType.STRING)
coursetypecode: string
@HasMany(() => Course)
courses: Course[]
@Column(DataType.JSONB)
name: object
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-102
View File
@@ -1,102 +0,0 @@
const { Model, BOOLEAN, DATE, DOUBLE, INTEGER, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Credit extends Model {}
Credit.init(
{
id: {
primaryKey: true,
type: STRING,
},
grade: {
type: STRING,
},
student_studentnumber: {
type: STRING,
references: {
model: 'student',
key: 'studentnumber',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
credits: {
type: DOUBLE,
},
createdate: {
type: DATE,
},
credittypecode: {
type: INTEGER,
references: {
model: 'credit_types',
key: 'credittypecode',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
attainment_date: {
type: DATE,
},
course_code: {
type: STRING,
},
course_id: {
type: STRING,
references: {
model: 'course',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
semester_composite: {
type: STRING,
references: {
model: 'semesters',
key: 'composite',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
semestercode: {
type: INTEGER,
},
isStudyModule: {
type: BOOLEAN,
},
org: {
type: STRING,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
language: {
type: STRING,
},
is_open: {
type: BOOLEAN,
},
studyright_id: {
type: STRING,
allowNull: true,
references: {
model: 'studyright',
key: 'studyrightid',
},
},
},
{
underscored: false,
sequelize: dbConnections.sequelize,
modelName: 'credit',
tableName: 'credit',
}
)
module.exports = Credit
+112
View File
@@ -0,0 +1,112 @@
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { CreditTypeCode } from '../types/creditTypeCode'
import { Course } from './course'
import { CreditTeacher } from './creditTeacher'
import { CreditType } from './creditType'
import { Semester } from './semester'
import { Student } from './student'
import { Studyright } from './studyright'
import { Teacher } from './teacher'
@Table({
underscored: false,
modelName: 'credit',
tableName: 'credit',
})
export class Credit extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@Column(DataType.STRING)
grade: string
@ForeignKey(() => Student)
@Column(DataType.STRING)
student_studentnumber: string
@BelongsTo(() => Student, { foreignKey: 'student_studentnumber', targetKey: 'studentnumber' })
student: Student
@BelongsTo(() => Semester, { foreignKey: 'semester_composite' })
semester: Semester
@Column(DataType.DOUBLE)
credits: number
@ForeignKey(() => CreditType)
@Column(DataType.INTEGER)
credittypecode: CreditTypeCode
@BelongsTo(() => CreditType, { foreignKey: 'credittypecode', targetKey: 'credittypecode' })
creditType: CreditType
@Column(DataType.DATE)
createdate: Date
@Column(DataType.DATE)
attainment_date: Date
@BelongsToMany(() => Teacher, () => CreditTeacher, 'credit_id')
teachers: Teacher[]
@Column(DataType.STRING)
course_code: string
@BelongsTo(() => Course, { foreignKey: 'course_id' })
course: Course
@ForeignKey(() => Course)
@Column(DataType.STRING)
course_id: string
@Column(DataType.STRING)
semester_composite: string
@Column(DataType.INTEGER)
semestercode: number
@Column(DataType.BOOLEAN)
isStudyModule: boolean
@Column(DataType.STRING)
org: string
@Column(DataType.STRING)
language: string
@Column(DataType.BOOLEAN)
is_open: boolean
@ForeignKey(() => Studyright)
@Column(DataType.STRING)
studyright_id: string
@BelongsTo(() => Studyright)
studyright: Studyright
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
static passed: ({ credittypecode }: { credittypecode: CreditTypeCode }) => boolean
static failed: ({ credittypecode }: { credittypecode: CreditTypeCode }) => boolean
static improved: ({ credittypecode }: { credittypecode: CreditTypeCode }) => boolean
static notUnnecessary: ({ credits }: { credits: number }) => boolean
}
@@ -1,46 +0,0 @@
const { Model, DATE, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class CreditTeacher extends Model {}
CreditTeacher.init(
{
composite: {
type: STRING,
unique: true,
},
credit_id: {
type: STRING,
references: {
model: 'credit',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
teacher_id: {
type: STRING,
references: {
model: 'teacher',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: false,
sequelize: dbConnections.sequelize,
modelName: 'credit_teacher',
tableName: 'credit_teachers',
}
)
module.exports = CreditTeacher
@@ -0,0 +1,42 @@
import {
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
Unique,
UpdatedAt,
} from 'sequelize-typescript'
import { Credit } from './credit'
import { Teacher } from './teacher'
@Table({
underscored: false,
modelName: 'credit_teacher',
tableName: 'credit_teachers',
})
export class CreditTeacher extends Model {
@PrimaryKey
@Unique
@Column(DataType.STRING)
composite: string
@ForeignKey(() => Credit)
@Column(DataType.STRING)
credit_id: string
@ForeignKey(() => Teacher)
@Column(DataType.STRING)
teacher_id: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-31
View File
@@ -1,31 +0,0 @@
const { Model, DATE, INTEGER, JSONB } = require('sequelize')
const { dbConnections } = require('../database/connection')
class CreditType extends Model {}
CreditType.init(
{
credittypecode: {
primaryKey: true,
type: INTEGER,
},
name: {
type: JSONB,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'credit_type',
tableName: 'credit_types',
}
)
module.exports = CreditType
+25
View File
@@ -0,0 +1,25 @@
import { Column, CreatedAt, DataType, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { CreditTypeCode } from '../types/creditTypeCode'
@Table({
underscored: true,
modelName: 'credit_type',
tableName: 'credit_types',
})
export class CreditType extends Model {
@PrimaryKey
@Column(DataType.INTEGER)
credittypecode: CreditTypeCode
@Column(DataType.JSONB)
name: object
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
@@ -1,26 +0,0 @@
const { Model, DATE, INTEGER, JSONB, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class ElementDetail extends Model {}
ElementDetail.init(
{
code: { type: STRING, primaryKey: true },
name: { type: JSONB },
type: { type: INTEGER },
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
sequelize: dbConnections.sequelize,
modelName: 'element_detail',
tableName: 'element_details',
}
)
module.exports = ElementDetail
@@ -0,0 +1,32 @@
import { Column, CreatedAt, DataType, HasMany, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { ElementDetailType } from '../types/elementDetailType'
import { StudyrightElement } from './studyrightElement'
@Table({
underscored: false,
modelName: 'element_detail',
tableName: 'element_details',
})
export class ElementDetail extends Model {
@PrimaryKey
@Column(DataType.STRING)
code: string
@Column(DataType.JSONB)
name: object
@Column(DataType.INTEGER)
type: ElementDetailType
@HasMany(() => StudyrightElement, { foreignKey: 'code', sourceKey: 'code' })
studyrightElements: StudyrightElement
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-78
View File
@@ -1,78 +0,0 @@
const { Model, BOOLEAN, DATE, INTEGER, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Enrollment extends Model {}
Enrollment.init(
{
id: {
primaryKey: true,
type: STRING,
},
studentnumber: {
type: STRING,
references: {
model: 'student',
key: 'studentnumber',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
course_code: {
type: STRING,
},
state: {
type: STRING,
},
enrollment_date_time: {
type: DATE,
},
course_id: {
type: STRING,
references: {
model: 'course',
key: 'id',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
semester_composite: {
type: STRING,
references: {
model: 'semesters',
key: 'composite',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
semestercode: {
type: INTEGER,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
is_open: {
type: BOOLEAN,
},
studyright_id: {
type: STRING,
allowNull: true,
references: {
model: 'studyright',
key: 'studyrightid',
},
},
},
{
underscored: false,
sequelize: dbConnections.sequelize,
modelName: 'enrollment',
tableName: 'enrollment',
}
)
module.exports = Enrollment
+78
View File
@@ -0,0 +1,78 @@
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { Course } from './course'
import { Semester } from './semester'
import { Student } from './student'
import { Studyright } from './studyright'
@Table({
underscored: false,
modelName: 'enrollment',
tableName: 'enrollment',
})
export class Enrollment extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@ForeignKey(() => Student)
@Column(DataType.STRING)
studentnumber: string
@BelongsTo(() => Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
student: Student
@Column(DataType.STRING)
course_code: string
@Column(DataType.STRING)
state: string
@Column(DataType.DATE)
enrollment_date_time: Date
@ForeignKey(() => Course)
@Column(DataType.STRING)
course_id: string
@ForeignKey(() => Semester)
@Column(DataType.STRING)
semester_composite: string
@BelongsTo(() => Semester, { foreignKey: 'semester_composite', targetKey: 'composite' })
semester: Semester
@Column(DataType.INTEGER)
semestercode: number
@Column(DataType.BOOLEAN)
is_open: boolean
@ForeignKey(() => Studyright)
@Column({ type: DataType.STRING, allowNull: true })
studyright_id: string
@BelongsTo(() => Studyright, { foreignKey: 'studyright_id', targetKey: 'studyrightid', constraints: false })
studyright: Studyright
@BelongsTo(() => Course, { foreignKey: 'course_id', targetKey: 'id' })
course: Course
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
@@ -1,38 +0,0 @@
const { Model, DATE, INTEGER, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class ExcludedCourse extends Model {}
ExcludedCourse.init(
{
id: {
primaryKey: true,
type: INTEGER,
autoIncrement: true,
},
programme_code: {
type: STRING,
},
course_code: {
type: STRING,
},
curriculum_version: {
type: STRING,
},
created_at: {
type: DATE,
},
updated_at: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'excluded_course',
tableName: 'excluded_courses',
}
)
module.exports = ExcludedCourse
-161
View File
@@ -1,161 +0,0 @@
const Course = require('./course')
const CourseProvider = require('./courseProvider')
const CourseType = require('./courseType')
const Credit = require('./credit')
const CreditTeacher = require('./creditTeacher')
const CreditType = require('./creditType')
const ElementDetail = require('./elementDetail')
const Enrollment = require('./enrollment')
const ExcludedCourse = require('./excludedCourse')
const Organization = require('./organization')
const ProgrammeModule = require('./programmeModule')
const ProgrammeModuleChild = require('./programmeModuleChild')
const Semester = require('./semester')
const SemesterEnrollment = require('./semesterEnrollment')
const SISStudyRight = require('./SISStudyRight')
const SISStudyRightElement = require('./SISStudyRightElement')
const Student = require('./student')
const Studyplan = require('./studyplan')
const Studyright = require('./studyright')
const StudyrightElement = require('./studyrightElement')
const StudyrightExtent = require('./studyrightExtent')
const Teacher = require('./teacher')
const Transfer = require('./transfer')
CourseType.hasMany(Course, { foreignKey: 'coursetypecode', sourceKey: 'coursetypecode' })
Course.belongsTo(CourseType, { foreignKey: 'coursetypecode', targetKey: 'coursetypecode' })
Course.belongsToMany(Organization, { through: CourseProvider, foreignKey: 'coursecode' })
Organization.belongsToMany(Course, { through: CourseProvider, foreignKey: 'organizationcode' })
SemesterEnrollment.belongsTo(Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
Student.hasMany(SemesterEnrollment, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
SemesterEnrollment.belongsTo(Semester, { foreignKey: 'semestercomposite', targetKey: 'composite' })
Semester.hasMany(SemesterEnrollment, { foreignKey: 'semestercomposite', sourceKey: 'composite' })
ProgrammeModule.belongsTo(Organization, { foreignKey: 'organization_id' })
Organization.hasMany(ProgrammeModule, { foreignKey: 'organization_id' })
Credit.notUnnecessary = credit => {
return credit.credits > 0 && credit.credits <= 12
}
const CREDIT_TYPE_CODES = {
PASSED: 4,
FAILED: 10,
IMPROVED: 7,
APPROVED: 9,
}
Credit.passed = ({ credittypecode }) =>
credittypecode === CREDIT_TYPE_CODES.PASSED || credittypecode === CREDIT_TYPE_CODES.APPROVED
Credit.failed = credit => credit.credittypecode === CREDIT_TYPE_CODES.FAILED
Credit.improved = credit => credit.credittypecode === CREDIT_TYPE_CODES.IMPROVED
Credit.belongsTo(Studyright, { foreignKey: 'studyright_id', targetKey: 'studyrightid', constraints: false })
Studyright.hasMany(Credit, { foreignKey: 'studyright_id', constraints: false })
Credit.belongsTo(Student, { foreignKey: 'student_studentnumber', targetKey: 'studentnumber' })
Student.hasMany(Credit, { foreignKey: 'student_studentnumber', sourceKey: 'studentnumber' })
Credit.belongsTo(Course, { foreignKey: 'course_id' })
Course.hasMany(Credit, { foreignKey: 'course_id' })
Credit.belongsTo(CreditType, { foreignKey: 'credittypecode', targetKey: 'credittypecode' })
Credit.belongsToMany(Teacher, { through: CreditTeacher, foreignKey: 'credit_id' })
Teacher.belongsToMany(Credit, { through: CreditTeacher, foreignKey: 'teacher_id' })
Organization.hasMany(Studyright, { foreignKey: 'facultyCode', sourceKey: 'code' })
Studyright.belongsTo(Organization, { foreignKey: 'facultyCode', targetKey: 'code' })
SISStudyRight.belongsTo(Organization, { foreignKey: 'facultyCode', targetKey: 'code' })
Organization.hasMany(SISStudyRight, { foreignKey: 'facultyCode', sourceKey: 'code' })
Studyplan.belongsTo(Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
Student.hasMany(Studyplan, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
Studyplan.belongsTo(Studyright, { foreignKey: 'studyrightid', targetKey: 'studyrightid' })
Studyright.hasMany(Studyplan, { foreignKey: 'studyrightid', sourceKey: 'studyrightid' })
Studyright.belongsTo(Student, { foreignKey: 'studentStudentnumber', targetKey: 'studentnumber' })
Student.hasMany(Studyright, { foreignKey: 'studentStudentnumber', sourceKey: 'studentnumber' })
SISStudyRight.belongsTo(Student, { foreignKey: 'studentNumber' })
Student.hasMany(SISStudyRight, { as: 'studyRights', foreignKey: 'studentNumber' })
StudyrightElement.belongsTo(Studyright, { foreignKey: 'studyrightid', targetKey: 'studyrightid' })
Studyright.hasMany(StudyrightElement, { foreignKey: 'studyrightid', sourceKey: 'studyrightid' })
SISStudyRightElement.belongsTo(SISStudyRight, { as: 'studyRight', foreignKey: 'studyRightId' })
SISStudyRight.hasMany(SISStudyRightElement, { as: 'studyRightElements', foreignKey: 'studyRightId' })
Studyplan.belongsTo(SISStudyRight, { as: 'studyRight', foreignKey: 'sis_study_right_id' })
SISStudyRight.hasMany(Studyplan, { as: 'studyPlans', foreignKey: 'sis_study_right_id' })
StudyrightElement.belongsTo(ElementDetail, { foreignKey: 'code', targetKey: 'code' })
ElementDetail.hasMany(StudyrightElement, { foreignKey: 'code', sourceKey: 'code' })
StudyrightElement.belongsTo(Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
Student.hasMany(StudyrightElement, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
StudyrightExtent.hasMany(Studyright, { foreignKey: 'extentcode', sourceKey: 'extentcode' })
Studyright.belongsTo(StudyrightExtent, { foreignKey: 'extentcode', targetKey: 'extentcode' })
Credit.belongsTo(Semester, { foreignKey: { name: 'semester_composite', allowNull: false } })
Transfer.belongsTo(Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
Student.hasMany(Transfer, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
Transfer.belongsTo(Studyright, { foreignKey: 'studyrightid', targetKey: 'studyrightid' })
Studyright.hasMany(Transfer, { foreignKey: 'studyrightid', sourceKey: 'studyrightid' })
Transfer.belongsTo(ElementDetail, { as: 'source', foreignKey: 'sourcecode' })
Transfer.belongsTo(ElementDetail, { as: 'target', foreignKey: 'targetcode' })
Enrollment.belongsTo(Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
Enrollment.belongsTo(Studyright, { foreignKey: 'studyright_id', targetKey: 'studyrightid', constraints: false })
Studyright.hasMany(Enrollment, { foreignKey: 'studyright_id', constraints: false })
Student.hasMany(Enrollment, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
Enrollment.belongsTo(Course, { foreignKey: 'course_id' })
Course.hasMany(Enrollment, { foreignKey: 'course_id' })
Enrollment.belongsTo(Semester, { foreignKey: { name: 'semester_composite', allowNull: false } })
ProgrammeModule.belongsToMany(ProgrammeModule, {
as: 'parents',
through: ProgrammeModuleChild,
foreignKey: 'child_id',
})
ProgrammeModule.belongsToMany(ProgrammeModule, {
as: 'children',
through: ProgrammeModuleChild,
foreignKey: 'parent_id',
})
Organization.hasMany(Organization, { foreignKey: 'parent_id', as: 'children' })
module.exports = {
Course,
CourseProvider,
CourseType,
Credit,
CreditTeacher,
CreditType,
ElementDetail,
Enrollment,
ExcludedCourse,
Organization,
ProgrammeModule,
ProgrammeModuleChild,
Semester,
SemesterEnrollment,
SISStudyRight,
SISStudyRightElement,
Student,
Studyplan,
StudyrightExtent,
Studyright,
StudyrightElement,
Teacher,
Transfer,
}
+53
View File
@@ -0,0 +1,53 @@
import { CreditTypeCode } from '../types/creditTypeCode'
import { Course } from './course'
import { CourseProvider } from './courseProvider'
import { CourseType } from './courseType'
import { Credit } from './credit'
import { CreditTeacher } from './creditTeacher'
import { CreditType } from './creditType'
import { ElementDetail } from './elementDetail'
import { Enrollment } from './enrollment'
import { Organization } from './organization'
import { ProgrammeModule } from './programmeModule'
import { ProgrammeModuleChild } from './programmeModuleChild'
import { Student } from './student'
import { Semester } from './semester'
import { SemesterEnrollment } from './semesterEnrollment'
import { SISStudyRight } from './SISStudyRight'
import { SISStudyRightElement } from './SISStudyRightElement'
import { Studyplan } from './studyplan'
import { Studyright } from './studyright'
import { StudyrightElement } from './studyrightElement'
import { StudyrightExtent } from './studyrightExtent'
import { Teacher } from './teacher'
import { Transfer } from './transfer'
Credit.passed = ({ credittypecode }) => [CreditTypeCode.PASSED, CreditTypeCode.APPROVED].includes(credittypecode)
Credit.failed = ({ credittypecode }) => credittypecode === CreditTypeCode.FAILED
Credit.improved = ({ credittypecode }) => credittypecode === CreditTypeCode.IMPROVED
Credit.notUnnecessary = ({ credits }) => credits > 0 && credits <= 12
export {
Course,
CourseProvider,
CourseType,
Credit,
CreditTeacher,
CreditType,
ElementDetail,
Enrollment,
Organization,
ProgrammeModule,
ProgrammeModuleChild,
Semester,
SemesterEnrollment,
SISStudyRight,
SISStudyRightElement,
Student,
Studyplan,
Studyright,
StudyrightExtent,
StudyrightElement,
Teacher,
Transfer,
}
@@ -0,0 +1,22 @@
import { AutoIncrement, Column, DataType, Model, PrimaryKey, Table } from 'sequelize-typescript'
@Table({
underscored: false,
modelName: 'custom_population_search',
tableName: 'custom_population_searches',
})
export class CustomPopulationSearch extends Model {
@PrimaryKey
@AutoIncrement
@Column(DataType.BIGINT)
id: bigint
@Column(DataType.BIGINT)
userId: bigint
@Column(DataType.STRING)
name: string
@Column(DataType.ARRAY(DataType.STRING))
students: string[]
}
@@ -0,0 +1,27 @@
import { Column, DataType, Model, PrimaryKey, Table } from 'sequelize-typescript'
@Table({
underscored: true,
modelName: 'excluded_course',
tableName: 'excluded_courses',
})
export class ExcludedCourse extends Model {
@PrimaryKey
@Column(DataType.INTEGER)
id: number
@Column(DataType.STRING)
programme_code: string
@Column(DataType.STRING)
course_code: string
@Column(DataType.STRING)
curriculum_version: string
@Column(DataType.DATE)
createdAt: Date
@Column(DataType.DATE)
updatedAt: Date
}
+19
View File
@@ -0,0 +1,19 @@
import { CustomPopulationSearch } from './customPopulationSearch'
import { ExcludedCourse } from './excludedCourse'
import { OpenUniPopulationSearch } from './openUniPopulationSearch'
import { ProgressCriteria } from './progressCriteria'
import { StudyGuidanceGroupTag } from './studyGuidanceGroupTag'
import { StudyProgrammePin } from './studyProgrammePin'
import { Tag } from './tag'
import { TagStudent } from './tagStudent'
export {
CustomPopulationSearch,
ExcludedCourse,
OpenUniPopulationSearch,
ProgressCriteria,
StudyGuidanceGroupTag,
StudyProgrammePin,
Tag,
TagStudent,
}
@@ -0,0 +1,23 @@
import { AutoIncrement, Column, DataType, Model, PrimaryKey, Table } from 'sequelize-typescript'
@Table({
underscored: true,
timestamps: true,
modelName: 'open_uni_population_search',
tableName: 'open_uni_population_searches',
})
export class OpenUniPopulationSearch extends Model {
@PrimaryKey
@AutoIncrement
@Column(DataType.BIGINT)
id: bigint
@Column(DataType.BIGINT)
userId: bigint
@Column(DataType.STRING)
name: string
@Column(DataType.ARRAY(DataType.STRING))
courseCodes: string[]
}
@@ -0,0 +1,52 @@
import { Column, DataType, Model, PrimaryKey, Table } from 'sequelize-typescript'
@Table({
underscored: true,
timestamps: true,
modelName: 'progress_criteria',
tableName: 'progress_criteria',
})
export class ProgressCriteria extends Model {
@PrimaryKey
@Column(DataType.STRING)
code: string
@Column(DataType.STRING)
curriculumVersion: string
@Column(DataType.ARRAY(DataType.STRING))
coursesYearOne: string[]
@Column(DataType.ARRAY(DataType.STRING))
coursesYearTwo: string[]
@Column(DataType.ARRAY(DataType.STRING))
coursesYearThree: string[]
@Column(DataType.ARRAY(DataType.STRING))
coursesYearFour: string[]
@Column(DataType.ARRAY(DataType.STRING))
coursesYearFive: string[]
@Column(DataType.ARRAY(DataType.STRING))
coursesYearSix: string[]
@Column(DataType.INTEGER)
creditsYearOne: number
@Column(DataType.INTEGER)
creditsYearTwo: number
@Column(DataType.INTEGER)
creditsYearThree: number
@Column(DataType.INTEGER)
creditsYearFour: number
@Column(DataType.INTEGER)
creditsYearFive: number
@Column(DataType.INTEGER)
creditsYearSix: number
}
@@ -0,0 +1,24 @@
import { AutoIncrement, Column, DataType, Model, PrimaryKey, Table, Unique } from 'sequelize-typescript'
@Table({
underscored: true,
timestamps: true,
modelName: 'study_guidance_group_tag',
tableName: 'study_guidance_group_tags',
})
export class StudyGuidanceGroupTag extends Model {
@PrimaryKey
@AutoIncrement
@Column(DataType.BIGINT)
id: bigint
@Unique
@Column(DataType.STRING)
studyGuidanceGroupId: string
@Column(DataType.STRING)
studyProgramme: string
@Column(DataType.STRING)
year: string
}
@@ -0,0 +1,16 @@
import { Column, DataType, Model, PrimaryKey, Table } from 'sequelize-typescript'
@Table({
underscored: true,
timestamps: false,
modelName: 'study_programme_pin',
tableName: 'study_programme_pins',
})
export class StudyProgrammePin extends Model {
@PrimaryKey
@Column(DataType.INTEGER)
userId: number
@Column(DataType.ARRAY(DataType.STRING))
studyProgrammes: string[]
}
+30
View File
@@ -0,0 +1,30 @@
import { AutoIncrement, BelongsTo, Column, DataType, ForeignKey, Model, PrimaryKey, Table } from 'sequelize-typescript'
import { TagStudent } from './tagStudent'
@Table({
modelName: 'tag',
tableName: 'tag',
})
export class Tag extends Model {
@PrimaryKey
@AutoIncrement
@ForeignKey(() => TagStudent)
@Column(DataType.BIGINT)
tag_id: bigint
@BelongsTo(() => TagStudent)
tagStudent: TagStudent
@Column(DataType.STRING)
tagname: string
@Column(DataType.STRING)
studytrack: string
@Column(DataType.STRING)
year: string
@Column(DataType.BIGINT)
personal_user_id: bigint
}
@@ -0,0 +1,24 @@
import { AutoIncrement, BelongsTo, Column, DataType, ForeignKey, Model, PrimaryKey, Table } from 'sequelize-typescript'
import { Student } from '../student'
import { Tag } from './tag'
@Table({
modelName: 'tag_student',
tableName: 'tag_student',
})
export class TagStudent extends Model {
@PrimaryKey
@AutoIncrement
@ForeignKey(() => Tag)
@Column(DataType.BIGINT)
tag_id: bigint
@PrimaryKey
@ForeignKey(() => Student)
@Column(DataType.STRING)
studentnumber: string
@BelongsTo(() => Tag)
tag: Tag
}
-215
View File
@@ -1,215 +0,0 @@
const { ARRAY, BIGINT, DATE, INTEGER, STRING } = require('sequelize')
const { sequelizeKone } = require('../database/connection')
const ExcludedCourse = sequelizeKone.define(
'excluded_courses',
{
id: {
primaryKey: true,
type: INTEGER,
autoIncrement: true,
},
programme_code: {
type: STRING,
},
curriculum_version: {
type: STRING,
},
course_code: {
type: STRING,
},
created_at: {
type: DATE,
},
updated_at: {
type: DATE,
},
},
{
underscored: true,
}
)
const TagStudent = sequelizeKone.define(
'tag_student',
{
studentnumber: {
primaryKey: true,
type: STRING,
},
tag_id: {
primaryKey: true,
type: BIGINT,
},
},
{
tableName: 'tag_student',
}
)
const Tag = sequelizeKone.define(
'tag',
{
tagname: {
type: STRING,
},
tag_id: {
primaryKey: true,
type: BIGINT,
autoIncrement: true,
},
studytrack: {
type: STRING,
},
year: {
type: STRING,
},
personal_user_id: {
type: BIGINT,
},
},
{
tableName: 'tag',
}
)
const CustomPopulationSearch = sequelizeKone.define('custom_population_searches', {
id: {
primaryKey: true,
type: BIGINT,
autoIncrement: true,
},
userId: {
type: BIGINT,
},
name: {
type: STRING,
},
students: {
type: ARRAY(STRING),
},
})
// also used in completed courses search feature
const OpenUniPopulationSearch = sequelizeKone.define(
'open_uni_population_searches',
{
id: {
primaryKey: true,
type: BIGINT,
autoIncrement: true,
},
userId: {
type: BIGINT,
},
name: {
type: STRING,
allowNull: false,
},
courseCodes: {
type: ARRAY(STRING),
},
},
{ underscored: true, timestamps: true }
)
const ProgressCriteria = sequelizeKone.define(
'progress_criteria',
{
code: {
primaryKey: true,
type: STRING,
},
curriculumVersion: {
type: STRING,
},
coursesYearOne: {
type: ARRAY(STRING),
},
coursesYearTwo: {
type: ARRAY(STRING),
},
coursesYearThree: {
type: ARRAY(STRING),
},
coursesYearFour: {
type: ARRAY(STRING),
},
coursesYearFive: {
type: ARRAY(STRING),
},
coursesYearSix: {
type: ARRAY(STRING),
},
creditsYearOne: {
type: INTEGER,
},
creditsYearTwo: {
type: INTEGER,
},
creditsYearThree: {
type: INTEGER,
},
creditsYearFour: {
type: INTEGER,
},
creditsYearFive: {
type: INTEGER,
},
creditsYearSix: {
type: INTEGER,
},
},
{ underscored: true, timestamps: true }
)
const StudyGuidanceGroupTag = sequelizeKone.define(
'study_guidance_group_tags',
{
id: {
primaryKey: true,
type: BIGINT,
autoIncrement: true,
},
studyGuidanceGroupId: {
type: STRING,
unique: true,
},
studyProgramme: {
type: STRING,
},
year: {
type: STRING,
},
},
{ underscored: true, timestamps: true }
)
const StudyProgrammePin = sequelizeKone.define(
'study_programme_pins',
{
userId: {
primaryKey: true,
type: INTEGER,
},
studyProgrammes: {
type: ARRAY(STRING),
},
},
{ underscored: true, timestamps: false }
)
TagStudent.belongsTo(Tag, { foreignKey: 'tag_id', sourceKey: 'tag_id' })
Tag.hasMany(TagStudent, { foreignKey: 'tag_id', sourceKey: 'tag_id' })
module.exports = {
CustomPopulationSearch,
ExcludedCourse,
OpenUniPopulationSearch,
ProgressCriteria,
StudyGuidanceGroupTag,
StudyProgrammePin,
Tag,
TagStudent,
}
@@ -1,42 +0,0 @@
const { ARRAY, BIGINT, DATE, STRING } = require('sequelize')
const { sequelizeUser } = require('../database/connection')
const User = sequelizeUser.define(
'users',
{
id: {
primaryKey: true,
type: BIGINT,
autoIncrement: true,
},
fullName: STRING,
username: {
type: STRING,
unique: true,
},
email: STRING,
language: STRING,
sisuPersonId: STRING,
lastLogin: DATE,
roles: {
type: ARRAY(STRING),
allowNull: false,
defaultValue: [],
},
programmeRights: {
type: ARRAY(STRING),
allowNull: false,
defaultValue: [],
},
},
{
tableName: 'users',
timestamps: false,
underscored: true,
}
)
module.exports = {
User,
}
@@ -1,41 +0,0 @@
const { Model, DATE, JSONB, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Organization extends Model {}
Organization.init(
{
id: {
type: STRING,
primaryKey: true,
},
code: {
type: STRING,
references: {
table: 'organization',
field: 'code',
},
},
name: {
type: JSONB,
},
parent_id: {
type: STRING,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'organization',
tableName: 'organization',
}
)
module.exports = Organization
@@ -0,0 +1,61 @@
import {
BelongsToMany,
Column,
CreatedAt,
DataType,
HasMany,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { Name } from '../types/name'
import { Course } from './course'
import { CourseProvider } from './courseProvider'
import { ProgrammeModule } from './programmeModule'
import { SISStudyRight } from './SISStudyRight'
import { Studyright } from './studyright'
@Table({
underscored: true,
modelName: 'organization',
tableName: 'organization',
})
export class Organization extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@Column(DataType.STRING)
code: string
@Column(DataType.JSONB)
name: Name
@Column(DataType.STRING)
parent_id: string
@HasMany(() => Organization, { foreignKey: 'parent_id', as: 'children' })
children: Organization[]
@HasMany(() => ProgrammeModule, { foreignKey: 'organization_id' })
programmeModules: ProgrammeModule[]
@HasMany(() => SISStudyRight, { foreignKey: 'facultyCode', sourceKey: 'code' })
SISStudyRights: SISStudyRight[]
@HasMany(() => Studyright, { foreignKey: 'facultyCode', sourceKey: 'code' })
studyrights: Studyright[]
@BelongsToMany(() => Course, () => CourseProvider, 'organizationcode')
courses: Course[]
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
@@ -1,62 +0,0 @@
const { Model, ARRAY, DATE, INTEGER, JSONB, STRING, TEXT } = require('sequelize')
const { dbConnections } = require('../database/connection')
class ProgrammeModule extends Model {}
ProgrammeModule.init(
{
id: {
primaryKey: true,
type: STRING,
},
group_id: {
type: STRING,
},
code: {
type: STRING,
},
name: {
type: JSONB,
},
type: {
type: STRING,
},
order: {
type: INTEGER,
},
studyLevel: {
type: STRING,
},
organization_id: {
type: STRING,
references: {
model: 'organization',
key: 'id',
},
},
valid_from: {
type: DATE,
},
valid_to: {
type: DATE,
},
curriculum_period_ids: {
type: ARRAY(TEXT),
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'programme_module',
tableName: 'programme_modules',
}
)
module.exports = ProgrammeModule
@@ -0,0 +1,77 @@
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
ForeignKey,
Model,
DataType,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { Organization } from './organization'
import { ProgrammeModuleChild } from './programmeModuleChild'
@Table({
underscored: true,
modelName: 'programme_module',
tableName: 'programme_modules',
})
export class ProgrammeModule extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@BelongsToMany(() => ProgrammeModule, () => ProgrammeModuleChild, 'child_id')
parents: ProgrammeModule[]
@BelongsToMany(() => ProgrammeModule, () => ProgrammeModuleChild, 'parent_id')
children: ProgrammeModule[]
@Column(DataType.STRING)
group_id: string
@Column(DataType.STRING)
code: string
@Column(DataType.JSONB)
name: object
@Column(DataType.STRING)
type: string
@Column(DataType.INTEGER)
order: number
@Column(DataType.STRING)
studyLevel: string
@ForeignKey(() => Organization)
@Column(DataType.STRING)
organization_id: string
@BelongsTo(() => Organization, { foreignKey: 'organization_id' })
organization: Organization
@Column(DataType.DATE)
valid_from: Date
@Column(DataType.DATE)
valid_to: Date
@Column(DataType.ARRAY(DataType.STRING))
curriculum_period_ids: string[]
@Column(DataType.STRING)
degreeProgrammeType: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
@@ -1,38 +0,0 @@
const { Model, DATE, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class ProgrammeModuleChild extends Model {}
ProgrammeModuleChild.init(
{
composite: {
type: STRING,
primaryKey: true,
},
parentId: {
type: STRING,
references: {
model: 'programme_modules',
key: 'id',
},
},
childId: {
type: STRING,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'programme_module_child',
tableName: 'programme_module_children',
}
)
module.exports = ProgrammeModuleChild
@@ -0,0 +1,29 @@
import { Column, CreatedAt, DataType, ForeignKey, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { ProgrammeModule } from './programmeModule'
@Table({
underscored: true,
modelName: 'programme_module_child',
tableName: 'programme_module_children',
})
export class ProgrammeModuleChild extends Model {
@PrimaryKey
@Column(DataType.STRING)
composite: string
@ForeignKey(() => ProgrammeModule)
@Column(DataType.STRING)
parentId: string
@Column(DataType.STRING)
childId: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-55
View File
@@ -1,55 +0,0 @@
const { Model, DATE, INTEGER, JSONB, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Semester extends Model {}
Semester.init(
{
composite: {
type: STRING,
primaryKey: true,
},
semestercode: {
type: INTEGER,
},
name: {
type: JSONB,
},
startdate: {
type: DATE,
},
enddate: {
type: DATE,
},
yearcode: {
type: INTEGER,
},
org: {
type: STRING,
},
yearname: {
type: STRING,
},
termIndex: {
type: INTEGER,
},
startYear: {
type: INTEGER,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'semester',
tableName: 'semesters',
}
)
module.exports = Semester
+53
View File
@@ -0,0 +1,53 @@
import { Column, CreatedAt, DataType, HasMany, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { SemesterEnrollment } from './semesterEnrollment'
import { Name } from '../types/name'
@Table({
underscored: true,
modelName: 'semester',
tableName: 'semesters',
})
export class Semester extends Model {
@PrimaryKey
@Column(DataType.STRING)
composite: string
@HasMany(() => SemesterEnrollment)
semesterEnrollments: SemesterEnrollment[]
@Column(DataType.INTEGER)
semestercode: number
@Column(DataType.JSONB)
name: Name
@Column(DataType.INTEGER)
startYear: number
@Column(DataType.DATE)
startdate: Date
@Column(DataType.DATE)
enddate: Date
@Column(DataType.INTEGER)
yearcode: number
@Column(DataType.STRING)
yearname: string
@Column(DataType.STRING)
org: string
@Column(DataType.INTEGER)
termIndex: number
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
@@ -1,59 +0,0 @@
const { Model, BOOLEAN, DATE, INTEGER, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class SemesterEnrollment extends Model {}
SemesterEnrollment.init(
{
enrollmenttype: {
type: INTEGER,
},
org: {
type: STRING,
},
studentnumber: {
type: STRING,
references: {
model: 'student',
key: 'studentnumber',
},
onUpdate: 'cascade',
onDelete: 'cascade',
primaryKey: true,
},
semestercode: {
type: INTEGER,
},
semestercomposite: {
type: STRING,
references: {
model: 'semesters',
key: 'composite',
},
onUpdate: 'cascade',
onDelete: 'cascade',
primaryKey: true,
},
enrollment_date: {
type: DATE,
},
statutory_absence: {
type: BOOLEAN,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: false,
sequelize: dbConnections.sequelize,
modelName: 'semester_enrollment',
tableName: 'semester_enrollments',
}
)
module.exports = SemesterEnrollment
@@ -0,0 +1,60 @@
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { EnrollmentType } from '../types/enrollmentType'
import { Semester } from './semester'
import { Student } from './student'
@Table({
underscored: false,
modelName: 'semester_enrollment',
tableName: 'semester_enrollments',
})
export class SemesterEnrollment extends Model {
@PrimaryKey
@Column(DataType.INTEGER)
enrollmenttype: EnrollmentType
@Column(DataType.STRING)
org: string
@ForeignKey(() => Student)
@Column(DataType.STRING)
studentnumber: string
@BelongsTo(() => Student)
student: Student
@Column(DataType.INTEGER)
semestercode: number
@ForeignKey(() => Semester)
@Column(DataType.STRING)
semestercomposite: string
@BelongsTo(() => Semester)
semester: Semester
@Column(DataType.DATE)
enrollment_date: Date
@Column(DataType.BOOLEAN)
statutory_absence: boolean
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-46
View File
@@ -1,46 +0,0 @@
const { Model, DATE, INTEGER, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Student extends Model {}
Student.init(
{
studentnumber: {
primaryKey: true,
type: STRING,
},
lastname: { type: STRING },
firstnames: { type: STRING },
abbreviatedname: { type: STRING },
birthdate: { type: DATE },
creditcount: { type: INTEGER },
dateofuniversityenrollment: { type: DATE },
email: { type: STRING },
secondary_email: { type: STRING },
national_student_number: { type: STRING },
phone_number: { type: STRING },
country_fi: { type: STRING },
country_sv: { type: STRING },
country_en: { type: STRING },
home_country_fi: { type: STRING },
home_country_sv: { type: STRING },
home_country_en: { type: STRING },
gender_code: { type: STRING },
sis_person_id: { type: STRING },
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'student',
tableName: 'student',
}
)
module.exports = Student
+108
View File
@@ -0,0 +1,108 @@
import { Column, CreatedAt, DataType, HasMany, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { GenderCode } from '../types/genderCode'
import { Credit } from './credit'
import { Enrollment } from './enrollment'
import { SemesterEnrollment } from './semesterEnrollment'
import { SISStudyRight } from './SISStudyRight'
import { Studyplan } from './studyplan'
import { Studyright } from './studyright'
import { StudyrightElement } from './studyrightElement'
import { Transfer } from './transfer'
@Table({
underscored: true,
modelName: 'student',
tableName: 'student',
})
export class Student extends Model {
@PrimaryKey
@Column(DataType.STRING)
studentnumber: string
@Column(DataType.STRING)
lastname: string
@Column(DataType.STRING)
firstnames: string
@Column(DataType.STRING)
abbreviatedname: string
@HasMany(() => Enrollment, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
enrollments: Enrollment[]
@HasMany(() => SemesterEnrollment)
semester_enrollments: SemesterEnrollment[]
@HasMany(() => SISStudyRight)
studyRights: SISStudyRight[]
@HasMany(() => Studyright)
studyrights: Studyright[]
@HasMany(() => StudyrightElement, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
studyrightElements: StudyrightElement[]
@HasMany(() => Studyplan, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
studyplans: Studyplan[]
@HasMany(() => Transfer, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
transfers: Transfer[]
@HasMany(() => Credit, { foreignKey: 'student_studentnumber', sourceKey: 'studentnumber' })
credits: Credit
@Column(DataType.DATE)
birthdate: Date
@Column(DataType.INTEGER)
creditcount: number
@Column(DataType.DATE)
dateofuniversityenrollment: Date
@Column(DataType.STRING)
email: string
@Column(DataType.STRING)
secondary_email: string
@Column(DataType.STRING)
national_student_number: string
@Column(DataType.STRING)
phone_number: string
@Column(DataType.STRING)
country_fi: string
@Column(DataType.STRING)
country_sv: string
@Column(DataType.STRING)
country_en: string
@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
@Column(DataType.STRING)
sis_person_id: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-68
View File
@@ -1,68 +0,0 @@
const { Model, ARRAY, DATE, INTEGER, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Studyplan extends Model {}
Studyplan.init(
{
id: {
primaryKey: true,
type: STRING,
allowNull: false,
},
studentnumber: {
type: STRING,
references: {
model: 'student',
key: 'studentnumber',
},
onUpdate: 'cascade',
onDelete: 'cascade',
allowNull: false,
},
studyrightid: {
type: STRING,
references: {
model: 'studyright',
key: 'studyrightid',
},
onUpdate: 'cascade',
onDelete: 'cascade',
allowNull: false,
},
programme_code: {
type: STRING,
allowNull: false,
},
included_courses: {
type: ARRAY(STRING),
},
sisu_id: {
type: STRING,
},
completed_credits: {
type: INTEGER,
},
curriculum_period_id: {
type: STRING,
},
sis_study_right_id: {
type: STRING,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: false,
sequelize: dbConnections.sequelize,
modelName: 'studyplan',
tableName: 'studyplan',
}
)
module.exports = Studyplan
+70
View File
@@ -0,0 +1,70 @@
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { SISStudyRight } from './SISStudyRight'
import { Student } from './student'
import { Studyright } from './studyright'
@Table({
underscored: false,
modelName: 'studyplan',
tableName: 'studyplan',
})
export class Studyplan extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@ForeignKey(() => Student)
@Column(DataType.STRING)
studentnumber: string
@BelongsTo(() => Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
student: Student
@ForeignKey(() => Studyright)
@Column(DataType.STRING)
studyrightid: string
@BelongsTo(() => Studyright, { foreignKey: 'studyrightid', targetKey: 'studyrightid' })
studyright: Studyright
@ForeignKey(() => SISStudyRight)
@Column(DataType.STRING)
sis_study_right_id: string
@BelongsTo(() => SISStudyRight, { foreignKey: 'sis_study_right_id', targetKey: 'id', as: 'studyRight' })
studyRight: SISStudyRight
@Column(DataType.STRING)
programme_code: string
@Column(DataType.ARRAY(DataType.STRING))
included_courses: string[]
@Column(DataType.STRING)
sisu_id: string
@Column(DataType.DOUBLE)
completed_credits: number
@Column(DataType.STRING)
curriculum_period_id: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-85
View File
@@ -1,85 +0,0 @@
const { Model, BOOLEAN, DATE, INTEGER, JSONB, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Studyright extends Model {}
Studyright.init(
{
studyrightid: {
primaryKey: true,
type: STRING,
},
actual_studyrightid: {
type: STRING,
},
startdate: {
type: DATE,
},
enddate: {
type: DATE,
},
givendate: {
type: DATE,
},
studystartdate: {
type: DATE,
},
graduated: {
type: INTEGER,
},
active: {
type: INTEGER,
},
cancelled: {
type: BOOLEAN,
},
studentStudentnumber: {
type: STRING,
references: {
model: 'student',
key: 'studentnumber',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
facultyCode: {
type: STRING,
},
prioritycode: {
type: INTEGER,
},
extentcode: {
type: INTEGER,
references: {
model: 'studyright_extents',
key: 'extentcode',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
admissionType: {
type: STRING,
},
isBaMa: {
type: BOOLEAN,
},
semesterEnrollments: {
type: JSONB,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'studyright',
tableName: 'studyright',
}
)
module.exports = Studyright
+110
View File
@@ -0,0 +1,110 @@
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
HasMany,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { ExtentCode } from '../types/extentCode'
import { PriorityCode } from '../types/priorityCode'
import { Enrollment } from './enrollment'
import { Organization } from './organization'
import { Student } from './student'
import { Studyplan } from './studyplan'
import { StudyrightElement } from './studyrightElement'
import { StudyrightExtent } from './studyrightExtent'
import { Transfer } from './transfer'
@Table({
modelName: 'studyright',
tableName: 'studyright',
timestamps: false,
})
export class Studyright extends Model {
@PrimaryKey
@Column(DataType.STRING)
studyrightid: string
@HasMany(() => StudyrightElement, { foreignKey: 'studyrightid', sourceKey: 'studyrightid' })
studyright_elements: StudyrightElement[]
@ForeignKey(() => StudyrightElement)
@Column(DataType.STRING)
actual_studyrightid: string
@HasMany(() => Enrollment, { foreignKey: 'studyright_id', constraints: false })
enrollments: Enrollment[]
@BelongsTo(() => Student)
student: Student
@BelongsTo(() => Organization, { targetKey: 'code' })
organization: Organization
@HasMany(() => Transfer, { foreignKey: 'studyrightid', sourceKey: 'studyrightid' })
transfers: Transfer[]
@HasMany(() => Studyplan, { foreignKey: 'studyrightid', sourceKey: 'studyrightid' })
studyplans: Studyplan[]
@Column(DataType.DATE)
startdate: Date
@Column(DataType.DATE)
enddate: Date
@Column(DataType.DATE)
givendate: Date
@Column(DataType.DATE)
studystartdate: Date
@Column(DataType.STRING)
graduated: string
@Column(DataType.STRING)
active: string
@Column(DataType.BOOLEAN)
cancelled: boolean
@ForeignKey(() => Student)
@Column(DataType.STRING)
student_studentnumber: string
@ForeignKey(() => Organization)
@Column({ field: 'faculty_code', type: DataType.STRING })
facultyCode: string
@Column(DataType.INTEGER)
prioritycode: PriorityCode
@Column(DataType.INTEGER)
extentcode: ExtentCode
@BelongsTo(() => StudyrightExtent, { foreignKey: 'extentcode', targetKey: 'extentcode' })
studyrightExtent: StudyrightExtent
@Column(DataType.STRING)
admission_type: string
@Column(DataType.BOOLEAN)
is_ba_ma: boolean
@Column({ field: 'semester_enrollments', type: DataType.JSONB })
semesterEnrollments: object
@CreatedAt
@Column({ field: 'created_at', type: DataType.DATE })
created_at: Date
@UpdatedAt
@Column({ field: 'updated_at', type: DataType.DATE })
updated_at: Date
}
@@ -1,61 +0,0 @@
const { Model, DATE, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class StudyrightElement extends Model {}
StudyrightElement.init(
{
id: {
type: STRING,
primaryKey: true,
},
startdate: {
type: DATE,
},
enddate: {
type: DATE,
},
studyrightid: {
type: STRING,
references: {
model: 'studyright',
key: 'studyrightid',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
code: {
type: STRING,
references: {
model: 'element_details',
key: 'code',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
studentnumber: {
type: STRING,
references: {
model: 'student',
key: 'studentnumber',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: false,
sequelize: dbConnections.sequelize,
modelName: 'studyright_element',
tableName: 'studyright_elements',
}
)
module.exports = StudyrightElement
@@ -0,0 +1,61 @@
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { ElementDetail } from './elementDetail'
import { Student } from './student'
import { Studyright } from './studyright'
@Table({
underscored: false,
modelName: 'studyright_element',
tableName: 'studyright_elements',
})
export class StudyrightElement extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@BelongsTo(() => ElementDetail, { foreignKey: 'code', targetKey: 'code' })
element_detail: ElementDetail
@Column(DataType.STRING)
startdate: string
@Column(DataType.DATE)
enddate: Date
@ForeignKey(() => Studyright)
@Column(DataType.STRING)
studyrightid: string
@BelongsTo(() => Studyright, { foreignKey: 'studyrightid', targetKey: 'studyrightid' })
studyright: Studyright
@BelongsTo(() => Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
student: Student
@ForeignKey(() => ElementDetail)
@Column(DataType.STRING)
code: string
@ForeignKey(() => Student)
@Column(DataType.STRING)
studentnumber: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
@@ -1,30 +0,0 @@
const { Model, DATE, INTEGER, JSONB } = require('sequelize')
const { dbConnections } = require('../database/connection')
class StudyrightExtent extends Model {}
StudyrightExtent.init(
{
extentcode: {
type: INTEGER,
primaryKey: true,
},
name: {
type: JSONB,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
sequelize: dbConnections.sequelize,
modelName: 'studyright_extent',
tableName: 'studyright_extents',
}
)
module.exports = StudyrightExtent
@@ -0,0 +1,29 @@
import { Column, CreatedAt, DataType, HasMany, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { ExtentCode } from '../types/extentCode'
import { Studyright } from './studyright'
@Table({
underscored: true,
modelName: 'studyright_extent',
tableName: 'studyright_extents',
})
export class StudyrightExtent extends Model {
@PrimaryKey
@Column(DataType.INTEGER)
extentcode: ExtentCode
@Column(DataType.STRING)
name: string
@HasMany(() => Studyright, { foreignKey: 'extentcode', sourceKey: 'extentcode' })
studyrights: Studyright[]
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-31
View File
@@ -1,31 +0,0 @@
const { Model, DATE, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Teacher extends Model {}
Teacher.init(
{
id: {
primaryKey: true,
type: STRING,
},
name: {
type: STRING,
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'teacher',
tableName: 'teacher',
}
)
module.exports = Teacher
+29
View File
@@ -0,0 +1,29 @@
import { BelongsToMany, Column, CreatedAt, DataType, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { Credit } from './credit'
import { CreditTeacher } from './creditTeacher'
@Table({
underscored: true,
modelName: 'teacher',
tableName: 'teacher',
})
export class Teacher extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@Column(DataType.STRING)
name: string
@BelongsToMany(() => Credit, () => CreditTeacher)
credits: Credit[]
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
-67
View File
@@ -1,67 +0,0 @@
const { Model, DATE, STRING } = require('sequelize')
const { dbConnections } = require('../database/connection')
class Transfer extends Model {}
Transfer.init(
{
id: {
type: STRING,
primaryKey: true,
},
sourcecode: {
type: STRING,
references: {
model: 'element_details',
key: 'code',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
targetcode: {
type: STRING,
references: {
model: 'element_details',
key: 'code',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
transferdate: {
type: DATE,
},
studentnumber: {
type: STRING,
references: {
model: 'student',
key: 'studentnumber',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
studyrightid: {
type: STRING,
references: {
model: 'studyright',
key: 'studyrightid',
},
onUpdate: 'cascade',
onDelete: 'cascade',
},
createdAt: {
type: DATE,
},
updatedAt: {
type: DATE,
},
},
{
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'transfer',
tableName: 'transfers',
}
)
module.exports = Transfer
+65
View File
@@ -0,0 +1,65 @@
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { ElementDetail } from './elementDetail'
import { Student } from './student'
import { Studyright } from './studyright'
@Table({
underscored: true,
modelName: 'transfer',
tableName: 'transfers',
})
export class Transfer extends Model {
@PrimaryKey
@Column(DataType.STRING)
id: string
@BelongsTo(() => Student, { foreignKey: 'studentnumber', targetKey: 'studentnumber' })
student: Student
@BelongsTo(() => Studyright, { foreignKey: 'studyrightid', targetKey: 'studyrightid' })
studyright: Studyright
@BelongsTo(() => ElementDetail, { foreignKey: 'sourcecode' })
source: ElementDetail
@BelongsTo(() => ElementDetail, { foreignKey: 'targetcode' })
target: ElementDetail
@ForeignKey(() => ElementDetail)
@Column(DataType.STRING)
sourcecode: string
@ForeignKey(() => ElementDetail)
@Column(DataType.STRING)
targetcode: string
@Column(DataType.DATE)
transferdate: Date
@ForeignKey(() => Student)
@Column(DataType.STRING)
studentnumber: string
@ForeignKey(() => Studyright)
@Column(DataType.STRING)
studyrightid: string
@CreatedAt
@Column(DataType.DATE)
createdAt: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt: Date
}
+41
View File
@@ -0,0 +1,41 @@
import { AutoIncrement, Column, DataType, Default, Model, PrimaryKey, Table, Unique } from 'sequelize-typescript'
@Table({
underscored: true,
timestamps: false,
modelName: 'users',
tableName: 'users',
})
export class User extends Model {
@PrimaryKey
@AutoIncrement
@Column(DataType.BIGINT)
id: bigint
@Column(DataType.STRING)
fullName: string
@Unique
@Column(DataType.STRING)
username: string
@Column(DataType.STRING)
email: string
@Column(DataType.STRING)
language: string
@Column(DataType.STRING)
sisuPersonId: string
@Column(DataType.DATE)
lastLogin: Date
@Default([])
@Column(DataType.ARRAY(DataType.STRING))
roles: string[]
@Default([])
@Column(DataType.ARRAY(DataType.STRING))
programmeRights: string[]
}
+2
View File
@@ -31,6 +31,7 @@ const studyProgrammeCriteria = require('./routes/studyProgrammeCriteria')
const studyProgrammePins = require('./routes/studyProgrammePins')
const tags = require('./routes/tags')
const teachers = require('./routes/teachers')
const testRoute = require('./routes/testRoute')
const university = require('./routes/university')
const updater = require('./routes/updater')
const usersToska = require('./routes/users')
@@ -38,6 +39,7 @@ const usersFd = require('./routes/usersFd')
const initializeSentry = require('./util/sentry')
module.exports = (app, url) => {
app.use(`${url}/testroute`, testRoute)
initializeSentry(app)
const users = serviceProvider === 'Toska' ? usersToska : usersFd
+20 -13
View File
@@ -4,7 +4,7 @@ const auth = require('../middleware/auth')
const { combineFacultyBasics } = require('../services/faculty/facultyBasics')
const { getFacultyCredits } = require('../services/faculty/facultyCredits')
const { countGraduationTimes } = require('../services/faculty/facultyGraduationTimes')
const { getFacultyList } = require('../services/faculty/facultyHelpers')
const { getSortedFaculties } = require('../services/faculty/facultyHelpers')
const {
getProgrammes,
getBasicStats,
@@ -28,8 +28,8 @@ const logger = require('../util/logger')
// Some of them have been copied here and slightly edited for faculty purpose.
router.get('/', async (_req, res) => {
const facultyList = await getFacultyList()
res.json(facultyList)
const faculties = await getSortedFaculties()
res.json(faculties)
})
router.get('/:id/basicstats', auth.roles(['facultyStatistics', 'katselmusViewer']), async (req, res) => {
@@ -147,25 +147,32 @@ router.get('/:id/studentstats', auth.roles(['facultyStatistics', 'katselmusViewe
router.get('/:id/update_basicview', auth.roles(['facultyStatistics', 'katselmusViewer']), async (req, res) => {
const code = req.params.id
const statsType = req.query?.stats_type
if (code) {
if (!code) {
return res.status(400).json({ error: 'Missing code' })
}
try {
const result = await updateFacultyOverview(code, statsType)
return res.json(result)
} catch (error) {
const message = `Failed to update basic stats for faculty ${code}`
logger.error(message, { error })
return res.status(500).json({ error: message })
}
return res.status(422).end()
})
router.get('/:id/update_progressview', auth.roles(['facultyStatistics', 'katselmusViewer']), async (req, res) => {
const code = req.params.id
if (code) {
let result = null
try {
result = await updateFacultyProgressOverview(code)
} catch (error) {
logger.error(`Failed to update faculty ${code} progress tab stats: ${error}`)
}
if (!code) {
return res.status(400).json({ error: 'Missing code' })
}
try {
const result = await updateFacultyProgressOverview(code)
return res.json(result)
} catch (error) {
const message = `Failed to update progress tab stats for faculty ${code}`
logger.error(message, { error })
return res.status(500).json({ error: message })
}
return res.status(422).end()
})
module.exports = router
+1 -1
View File
@@ -2,7 +2,7 @@ const router = require('express').Router()
const { getSemestersAndYears } = require('../services/semesters')
router.get('/', async (req, res) => {
router.get('/', async (_req, res) => {
const providers = await getSemestersAndYears()
res.json(providers)
})
+29 -19
View File
@@ -25,6 +25,10 @@ const logger = require('../util/logger')
// For grafana statistics (idea stolen from Norppa)
const logInfoForGrafana = async (code, combinedProgramme) => {
const programme = await SISStudyRightElement.findOne({ where: { code } })
if (!programme) {
logger.error('Study Programme not found', { studyprogrammeCode: code })
return
}
const programmeCode = combinedProgramme ? `${programme.code}-${combinedProgramme}` : programme.code
logger.info('Study Programme', {
studyprogrammeName: combinedProgramme ? `${programme.name.fi} + maisteri` : programme.name.fi,
@@ -52,11 +56,15 @@ router.get('/:id/basicstats', async (req, res) => {
const yearType = req.query?.year_type
const specialGroups = req.query?.special_groups
const combinedProgramme = req.query?.combined_programme
if (!code) return res.status(422).end()
if (!code) {
return res.status(422).end()
}
logInfoForGrafana(code, combinedProgramme)
const data = await getBasicStats(code, combinedProgramme, yearType, specialGroups)
if (data) return res.json(data)
if (data) {
return res.json(data)
}
const updated = await getBasicStatsForStudytrack({
studyprogramme: req.params.id,
@@ -148,32 +156,34 @@ router.get('/:id/colorizedtablecoursestats', async (req, res) => {
router.get('/:id/update_basicview', async (req, res) => {
const code = req.params.id
const combinedProgramme = req.query?.combined_programme
if (code) {
let result = null
try {
result = await updateBasicView(code, combinedProgramme)
} catch (error) {
logger.error({ message: `Failed to update code ${code} ${combinedProgramme} basic stats`, meta: `${error}` })
}
if (!code) {
return res.status(400).json({ error: 'Missing code' })
}
try {
const result = await updateBasicView(code, combinedProgramme)
return res.json(result)
} catch (error) {
const message = `Failed to update basic stats for programme ${code}${combinedProgramme ? `+${combinedProgramme}` : ''}`
logger.error(message, { error })
return res.status(500).json({ error: message })
}
res.status(422).end()
})
router.get('/:id/update_studytrackview', async (req, res) => {
const code = req.params.id
const combinedProgramme = req.query?.combined_programme
if (code) {
let result = null
try {
const associations = await getAssociations()
result = await updateStudytrackView(code, combinedProgramme, associations)
} catch (error) {
logger.error({ message: `Failed to update code ${code} ${combinedProgramme} studytrack stats`, meta: `${error}` })
}
if (!code) {
return res.status(400).json({ error: 'Missing code' })
}
try {
const associations = await getAssociations()
const result = await updateStudytrackView(code, combinedProgramme, associations)
return res.json(result)
} catch (error) {
const message = `Failed to update study track stats for programme ${code}${combinedProgramme ? `+${combinedProgramme}` : ''}`
logger.error(message, { error })
return res.status(500).json({ error: message })
}
res.status(422).end()
})
router.get('/:id/evaluationstats', async (req, res) => {
+1 -1
View File
@@ -1,6 +1,6 @@
const router = require('express').Router()
const { isFaculty, providersOfFaculty } = require('../services/organisations')
const { isFaculty, providersOfFaculty } = require('../services/organizations')
const { bySearchTerm, yearlyStatistics, teacherStats } = require('../services/teachers')
const { ID, getTeacherStats, findAndSaveTeachers, getCategoriesAndYears } = require('../services/topteachers')
const { getFullStudyProgrammeRights, splitByEmptySpace } = require('../util')
+10
View File
@@ -0,0 +1,10 @@
const router = require('express').Router()
const { Credit, CreditType, Studyright } = require('../models/index')
router.get('/', async (req, res) => {
const credit = await Credit.findOne({ include: [CreditType, Studyright] })
return res.send(credit)
})
module.exports = router
+4 -4
View File
@@ -1,8 +1,8 @@
const router = require('express').Router()
const { magicFacultyCode } = require('../../config/facultyCodes')
const { serviceProvider } = require('../conf-backend')
const { getFacultyList } = require('../services/faculty/facultyHelpers')
const { magicFacultyCode } = require('../config/organizationConstants')
const { getSortedFaculties } = require('../services/faculty/facultyHelpers')
const { getFacultyProgressStats, getGraduationStats } = require('../services/faculty/facultyService')
const { getMedian } = require('../services/studyProgramme/studyProgrammeHelpers')
@@ -11,7 +11,7 @@ const degreeNames = ['bachelor', 'bachelorMaster', 'master', 'licentiate', 'doct
router.get('/allprogressstats', async (req, res) => {
const specialGroups = req.query?.specialsIncluded === 'true' ? 'SPECIAL_INCLUDED' : 'SPECIAL_EXCLUDED'
const graduated = req.query?.graduated
const allFaculties = await getFacultyList()
const allFaculties = await getSortedFaculties()
const facultyCodes = allFaculties.map(faculty => faculty.code)
const codeToData = {}
@@ -88,7 +88,7 @@ router.get('/allprogressstats', async (req, res) => {
router.get('/allgraduationstats', async (_req, res) => {
const degreeNames = ['bachelor', 'bcMsCombo', 'master', 'licentiate', 'doctor']
const allFaculties = await getFacultyList()
const allFaculties = await getSortedFaculties()
const facultyCodes = allFaculties.map(faculty => faculty.code)
const facultyData = {}
const timesArrays = [] // keep book of these to null them in the end, large lists not used in frontend
+6 -1
View File
@@ -11,7 +11,7 @@ const {
updateStudentsByStudentNumber,
} = require('../services/sisUpdaterService')
const logger = require('../util/logger')
const { jobMaker, getJobs } = require('../worker/queue')
const { jobMaker, getJobs, removeWaitingJobs } = require('../worker/queue')
const refreshFacultiesByList = list => {
list.forEach(code => jobMaker.faculty(code))
@@ -117,4 +117,9 @@ router.get('/jobs', async (_req, res) => {
res.status(200).json({ waiting, active })
})
router.delete('/jobs', async (_req, res) => {
await removeWaitingJobs()
res.status(200).end()
})
module.exports = router
@@ -1,11 +1,11 @@
const moment = require('moment')
const { facultyCodes, ignoredFacultyCodes } = require('../config/organizationConstants')
const { redisClient } = require('./redis')
// Only new bachelor, masters and doctoral programmes get their data updated in redis every night, use redis for them
const isUpdatedNewProgramme = code => code.includes('KH') || code.includes('MH') || /^(T)[0-9]{6}$/.test(code)
const isFaculty = code =>
['H10', 'H20', 'H30', 'H40', 'H50', 'H55', 'H57', 'H60', 'H70', 'H74', 'H80', 'H90'].includes(code)
const filteredFacultyCodes = facultyCodes.filter(item => !ignoredFacultyCodes.includes(item))
const isFaculty = code => filteredFacultyCodes.includes(code)
const createRedisKeyForBasicStats = (id, yearType, specialGroups) => `BASIC_STATS_${id}_${yearType}_${specialGroups}`
const createRedisKeyForCreditStats = (id, yearType, specialGroups) => `CREDIT_STATS_${id}_${yearType}_${specialGroups}`
const createRedisKeyForGraduationStats = (id, yearType, specialGroups) =>
@@ -1,6 +1,5 @@
const { Op, where, fn, col } = require('sequelize')
const { Course, Credit, Enrollment, Student } = require('../models')
const { Course, Credit, Enrollment, Student } = require('../models/index')
const getCompletedCourses = async (studentNumbers, courseCodes) => {
let courses = await Course.findAll({
@@ -270,7 +270,6 @@ const courseYearlyStats = async (coursecodes, separate, anonymizationSalt, combi
attributes: ['student_studentnumber'],
where: { course_code: { [Op.in]: coursecodes } },
})
const enrollment = await Enrollment.findAll({
attributes: ['studentnumber'],
where: { course_code: { [Op.in]: coursecodes } },
@@ -1,45 +0,0 @@
const { CustomPopulationSearch } = require('../models/models_kone')
const getCustomPopulationSearchesByUser = async userId =>
CustomPopulationSearch.findAll({
where: {
userId,
},
})
const createCustomPopulationSearch = async (name, userId, students) =>
CustomPopulationSearch.create({
name,
userId,
students,
})
const updateCustomPopulationSearch = async (userId, id, students) => {
const targetCustomPopulationSearch = await CustomPopulationSearch.findOne({
where: {
id,
userId,
},
})
if (!targetCustomPopulationSearch) return null
return targetCustomPopulationSearch.update({
students,
})
}
const deleteCustomPopulationSearch = async (userId, id) =>
CustomPopulationSearch.destroy({
where: {
id,
userId,
},
})
module.exports = {
getCustomPopulationSearchesByUser,
createCustomPopulationSearch,
updateCustomPopulationSearch,
deleteCustomPopulationSearch,
}
@@ -0,0 +1,43 @@
import { CustomPopulationSearch } from '../models/kone'
export const getCustomPopulationSearchesByUser = async (userId: bigint) => {
return CustomPopulationSearch.findAll({
where: {
userId,
},
})
}
export const createCustomPopulationSearch = async (name: string, userId: bigint, students: string[]) => {
return CustomPopulationSearch.create({
name,
userId,
students,
})
}
export const updateCustomPopulationSearch = async (userId: bigint, id: bigint, students: string[]) => {
const targetCustomPopulationSearch = await CustomPopulationSearch.findOne({
where: {
id,
userId,
},
})
if (!targetCustomPopulationSearch) {
return null
}
return targetCustomPopulationSearch.update({
students,
})
}
export const deleteCustomPopulationSearch = async (userId: bigint, id: bigint) => {
return CustomPopulationSearch.destroy({
where: {
id,
userId,
},
})
}
@@ -1,6 +1,6 @@
const { Op } = require('sequelize')
const { ExcludedCourse } = require('../models/models_kone')
const { ExcludedCourse } = require('../models/kone')
const addExcludedCourses = async (programmecode, coursecodes, curriculum) => {
return ExcludedCourse.bulkCreate(
@@ -1,430 +0,0 @@
const { uniqBy } = require('lodash')
const { Op } = require('sequelize')
const {
dbConnections: { sequelize },
} = require('../../database/connection')
const {
Course,
Credit,
ElementDetail,
Organization,
ProgrammeModule,
SemesterEnrollment,
Student,
Studyright,
StudyrightElement,
Transfer,
} = require('../../models')
const {
formatFacultyProgramme,
formatFacultyProgrammeStudents,
formatFacultyStudyRight,
formatFacultyThesisWriter,
formatFacultyTransfer,
formatOrganization,
isNewProgramme,
mapCodesToIds,
} = require('./facultyHelpers')
const transferredFaculty = async (programmeCodeIn, programmeCodeOut, start, end) =>
(
await Transfer.findAll({
where: {
transferdate: {
[Op.between]: [start, end],
},
[Op.or]: [
{
sourcecode: programmeCodeOut,
},
{
targetcode: programmeCodeIn,
},
],
},
})
).map(formatFacultyTransfer)
const startedStudyrights = async (faculty, code, since, studyRightWhere) =>
(
await Studyright.findAll({
include: [
{
model: StudyrightElement,
required: true,
where: {
code,
},
include: {
model: ElementDetail,
required: true,
},
},
],
where: {
facultyCode: faculty,
startdate: {
[Op.gte]: since,
},
...studyRightWhere,
},
})
).map(formatFacultyStudyRight)
const graduatedStudyrights = async (faculty, code, since, studyrightWhere) =>
(
await Studyright.findAll({
include: [
{
model: StudyrightElement,
required: true,
where: {
code,
},
include: {
model: ElementDetail,
required: true,
},
},
],
where: {
facultyCode: faculty,
enddate: {
[Op.gte]: since,
},
graduated: 1,
...studyrightWhere,
},
})
).map(formatFacultyStudyRight)
const studyrightsByRightStartYear = async (faculty, code, since, graduated = 1) =>
(
await Studyright.findAll({
include: [
{
model: StudyrightElement,
attributes: ['code', 'startdate'],
where: {
code,
},
required: true,
include: {
model: ElementDetail,
required: true,
},
},
],
where: {
facultyCode: faculty,
startdate: {
[Op.gte]: since,
},
graduated,
},
})
).map(formatFacultyStudyRight)
const getStudyRightsByExtent = async (faculty, startDate, endDate, code, extents, graduated) =>
(
await Studyright.findAll({
include: {
model: StudyrightElement,
attributes: [],
required: true,
where: {
code,
},
include: {
model: ElementDetail,
attributes: [],
},
},
group: [sequelize.col('studyright.studyrightid')],
where: {
facultyCode: faculty,
extentcode: {
[Op.in]: extents,
},
prioritycode: {
[Op.not]: 6,
},
graduated: {
[Op.in]: graduated,
},
[Op.and]: [
sequelize.where(
sequelize.fn(
'GREATEST',
sequelize.col('studyright_elements.startdate'),
sequelize.col('studyright.startdate')
),
{
[Op.between]: [startDate, endDate],
}
),
],
},
})
).map(formatFacultyStudyRight)
const getStudyRightsByBachelorStart = async (faculty, startDate, endDate, code, extents, graduated) =>
(
await Studyright.findAll({
include: {
model: StudyrightElement,
required: true,
where: {
code,
},
},
where: {
facultyCode: faculty,
extentcode: {
[Op.in]: extents,
},
prioritycode: {
[Op.not]: 6,
},
graduated: {
[Op.in]: graduated,
},
startdate: {
[Op.between]: [startDate, endDate],
},
},
})
).map(formatFacultyStudyRight)
const getStudentsByStudentnumbers = async studentnumbers =>
(
await Student.findAll({
where: {
studentnumber: studentnumbers,
},
include: {
model: SemesterEnrollment,
attributes: ['semestercode', 'enrollmenttype'],
},
})
).map(formatFacultyProgrammeStudents)
const hasMasterRight = async id => {
return await Studyright.findOne({
where: {
studyrightid: id,
extentcode: 2,
},
})
}
const transferredInsideFaculty = async (programmes, allProgrammeCodes, since) =>
(
await Transfer.findAll({
where: {
transferdate: {
[Op.gte]: since,
},
sourcecode: allProgrammeCodes,
targetcode: programmes,
},
})
).map(formatFacultyTransfer)
const transferredAway = async (programmes, allProgrammeCodes, since) =>
(
await Transfer.findAll({
where: {
transferdate: {
[Op.gte]: since,
},
sourcecode: programmes,
targetcode: {
[Op.notIn]: allProgrammeCodes,
},
},
})
).map(formatFacultyTransfer)
const transferredTo = async (programmes, allProgrammeCodes, since) =>
(
await Transfer.findAll({
where: {
transferdate: {
[Op.gte]: since,
},
sourcecode: {
[Op.notIn]: allProgrammeCodes,
},
targetcode: programmes,
},
})
).map(formatFacultyTransfer)
const degreeProgrammesOfFaculty = async facultyCode =>
// Some programmenames are different, causing this to return multiples of same codes.
// Hence the uniqBy
uniqBy(
(
await ProgrammeModule.findAll({
attributes: ['code', 'name'],
include: {
model: Organization,
where: {
code: {
[Op.startsWith]: facultyCode,
},
},
},
group: ['programme_module.code', 'programme_module.name', 'organization.id'],
})
).map(formatFacultyProgramme),
'code'
)
const facultyOrganizationId = async faculty => {
return await Organization.findOne({
attributes: ['id'],
where: {
code: faculty,
},
})
}
const getChildOrganizations = async facultyId =>
(
await Organization.findAll({
attributes: ['id', 'name', 'code', 'parent_id'],
where: {
parent_id: facultyId,
},
})
).map(formatOrganization)
const thesisWriters = async (provider, since, thesisTypes, students) =>
(
await Credit.findAll({
attributes: ['id', 'course_code', 'attainment_date', 'student_studentnumber'],
include: {
model: Course,
attributes: ['course_unit_type'],
required: true,
where: {
course_unit_type: {
[Op.in]: thesisTypes,
},
},
include: {
model: Organization,
attributes: [],
required: true,
where: {
code: provider,
},
},
},
where: {
credittypecode: 4,
isStudyModule: {
[Op.not]: true,
},
attainment_date: {
[Op.gte]: since,
},
student_studentnumber: students.length > 0 ? students : { [Op.not]: null },
},
})
).map(formatFacultyThesisWriter)
// Some programme modules are not directly associated to a faculty (organization).
// Some have intermediate organizations, such as department, so the connection must be digged up
const findFacultyProgrammeCodes = async (faculty, programmeFilter) => {
let allProgrammes = []
let allProgrammeCodes = []
const directAssociationProgrammes = await degreeProgrammesOfFaculty(faculty)
allProgrammes = allProgrammes.concat(directAssociationProgrammes)
allProgrammeCodes = allProgrammeCodes.concat(directAssociationProgrammes.map(prog => prog.code))
// find faculty's organization id and its direct child organizations
const { id } = await facultyOrganizationId(faculty)
const facultyChildOrganizations = await getChildOrganizations(id)
// get programme modules that have a faculty child as organization(_id)
for (const org of facultyChildOrganizations) {
const childAssociationProgrammes = await degreeProgrammesOfFaculty(org.code)
if (childAssociationProgrammes.length > 0) {
// eslint-disable-next-line no-loop-func
childAssociationProgrammes.forEach(prog => {
if (!(prog.code in allProgrammeCodes)) {
allProgrammes = allProgrammes.concat([prog])
allProgrammeCodes = allProgrammeCodes.concat([prog.code])
}
})
} else {
// dig deeper
const grandChildren = await getChildOrganizations(org.id)
if (grandChildren.length > 0) {
for (const gcOrg of grandChildren) {
const associatedProgrammes = await degreeProgrammesOfFaculty(gcOrg.code)
if (associatedProgrammes.length > 0) {
// eslint-disable-next-line no-loop-func
associatedProgrammes.forEach(prog => {
if (!(prog.code in allProgrammeCodes)) {
allProgrammes = allProgrammes.concat([prog])
allProgrammeCodes = allProgrammeCodes.concat([prog.code])
}
})
}
}
}
}
}
if (programmeFilter === 'NEW_STUDY_PROGRAMMES') {
allProgrammes = allProgrammes.filter(prog => isNewProgramme(prog.code))
}
mapCodesToIds(allProgrammes)
return allProgrammes
}
const getTransferredToAndAway = async (programmeCodes, allProgrammeCodes, since) => {
const awayTransfers = await transferredAway(programmeCodes, allProgrammeCodes, since)
const toTransfers = await transferredTo(programmeCodes, allProgrammeCodes, since)
return [...toTransfers, ...awayTransfers]
}
const getTransferredInside = async (programmeCodes, allProgrammeCodes, since) => {
return await transferredInsideFaculty(programmeCodes, allProgrammeCodes, since)
}
const getTransfersOut = async (programmeCode, start, end) => {
return await transferredFaculty([], [programmeCode], start, end)
}
const getTransfersIn = async (programmeCode, start, end) => {
return await transferredFaculty([programmeCode], [], start, end)
}
module.exports = {
startedStudyrights,
graduatedStudyrights,
studyrightsByRightStartYear,
hasMasterRight,
transferredInsideFaculty,
transferredAway,
transferredTo,
thesisWriters,
findFacultyProgrammeCodes,
getTransferredToAndAway,
getTransferredInside,
getStudyRightsByExtent,
getStudyRightsByBachelorStart,
getStudentsByStudentnumbers,
getTransfersOut,
getTransfersIn,
}
@@ -0,0 +1,387 @@
import { uniqBy } from 'lodash'
import { Op } from 'sequelize'
import { dbConnections } from '../../database/connection'
const { sequelize } = dbConnections
import { CreditTypeCode } from '../../types/creditTypeCode'
import { ExtentCode } from '../../types/extentCode'
import { PriorityCode } from '../../types/priorityCode'
import {
Course,
Credit,
ElementDetail,
Organization,
ProgrammeModule,
SemesterEnrollment,
Student,
Studyright,
StudyrightElement,
} from '../../models'
import {
formatFacultyProgramme,
formatFacultyProgrammeStudents,
formatFacultyStudyRight,
formatFacultyThesisWriter,
formatOrganization,
} from './facultyFormatHelpers'
import { getExtentFilter, isNewProgramme, mapCodesToIds } from './facultyHelpers'
export const startedStudyrights = async (
facultyCode: string,
since: Date,
code: string,
includeAllSpecials: boolean
) => {
const studyRightWhere = getExtentFilter(includeAllSpecials)
const query = {
include: [
{
model: StudyrightElement,
required: true,
where: {
code,
},
include: [
{
model: ElementDetail,
required: true,
},
],
},
],
where: {
facultyCode,
startdate: {
[Op.gte]: since,
},
...studyRightWhere,
},
}
const studyRights = await Studyright.findAll(query)
return studyRights.map(formatFacultyStudyRight)
}
export const graduatedStudyrights = async (
facultyCode: string,
since: Date,
code: string,
includeAllSpecials?: boolean
) => {
const studyRightWhere = includeAllSpecials !== undefined ? getExtentFilter(includeAllSpecials) : {}
const query: Record<string, any> = {
include: [
{
model: StudyrightElement,
required: true,
where: {
code,
},
include: [
{
model: ElementDetail,
required: true,
},
],
},
],
where: {
facultyCode,
enddate: {
[Op.gte]: since,
},
graduated: 1,
...studyRightWhere,
},
}
const studyRights = await Studyright.findAll(query)
return studyRights.map(formatFacultyStudyRight)
}
export const studyrightsByRightStartYear = async (
facultyCode: string,
since: Date,
code: string | null = null,
graduated: number | number[] = 1
) => {
const query: Record<string, any> = {
include: [
{
model: StudyrightElement,
attributes: ['code', 'startdate'],
required: true,
include: [
{
model: ElementDetail,
required: true,
},
],
},
],
where: {
facultyCode,
startdate: {
[Op.gte]: since,
},
graduated,
},
}
if (code !== null) {
query.include[0].where = { code }
}
const studyRights = await Studyright.findAll(query)
return studyRights.map(formatFacultyStudyRight)
}
export const getStudyRightsByExtent = async (
facultyCode: string,
startDate: Date,
endDate: Date,
code: string,
extentCodes: ExtentCode[],
graduated: number[]
) => {
const query: Record<string, any> = {
include: {
model: StudyrightElement,
attributes: [],
required: true,
where: {
code,
},
include: [
{
model: ElementDetail,
attributes: [],
},
],
},
group: [sequelize.col('studyright.studyrightid')],
where: {
facultyCode,
extentcode: {
[Op.in]: extentCodes,
},
prioritycode: {
[Op.not]: PriorityCode.OPTION,
},
graduated: {
[Op.in]: graduated,
},
[Op.and]: [
sequelize.where(
sequelize.fn(
'GREATEST',
sequelize.col('studyright_elements.startdate'),
sequelize.col('studyright.startdate')
),
{
[Op.between]: [startDate, endDate],
}
),
],
},
}
const studyRights = await Studyright.findAll(query)
return studyRights.map(formatFacultyStudyRight)
}
export const getStudyRightsByBachelorStart = async (
facultyCode: string,
startDate: Date,
endDate: Date,
code: string,
extentCodes: ExtentCode[],
graduated: number[]
) => {
const query: Record<string, any> = {
include: {
model: StudyrightElement,
required: true,
where: {
code,
},
},
where: {
facultyCode,
extentcode: {
[Op.in]: extentCodes,
},
prioritycode: {
[Op.not]: PriorityCode.OPTION,
},
graduated: {
[Op.in]: graduated,
},
startdate: {
[Op.between]: [startDate, endDate],
},
},
}
const studyRights = await Studyright.findAll(query)
return studyRights.map(formatFacultyStudyRight)
}
export const getStudentsByStudentnumbers = async (studentNumbers: string[]) => {
const query: Record<string, any> = {
where: {
studentnumber: studentNumbers,
},
include: {
model: SemesterEnrollment,
attributes: ['semestercode', 'enrollmenttype'],
},
}
const students = await Student.findAll(query)
return students.map(formatFacultyProgrammeStudents)
}
export const hasMasterRight = async (studyRightId: string): Promise<boolean> => {
const studyRight = await Studyright.findOne({
where: {
studyrightid: studyRightId,
extentcode: ExtentCode.MASTER,
},
})
return studyRight !== null
}
export const degreeProgrammesOfFaculty = async (facultyCode: string) => {
const query: Record<string, any> = {
attributes: ['code', 'name'],
include: {
model: Organization,
where: {
code: {
[Op.startsWith]: facultyCode,
},
},
},
group: ['programme_module.code', 'programme_module.name', 'organization.id'],
}
const programmes = await ProgrammeModule.findAll(query)
return uniqBy(programmes.map(formatFacultyProgramme), 'code')
}
export const facultyOrganizationId = async (facultyCode: string) => {
return await Organization.findOne({
attributes: ['id'],
where: {
code: facultyCode,
},
})
}
export const getChildOrganizations = async (facultyId: string) => {
const query: Record<string, any> = {
attributes: ['id', 'name', 'code', 'parent_id'],
where: {
parent_id: facultyId,
},
}
const organizations = await Organization.findAll(query)
return organizations.map(formatOrganization)
}
export const thesisWriters = async (provider: string, since: Date, thesisTypes: string[], studentNumbers: string[]) => {
const query: Record<string, any> = {
attributes: ['id', 'course_code', 'attainment_date', 'student_studentnumber'],
include: {
model: Course,
attributes: ['course_unit_type'],
required: true,
where: {
course_unit_type: {
[Op.in]: thesisTypes,
},
},
include: [
{
model: Organization,
attributes: [],
required: true,
where: {
code: provider,
},
},
],
},
where: {
credittypecode: CreditTypeCode.PASSED,
isStudyModule: {
[Op.not]: true,
},
attainment_date: {
[Op.gte]: since,
},
student_studentnumber: studentNumbers.length > 0 ? studentNumbers : { [Op.not]: null },
},
}
const thesisWriterCredits = await Credit.findAll(query)
return thesisWriterCredits.map(formatFacultyThesisWriter)
}
// Some programme modules are not directly associated to a faculty (organization).
// Some have intermediate organizations, such as department, so the connection must be digged up
export const findFacultyProgrammeCodes = async (facultyCode: string, programmeFilter: string) => {
let allProgrammes = []
let allProgrammeCodes = []
const directAssociationProgrammes = await degreeProgrammesOfFaculty(facultyCode)
allProgrammes = allProgrammes.concat(directAssociationProgrammes)
allProgrammeCodes = allProgrammeCodes.concat(directAssociationProgrammes.map(programme => programme.code))
// find faculty's organization id and its direct child organizations
const { id } = await facultyOrganizationId(facultyCode)
const facultyChildOrganizations = await getChildOrganizations(id)
// get programme modules that have a faculty child as organization(_id)
for (const org of facultyChildOrganizations) {
const childAssociationProgrammes = await degreeProgrammesOfFaculty(org.code)
if (childAssociationProgrammes.length > 0) {
// eslint-disable-next-line no-loop-func
childAssociationProgrammes.forEach(programme => {
if (!(programme.code in allProgrammeCodes)) {
allProgrammes = allProgrammes.concat([programme])
allProgrammeCodes = allProgrammeCodes.concat([programme.code])
}
})
} else {
// dig deeper
const grandChildren = await getChildOrganizations(org.id)
if (grandChildren.length > 0) {
for (const gcOrg of grandChildren) {
const associatedProgrammes = await degreeProgrammesOfFaculty(gcOrg.code)
if (associatedProgrammes.length > 0) {
// eslint-disable-next-line no-loop-func
associatedProgrammes.forEach(programme => {
if (!(programme.code in allProgrammeCodes)) {
allProgrammes = allProgrammes.concat([programme])
allProgrammeCodes = allProgrammeCodes.concat([programme.code])
}
})
}
}
}
}
}
if (programmeFilter === 'NEW_STUDY_PROGRAMMES') {
allProgrammes = allProgrammes.filter(programme => isNewProgramme(programme.code))
}
mapCodesToIds(allProgrammes)
return allProgrammes
}
@@ -1,36 +1,28 @@
const { indexOf, isArray } = require('lodash')
const moment = require('moment')
import { indexOf, isArray } from 'lodash'
import moment from 'moment'
const { codes } = require('../../../config/programmeCodes')
const { defineYear, getStatsBasis, getYearsArray } = require('../studyProgramme/studyProgrammeHelpers')
const {
import { programmeCodes } from '../../config/programmeCodes'
import { ExtentCode } from '../../types/extentCode'
import { defineYear, getStatsBasis, getYearsArray } from '../studyProgramme/studyProgrammeHelpers'
import { graduatedStudyrights, startedStudyrights, studyrightsByRightStartYear } from './faculty'
import { checkCommissioned, checkTransfers, findRightProgramme, isNewProgramme } from './facultyHelpers'
import {
getTransferredInside,
getTransferredToAndAway,
getTransfersIn,
graduatedStudyrights,
startedStudyrights,
studyrightsByRightStartYear,
transferredAway,
transferredInsideFaculty,
transferredTo,
} = require('./faculty')
const {
checkCommissioned,
checkTransfers,
findRightProgramme,
getExtentFilter,
isNewProgramme,
} = require('./facultyHelpers')
} from './facultyTransfers'
const filterDuplicateStudyrights = studyrights => {
const filterDuplicateStudyrights = (studyRights: { studyrightid: string }[]) => {
// bachelor+master students have two studyrights (separated by two last digits in studyrightid)
// choose only bachelor one, so we don't count start of masters as starting in faculty
const rightsToCount = {}
studyrights.forEach(right => {
const id = right.studyrightid.slice(0, -2)
if (right.studyrightid.slice(-2) === '-1') {
rightsToCount[id] = right
studyRights.forEach(studyRight => {
const id = studyRight.studyrightid.slice(0, -2)
if (studyRight.studyrightid.slice(-2) === '-1') {
rightsToCount[id] = studyRight
}
})
return Object.values(rightsToCount)
@@ -84,17 +76,16 @@ const getFacultyStarters = async (
const startedGraphStats = [...graphStats]
const startedTableStats = { ...tableStats }
const programmeTableStats = {}
const studyrightWhere = getExtentFilter(includeAllSpecials)
const start = new Date('2017-01-01').toUTCString()
const start = new Date('2017-01-01')
const end = new Date()
for (const code of programmes) {
const studyrights = await startedStudyrights(faculty, code, since, studyrightWhere)
let filteredStudyrights = filterDuplicateStudyrights(studyrights)
const studyRights = await startedStudyrights(faculty, since, code, includeAllSpecials)
let filteredStudyrights = filterDuplicateStudyrights(studyRights)
const insideTransfers = await getTransfersIn(code, start, end)
filteredStudyrights = filteredStudyrights.filter(
studyright => !checkTransfers(studyright, insideTransfers, insideTransfers)
)
const keys = Object.keys(codes)
const keys = Object.keys(programmeCodes)
if (!includeAllSpecials) {
filteredStudyrights = filteredStudyrights.filter(
@@ -109,7 +100,7 @@ const getFacultyStarters = async (
let programmeId = programme
if (keys.includes(programme)) {
programmeId = codes[programme]
programmeId = programmeCodes[programme]
}
if (programmeFilter === 'ALL_PROGRAMMES' || isNewProgramme(programme)) {
@@ -161,10 +152,9 @@ const getFacultyGraduates = async (
Object.keys(tableStats).forEach(year => {
graduatedTableStats[year] = [0, 0, 0, 0, 0]
})
const keys = Object.keys(codes)
const studyrightWhere = getExtentFilter(includeAllSpecials)
const keys = Object.keys(programmeCodes)
for (const code of programmes) {
let graduatedRights = await graduatedStudyrights(faculty, code, since, studyrightWhere)
let graduatedRights = await graduatedStudyrights(faculty, since, code, includeAllSpecials)
if (!includeAllSpecials) {
graduatedRights = graduatedRights.filter(
studyright =>
@@ -178,7 +168,7 @@ const getFacultyGraduates = async (
let programmeId = programme
if (keys.includes(programme)) {
programmeId = codes[programme]
programmeId = programmeCodes[programme]
}
if (programmeFilter === 'ALL_PROGRAMMES' || isNewProgramme(programme)) {
@@ -194,15 +184,15 @@ const getFacultyGraduates = async (
}
programmeTableStats[programmeId][endYear][0] += 1
if (extentcode === 1) {
if (extentcode === ExtentCode.BACHELOR) {
graduatedGraphStats[1][indexOf(yearsArray, endYear)] += 1
graduatedTableStats[endYear][1] += 1
programmeTableStats[programmeId][endYear][1] += 1
} else if (extentcode === 2) {
} else if (extentcode === ExtentCode.MASTER) {
graduatedGraphStats[2][indexOf(yearsArray, endYear)] += 1
graduatedTableStats[endYear][2] += 1
programmeTableStats[programmeId][endYear][2] += 1
} else if (extentcode === 4) {
} else if (extentcode === ExtentCode.DOCTOR) {
graduatedGraphStats[3][indexOf(yearsArray, endYear)] += 1
graduatedTableStats[endYear][3] += 1
programmeTableStats[programmeId][endYear][3] += 1
@@ -239,9 +229,9 @@ const getInsideTransfers = async (programmeCodes, allProgrammeCodes, since, incl
return await transferredInsideFaculty(programmeCodes, allProgrammeCodes, since)
}
const insiders = await transferredInsideFaculty(programmeCodes, allProgrammeCodes, since)
const studyrights = (
await studyrightsByRightStartYear(faculty, new Date(moment('2017-08-01', 'YYYY-MM-DD')).toUTCString())
).map(studyright => studyright.studyrightid)
const studyrights = (await studyrightsByRightStartYear(faculty, moment('2017-08-01', 'YYYY-MM-DD').toDate())).map(
studyright => studyright.studyrightid
)
const filteredTransfers = insiders.filter(transfer => studyrights.includes(transfer.studyrightid))
return filteredTransfers
}
@@ -354,11 +344,18 @@ const getFacultyTransfers = async (
})
}
const combineFacultyBasics = async (faculty, programmes, yearType, allProgrammeCodes, programmeFilter, special) => {
export const combineFacultyBasics = async (
faculty,
programmes,
yearType,
allProgrammeCodes,
programmeFilter,
special
) => {
const counts = {}
const countsGraduations = {}
const years = []
const programmeData = {}
const programmeData: { started?: object; graduated?: object; transferred?: object } = {}
const isAcademicYear = yearType === 'ACADEMIC_YEAR'
const includeAllSpecials = special === 'SPECIAL_INCLUDED'
const since = isAcademicYear ? new Date('2017-08-01') : new Date('2017-01-01')
@@ -503,5 +500,3 @@ const combineFacultyBasics = async (faculty, programmes, yearType, allProgrammeC
return allBasics
}
module.exports = { combineFacultyBasics }
@@ -1,20 +1,13 @@
const { getCreditsProduced } = require('../providerCredits')
const { isRelevantProgramme } = require('../studyProgramme/studyProgrammeHelpers')
const { getProgrammes } = require('./facultyService')
/*
Returns credits produced by the programmes of the faculty but also the credits where the provider is the faculty itself.
*/
/** Returns credits produced by the programmes of the faculty but also the credits where the provider is the faculty itself. */
const getFacultyCredits = async (facultyCode, isAcademicYear) => {
const allProgrammes = (await getProgrammes(facultyCode)).data
// Filter out old & obsolete codes
const programmes = allProgrammes.filter(
({ code }) =>
(code.includes('KH') && !code.startsWith('2_KH') && !code.endsWith('_2')) ||
(code.includes('MH') && !code.startsWith('2_MH') && !code.endsWith('_2')) ||
/^(T)[0-9]{6}$/.test(code)
)
const programmes = allProgrammes.filter(({ code }) => isRelevantProgramme(code))
const facultyCredits = await getCreditsProduced(facultyCode, isAcademicYear)
const stats = { [facultyCode]: facultyCredits, codes: [facultyCode], programmeNames: {}, ids: [facultyCode] }
for (const programme of programmes) {
const programmeCode = programme.code
@@ -0,0 +1,61 @@
export const formatFacultyStudyRight = studyright => {
return {
studyrightid: studyright.studyrightid,
studystartdate: studyright.studystartdate,
startdate: studyright.startdate,
enddate: studyright.enddate,
givendate: studyright.givendate,
graduated: studyright.graduated,
active: studyright.active,
prioritycode: studyright.prioritycode,
extentcode: studyright.extentcode,
studentnumber: studyright.student_studentnumber,
studyrightElements: studyright.studyright_elements,
facultyCode: studyright.facultyCode,
}
}
export const formatFacultyProgrammeStudents = student => {
return {
stundentNumber: student.studentnumber,
homeCountryEn: student.home_country_en,
genderCode: student.gender_code,
semesters: student.semester_enrollments.map(enrollment => enrollment.dataValues),
}
}
export const formatFacultyTransfer = transfer => {
return {
sourcecode: transfer.sourcecode,
targetcode: transfer.targetcode,
transferdate: transfer.transferdate,
studyrightid: transfer.studyrightid,
studentnumber: transfer.studentnumber,
}
}
export const formatFacultyProgramme = programme => {
return {
code: programme.code,
name: programme.name,
}
}
export const formatFacultyThesisWriter = credit => {
return {
course_code: credit.course_code,
credits: credit.credits,
attainment_date: credit.attainment_date,
student_studentnumber: credit.student_studentnumber,
courseUnitType: credit.course.course_unit_type,
}
}
export const formatOrganization = organization => {
return {
id: organization.id,
name: organization.name,
code: organization.code,
parentId: organization.parent_id,
}
}
@@ -1,18 +1,19 @@
const moment = require('moment')
import moment from 'moment'
const { codes } = require('../../../config/programmeCodes')
const { countTimeCategories, getBachelorStudyRight, getStatutoryAbsences } = require('../graduationHelpers')
const { defineYear, getMedian, getYearsArray, getYearsObject } = require('../studyProgramme/studyProgrammeHelpers')
const { graduatedStudyrights, hasMasterRight, studyrightsByRightStartYear } = require('./faculty')
const { findRightProgramme, isNewProgramme } = require('./facultyHelpers')
const { getProgrammes } = require('./facultyService')
import { programmeCodes } from '../../config/programmeCodes'
import { ExtentCode } from '../../types/extentCode'
import { countTimeCategories, getBachelorStudyRight, getStatutoryAbsences } from '../graduationHelpers'
import { defineYear, getMedian, getYearsArray, getYearsObject } from '../studyProgramme/studyProgrammeHelpers'
import { graduatedStudyrights, hasMasterRight, studyrightsByRightStartYear } from './faculty'
import { findRightProgramme, isNewProgramme } from './facultyHelpers'
import { getProgrammes } from './facultyService'
const sortProgrammes = data => {
const sortProgrammes = programmes => {
const check = name => {
if (Number.isNaN(name[0])) return -1
return 1
}
data.sort((a, b) => {
programmes.sort((a, b) => {
if (check(a.name) === check(b.name)) return a.name.localeCompare(b.name)
return check(a.name) - check(b.name)
})
@@ -22,8 +23,8 @@ const getSortedProgIds = progs => {
let programmes = []
for (const prog of progs) {
if (Object.keys(codes).includes(prog)) {
const name = codes[prog]
if (Object.keys(programmeCodes).includes(prog)) {
const name = programmeCodes[prog]
programmes = [...programmes, { name, code: prog }]
} else {
programmes = [...programmes, { name: prog, code: prog }]
@@ -46,17 +47,17 @@ const getProgrammeObjectBasis = (years, levels, emptyObject = true) => {
)
}
const hasMS = async (programme, elements, studyrightid) => {
const hasMS = async (programme: string, elements, studyrightid: string): Promise<boolean> => {
// Only bachelor studyright: socCom, kasvatustiede - varhaiskasvatus track, farmasia - applied only to Bc = farmaseutti,
if (['KH74_001', '620050-ba', '620030-ba', '520091-ba'].includes(programme)) {
return false
}
if (programme === 'KH60_001') {
return !elements.some(el => ['EDUK-VO', '0371', '620050-ba', '620030-ba'].includes(el.dataValues.code))
return !elements.some(element => ['EDUK-VO', '0371', '620050-ba', '620030-ba'].includes(element.dataValues.code))
}
if (programme === 'KH55_001') {
const result = await hasMasterRight(studyrightid.replace(/-1$/, '-2'))
if (!result) {
const hasRight = await hasMasterRight(studyrightid.replace(/-1$/, '-2'))
if (!hasRight) {
return false
}
}
@@ -80,18 +81,18 @@ const addGraduation = async (
if (programme === 'MH30_001' || programme === 'MH30_003') {
level = 'bcMsCombo'
} else if (extentcode === 1) {
} else if (extentcode === ExtentCode.BACHELOR) {
level = 'bachelor'
} else if (extentcode === 2) {
} else if (extentcode === ExtentCode.MASTER) {
if (studyrightid.slice(-2) === '-2') {
level = 'bcMsCombo'
actualStartdate = await findBachelorStartdate(studyrightid.replace(/-2$/, '-1'))
} else {
level = 'master'
}
} else if (extentcode === 4) {
} else if (extentcode === ExtentCode.DOCTOR) {
level = 'doctor'
} else if (extentcode === 3) {
} else if (extentcode === ExtentCode.LICENTIATE) {
level = 'licentiate'
} else {
return
@@ -104,8 +105,9 @@ const addGraduation = async (
graduationAmounts[level][year] += 1
graduationTimes[level][year] = [...graduationTimes[level][year], timeToGraduation]
if (!(programme in programmes[level][year]))
if (!(programme in programmes[level][year])) {
programmes[level][year][programme] = { graduationTimes: [], graduationAmounts: 0 }
}
programmes[level][year][programme].graduationAmounts += 1
programmes[level][year][programme].graduationTimes = [
@@ -116,7 +118,7 @@ const addGraduation = async (
const getClassSizes = async (faculty, programmeCodes, since, classSizes, programmeFilter, years) => {
for (const code of programmeCodes) {
const studyrights = await studyrightsByRightStartYear(faculty, code, since, [0, 1])
const studyrights = await studyrightsByRightStartYear(faculty, since, code, [0, 1])
// get all received studyrights for each year
// a transferred student is counted into new programmes class size i.e.
@@ -132,9 +134,9 @@ const getClassSizes = async (faculty, programmeCodes, since, classSizes, program
if (code === 'MH30_001' || code === 'MH30_003') {
level = 'bcMsCombo'
} else if (extentcode === 1) {
} else if (extentcode === ExtentCode.BACHELOR) {
level = 'bachelor'
} else if (extentcode === 2) {
} else if (extentcode === ExtentCode.MASTER) {
if (studyrightid.slice(-2) === '-2') {
level = 'bcMsCombo'
actualStartdate = await findBachelorStartdate(studyrightid.replace(/-2$/, '-1'))
@@ -143,9 +145,9 @@ const getClassSizes = async (faculty, programmeCodes, since, classSizes, program
} else {
level = 'master'
}
} else if (extentcode === 4) {
} else if (extentcode === ExtentCode.DOCTOR) {
level = 'doctor'
} else if (extentcode === 3) {
} else if (extentcode === ExtentCode.LICENTIATE) {
level = 'licentiate'
} else {
continue
@@ -162,7 +164,7 @@ const getClassSizes = async (faculty, programmeCodes, since, classSizes, program
}
if (!(code in classSizes.programmes)) {
if (extentcode === 2) {
if (extentcode === ExtentCode.MASTER) {
classSizes.programmes[code] = {}
classSizes.programmes[code].bcMsCombo = getYearsObject({ years, emptyArrays: false })
classSizes.programmes[code].master = getYearsObject({ years, emptyArrays: false })
@@ -171,7 +173,7 @@ const getClassSizes = async (faculty, programmeCodes, since, classSizes, program
}
}
if (extentcode === 2 && ['master', 'bcMsCombo'].includes(level)) {
if (extentcode === ExtentCode.MASTER && ['master', 'bcMsCombo'].includes(level)) {
classSizes.programmes[code][level][startYear] += 1
} else {
classSizes.programmes[code][startYear] += 1
@@ -210,9 +212,9 @@ const count = async (
for (const code of programmeCodes) {
let studyrights = null
if (mode === 'gradYear') {
studyrights = await graduatedStudyrights(faculty, code, since)
studyrights = await graduatedStudyrights(faculty, since, code)
} else {
studyrights = await studyrightsByRightStartYear(faculty, code, since)
studyrights = await studyrightsByRightStartYear(faculty, since, code)
}
for (const right of studyrights) {
const { enddate, startdate, studyrightid, extentcode, studyrightElements, studentnumber } = right
@@ -289,9 +291,8 @@ const count = async (
{
median,
amount: graduationAmounts[level][year],
/* Include individual graduation times for the sake of university-level
evaluation overview (UniversityView) which has to count medians by itself
*/
// Include individual graduation times for the sake of university-level
// evaluation overview which has to count medians by itself
times: graduationTimes[level][year],
statistics,
name: year,
@@ -303,7 +304,7 @@ const count = async (
return data
}
const countGraduationTimes = async (faculty, programmeFilter) => {
export const countGraduationTimes = async (faculty, programmeFilter) => {
const programmes = (await getProgrammes(faculty, programmeFilter)).data
const isAcademicYear = false
const since = isAcademicYear ? new Date('2017-08-01') : new Date('2017-01-01')
@@ -369,5 +370,3 @@ const countGraduationTimes = async (faculty, programmeFilter) => {
return { id: faculty, goals, byGradYear, byStartYear, programmeNames, classSizes }
}
module.exports = { countGraduationTimes }

Some files were not shown because too many files have changed in this diff Show More