Compare commits

...
573 Commits
Author SHA1 Message Date
Valtteri Kantanen 6f8b6133e5 Revert "[Backend] Type routes/feedback"
This reverts commit d8dcf1608d.
2024-07-22 15:10:54 +03:00
ShootingStar91 1118d2b2da Remove deprecated route 2024-07-22 15:08:42 +03:00
Riku Rauhala d8dcf1608d [Backend] Type routes/feedback 2024-07-22 15:01:21 +03:00
Valtteri Kantanen 592832eb95 [Study programme overview] Extract function for checking if student has transferred from or to programme 2024-07-22 14:51:18 +03:00
Riku Rauhala dba5045982 Revert [Backend] Enable path aliases 2024-07-22 14:27:03 +03:00
Riku Rauhala 2513986361 [Backend] Enable path aliases 2024-07-22 14:17:21 +03:00
Riku Rauhala e7dee7f139 [Backend] Type routes/changelog 2024-07-22 14:00:53 +03:00
Riku Rauhala fdcc7648c5 [Backend] Remove logoutUrl missing error 2024-07-22 13:48:03 +03:00
Riku Rauhala b3ddbf669a [Backend] Type routes/login 2024-07-22 13:25:43 +03:00
Riku Rauhala ca00efffd4 [Backend] Install @types/express as a dev dependency 2024-07-22 13:25:43 +03:00
Valtteri Kantanen b73850c8c6 [Study programme overview] Fix type error 2024-07-22 13:14:01 +03:00
Valtteri Kantanen db86b1a8d3 [Study programme overview] Use new models to calculate thesis writer stats 2024-07-22 12:49:47 +03:00
Valtteri Kantanen 6c27c11356 [Study programme overview] Exclude students who have transferred away from programme when 'includeAllSpecials' is false 2024-07-22 10:29:24 +03:00
Valtteri Kantanen 5e4f5b5e7a [Study programme overview] Fetch students only once when calculating study track stats 2024-07-22 10:29:24 +03:00
Valtteri Kantanen e9b7c677f8 [Evaluation overview] Align graduation times toggles horizontally 2024-07-22 10:29:24 +03:00
Valtteri Kantanen 7fa8ec5176 [Study programme overview] Update texts in infoboxes 2024-07-22 10:29:24 +03:00
Valtteri Kantanen 027899234f [Evaluation overview] Don't try to render objects 2024-07-22 10:29:24 +03:00
Valtteri Kantanen 5e223eb749 [Backend] Upgrade typescript from 5.4.5 to 5.5.3 2024-07-22 10:29:24 +03:00
Valtteri Kantanen cf5049db05 Remove strictPropertyInitialization: false from tsconfig.json 2024-07-22 10:29:24 +03:00
Valtteri Kantanen d20db4feb4 [Backend] Fix types 2024-07-22 10:29:24 +03:00
Valtteri Kantanen d214fe0282 [Backend] Add InferAttributes to all models and definite assignment assertions to all their properties 2024-07-22 10:29:24 +03:00
Riku Rauhala 492ad09e36 [Documentation] Document kone-db 2024-07-19 21:31:45 +03:00
Riku Rauhala f62cb9718c Create resources.md 2024-07-19 21:26:02 +03:00
Riku Rauhala a6ac9493d6 [Documentation] Document user-db 2024-07-19 21:26:02 +03:00
Valtteri Kantanen 93f1c3dc99 [Study programme overview] Remove pop-up from 'Other countries' table cell if there are no students from other countries 2024-07-19 17:30:14 +03:00
Valtteri Kantanen 853c4ceba5 [Study programme overview] Sort graduation stats by starting year in descending order 2024-07-19 17:30:14 +03:00
Valtteri Kantanen d485fc604d [Study programme overview] Update infobox texts to reflect recent changes 2024-07-19 17:30:14 +03:00
Valtteri Kantanen 68af9ab38c [Frontend] Update dependency unfuck-spa-shibboleth-session 2024-07-19 17:30:14 +03:00
Valtteri Kantanen 899d5e4e43 [Backend] Update tsx, remove nodemon and ts-node from dependencies 2024-07-19 17:30:14 +03:00
Riku Rauhala 4aadf329ea [Backend] Type organization service 2024-07-19 17:26:26 +03:00
Riku Rauhala 23ebb69778 [Backend] Type provider service 2024-07-19 16:30:02 +03:00
Riku Rauhala 855ca8e134 [Backend] Type mailService 2024-07-19 13:29:04 +03:00
Riku RauhalaandGitHub 9b21a4a1d4 Merge pull request #4579 from UniversityOfHelsinkiCS/fd-get-user-fix
[Backend] Renamed programmeRights to detailedProgrammeRights in fdGetUser
2024-07-19 13:22:57 +03:00
Valtteri Kantanen c083803858 [Backend] Fix error logging 2024-07-19 13:22:38 +03:00
Matti Räsänen 62735ab481 [Backend] Renamed programmeRights to detailedProgrammeRights in fdGetUser 2024-07-19 13:14:07 +03:00
Valtteri Kantanen cd252469e3 [Study programme overview] Add more specific types 2024-07-19 11:59:24 +03:00
Riku Rauhala 4ff5e42752 [Faculties] Remove refences to csv
Downloads are in xlsx format, not csv
2024-07-19 10:45:03 +03:00
Riku Rauhala d755aaab92 [Frontend] Refactor to remove identical code 2024-07-19 10:41:55 +03:00
Riku Rauhala b98b03b4a0 [Frontend] Fix a typo 2024-07-19 08:57:23 +03:00
Riku RauhalaandGitHub 6d7dfad89a Merge pull request #4577 from UniversityOfHelsinkiCS/type-user-service
Type user service
2024-07-18 20:52:34 +03:00
Riku Rauhala 6070486745 [Backend] Fix TypeScript issues 2024-07-18 20:35:55 +03:00
Riku RauhalaandGitHub f5ee0211a4 Merge branch 'master' into type-user-service 2024-07-18 20:10:30 +03:00
Riku Rauhala 49e1b3e99d [Backend] Fix fetching user 2024-07-18 20:03:57 +03:00
Valtteri Kantanen d5256f9b39 [Backend] Remove strictNullChecks: false from tsconfig.json 2024-07-18 17:39:27 +03:00
Valtteri Kantanen 3bdf76ed03 [Backend] Fix createLocaleComparator 2024-07-18 17:38:01 +03:00
Valtteri Kantanen cc692f34c3 [Backend] Add null checks to students.ts and add a util function keysOf 2024-07-18 17:37:30 +03:00
Valtteri Kantanen 98a56eead4 [Study programme overview] Fix tests 2024-07-18 15:40:04 +03:00
Riku Rauhala b823d0f23a [Backend] Extract type DetailedProgrammeRights 2024-07-18 15:39:47 +03:00
ShootingStar91andRiku Rauhala a3648898e6 Add SIS-models foreign keys 2024-07-18 15:39:47 +03:00
ShootingStar91andRiku Rauhala 0b64a9a11f Add faculty code as foreign key to sisstudyright 2024-07-18 15:39:47 +03:00
Valtteri KantanenandRiku Rauhala 4bb61d0784 [Study programme overview] Use new models in average graduation times calculations 2024-07-18 15:39:47 +03:00
Arttu KangasandGitHub 71bc53d787 Merge pull request #4576 from UniversityOfHelsinkiCS/ts-test-branch
Add faculty code as foreign key to sisstudyright
2024-07-18 15:34:44 +03:00
Valtteri Kantanen 005107882c [Study programme overview] Use new models in average graduation times calculations 2024-07-18 15:31:21 +03:00
Riku Rauhala 367376065e [Backend] Update createLocaleComparator 2024-07-18 15:30:29 +03:00
Riku Rauhala 45b72cef56 [Backend] Improve userService typing 2024-07-18 15:30:29 +03:00
Riku Rauhala 60ff7a210d [Backend] Type and refactor userService 2024-07-18 15:30:29 +03:00
ShootingStar91 0c7de151ba Add SIS-models foreign keys 2024-07-18 15:27:37 +03:00
ShootingStar91 32d7ef4534 Add faculty code as foreign key to sisstudyright 2024-07-18 15:10:50 +03:00
ShootingStar91 5bb39442b0 [Students] Fix student name disappearing 2024-07-18 15:01:46 +03:00
ShootingStar91 a8afade3b2 Type services/students.ts 2024-07-18 14:38:30 +03:00
Riku RauhalaandGitHub fb3acf1f34 Merge pull request #4575 from UniversityOfHelsinkiCS/OOD-34-fixes
[Updater] Added null check fallback to abbreviation in initGradeScaleIdToGradeIdsToGrades
2024-07-18 14:01:31 +03:00
Matti Räsänen 0a7db609d2 [Updater] Added null check fallback to abbreviation in initGradeScaleIdToGradeIdsToGrades 2024-07-18 13:56:22 +03:00
ShootingStar91 886f84d6df Update SortableTable docs & add link to root README 2024-07-18 13:44:53 +03:00
ShootingStar91 5e22c82ca0 Make SortableTable doc into markdown table 2024-07-18 13:40:14 +03:00
Riku RauhalaandGitHub 820c1d7a2e Merge pull request #4574 from UniversityOfHelsinkiCS/OOD-38
[Backend] Moved organisation related stuff to environment/organizationConfig.json file. Added conditional check to routes for users end point
2024-07-18 12:52:40 +03:00
Matti Räsänen 6a68350203 [Backend] Moved facultyCodes, magicFacultyCode, ignoredFacultyCodes to environment/organizationConfig.json file. Added resolveJsonModule to tsconfig 2024-07-18 12:05:47 +03:00
Matti Räsänen a71c6d51f1 [Backend] Moved facultyCodes, magicFacultyCode, ignoredFacultyCodes to environment/organizationConfig.json file. Edited usersFd route to have only fd related routes and added conditional check to routes to include this routes only when SERVICE_PROVIDER Fd. 2024-07-18 12:04:37 +03:00
Valtteri Kantanen bf93df66fe [Updater] Fix log message formatting in production 2024-07-18 11:47:45 +03:00
Valtteri Kantanen de62e7f528 [Backend] Improve and fix types 2024-07-18 10:42:45 +03:00
Riku Rauhala 854c8e5228 [Backend] Create type Language 2024-07-18 09:13:02 +03:00
Riku Rauhala 150d06e27b [Backend] Move Oodikone roles to config
Also form the Role type based on the list stored in config
2024-07-17 21:29:38 +03:00
Valtteri Kantanen 28ea80b1d0 Try uploading backend source maps into Sentry 2024-07-17 19:14:27 +03:00
Valtteri Kantanen 95a97f2c37 [Backend] Fix some types 2024-07-17 19:04:11 +03:00
Valtteri Kantanen ba35393c7d [Backend] Update TypeScript settings
Target was updated from ES6 to ES2021 as backend runs on Node 20. Also lib is specified to be ES2023 so that we are able to use methods like `Array.at()` or `Array.toSorted()` with types. Also strict is changed to true but all the individual rules that currently cause problems have been disabled.
2024-07-17 18:55:55 +03:00
Riku Rauhala c5c5889791 [Backend] Remove duplicated code 2024-07-17 18:31:16 +03:00
Riku Rauhala 75968b0b16 [Backend] Type courseFinders 2024-07-17 18:27:15 +03:00
Riku Rauhala 30b9c33f21 [Backend] Move conf-backend to config directory 2024-07-17 17:27:00 +03:00
Valtteri Kantanen fa9324431d [Study programme overview] Fix tests 2024-07-17 16:28:36 +03:00
Valtteri Kantanen e2dc26d4af [Backend] Change sentry.ts back to sentry.js 2024-07-17 15:51:43 +03:00
Valtteri Kantanen 12a405ed08 [Study programme overview] Update headers on study track stats table 2024-07-17 15:33:03 +03:00
Valtteri Kantanen dea2db5497 [Backend] Delete unused exports 2024-07-17 15:31:52 +03:00
Valtteri Kantanen 925e6646f0 [Backend] Add better types 2024-07-17 15:30:39 +03:00
Valtteri Kantanen 09889ba66f [Study programme overview] Use new models in study track stats 2024-07-17 15:25:07 +03:00
Riku Rauhala 1332a0ad29 [Backend] Type more utils 2024-07-17 15:21:35 +03:00
Riku Rauhala 281e142d5c [Backend] Type semester utils 2024-07-17 15:12:54 +03:00
Riku Rauhala ddac6ea36c [Backend] Improve util typing 2024-07-17 13:42:04 +03:00
Riku Rauhala 2aedf1efe8 [Backend] Type utils and remove duplicate code 2024-07-17 12:05:10 +03:00
Riku Rauhala 9edca0fc50 [Backend] Create a more accurate type for Role 2024-07-17 11:54:29 +03:00
Riku Rauhala 6bad2bc5dd [Backend] Revert changes to models 2024-07-16 21:57:50 +03:00
Riku Rauhala a1365406ae [Backend] Use explicit foreign key definitions for studyrights and organization 2024-07-16 21:47:43 +03:00
Riku Rauhala 8a66030f3b [Backend] Revert changes to models 2024-07-16 21:16:08 +03:00
Riku Rauhala ae82cfb333 [Backend] Import types from an index file 2024-07-16 20:59:45 +03:00
Riku Rauhala 4f9b87b591 [Backend] Use the correct StudyTrack type in SISStudyRightElement 2024-07-16 20:47:48 +03:00
Riku Rauhala 508d9d0479 [Backend] Explicitly define foreign keys for SISStudyRight and SISStudyRightElement 2024-07-16 20:37:06 +03:00
Riku Rauhala 3f83d6caaa Revert "[Backend] Explicitly define foreign keys"
This reverts commit 1071d085c7.
2024-07-16 19:20:32 +03:00
Riku Rauhala 1071d085c7 [Backend] Explicitly define foreign keys 2024-07-16 18:29:10 +03:00
Valtteri Kantanen e92726c06a Fix ESLint errors 2024-07-16 16:47:40 +03:00
Valtteri Kantanen e4831c16d8 Add ESLint settings for TypeScript files 2024-07-16 16:07:33 +03:00
Valtteri Kantanen e231a2046f [Updater] Remove colors from logs in production 2024-07-16 15:18:18 +03:00
Valtteri Kantanen 4ea39bd13b [Close to graduation] Handle the rare situation where there are no semester enrollments related to the selected study right 2024-07-16 15:11:01 +03:00
Valtteri Kantanen 9ed0576989 [Backend] Add a better type for semester enrollments 2024-07-16 12:49:15 +03:00
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
Riku Rauhala 0d385d003b [Navigation bar] Remove unnecessary className 2024-06-28 16:21:39 +03:00
Valtteri Kantanen 70308afa27 Give Sentry DSN as an env variable during build 2024-06-28 16:09:40 +03:00
arttu 1298b90604 Merge some more changes 2024-06-28 16:09:20 +03:00
Riku Rauhala 17c110cf28 Remove version field from docker-compoes files
The version field is obsolete and using it leads to console warnings
2024-06-28 16:02:12 +03:00
arttu ae273e846d Merge master to ts 2024-06-28 15:55:28 +03:00
Riku Rauhala 449c1932c6 Simplify docker-compose.test.yml 2024-06-28 15:50:35 +03:00
Valtteri Kantanen f8a1a547d7 [University view] Fix typo in programme filter 2024-06-28 15:12:01 +03:00
Riku Rauhala 61e487bd6b [Navigation bar] Attach a 'dev' label to the logo when in dev mode 2024-06-28 15:05:19 +03:00
Valtteri Kantanen 13535ba3f6 [Custom population] Fix tests 2024-06-28 14:59:12 +03:00
Valtteri Kantanen 317c4594c3 [Custom population] Allow commas, semicolons and spaces as separator for student numbers 2024-06-28 14:41:58 +03:00
Valtteri Kantanen 6064fe9514 [Custom population] Fix page crashing if no mathing study right is found 2024-06-28 10:57:20 +03:00
Valtteri Kantanen d49eb08bae [Study programme overview] Bring back new calculations that were deleted in aebfb0e 2024-06-28 10:42:07 +03:00
Riku Rauhala 880674b988 [Backend] Replace getAllProgrammes 2024-06-27 22:04:50 +03:00
Riku Rauhala 39a8f9eaee [Redux] Split useFilteredAndFormattedStudyProgrammes into smaller functions 2024-06-27 22:04:50 +03:00
Riku Rauhala 9c065e04fb [StudyProgramme] Replace ElementDetail with SISStudyRightElement 2024-06-27 22:04:50 +03:00
Riku Rauhala a563ee474f Replace ElementDetails 2024-06-27 22:04:50 +03:00
Riku Rauhala aebfb0e2c9 [Backend] Rename studyProgramme services 2024-06-27 22:04:50 +03:00
Valtteri Kantanen c036e3b626 [Study programme overview] Update tests 2024-06-27 15:20:52 +03:00
Valtteri Kantanen ef3b8c0baa [Study programme overview] Use new models to calculate 'Started studying' and 'Accepted' numbers 2024-06-27 14:56:48 +03:00
Valtteri Kantanen 57129b91fe [Study programme overview] Remove extent code filter from study right query 2024-06-27 14:55:15 +03:00
Valtteri Kantanen 451bc495ee Change Sentry DSN into an environment variable 2024-06-27 13:35:38 +03:00
Riku Rauhala 64f9e61431 [Code Climate] Allow higher method complexity
From what I've seen this setting is quite strict, let's allow a little bit more complexity
2024-06-27 09:37:29 +03:00
Riku Rauhala 4d4fa4d0e2 [Code Climate] Ignore more files 2024-06-27 09:28:14 +03:00
Riku Rauhala 8385077ab0 [Code Climate] Ignore more files 2024-06-27 09:23:49 +03:00
Riku Rauhala 74d2baa9f9 [Code Climate] Ignore more files 2024-06-27 09:17:25 +03:00
Riku Rauhala 2916b4153d Create Code Climate config 2024-06-27 09:09:18 +03:00
Riku Rauhala dd484d7207 [Backend] Refactor updateCreditCriteriaInfo 2024-06-26 19:38:32 +03:00
Riku Rauhala b523aab3b6 [Backend] Refactor populations functions 2024-06-26 16:10:12 +03:00
Valtteri Kantanen 2bb1af35d3 [Study programme overview] Use SISStudyRights to determine next programme after bachelor's programme 2024-06-26 15:55:43 +03:00
Riku RauhalaandGitHub f75fc169a7 Merge pull request #4556 from UniversityOfHelsinkiCS/university-view-generalization
[Backend] Make H50 configurable
2024-06-26 15:51:49 +03:00
Valtteri Kantanen 249fb6f20a [Students] Fix error caused by unavailable course data 2024-06-26 15:27:10 +03:00
Valtteri Kantanen 8bba2d8842 [Updater] Fix logic for end date of study right elements 2024-06-26 12:43:08 +03:00
Riku Rauhala a492b14a14 [Progress tab] Refactor to reduce copy-paste 2024-06-26 12:04:02 +03:00
Riku Rauhala 5010e65ea9 [Frontend] Remove duplicate date format constant 2024-06-26 12:04:02 +03:00
Riku Rauhala d216069df6 [Frontend] Move timeAndDate to util 2024-06-26 12:04:02 +03:00
Valtteri Kantanen 47bb1f5d61 [Study programme overview] Use SISStudyRights to determine previous programme before master's programme 2024-06-26 11:29:38 +03:00
Valtteri Kantanen 217bca5153 [Backend] Fix foreign keys used by models 2024-06-26 11:22:36 +03:00
EssKayz a03e535eb3 Python programmer making his snake into a camel 2024-06-26 10:58:32 +03:00
Valtteri KantanenandGitHub e5dd44b657 Merge pull request #4557 from UniversityOfHelsinkiCS/thesis-urns-expansion
[Backend] Add more thesis type urns
2024-06-26 10:33:30 +03:00
Valtteri KantanenandGitHub f45846b1a3 Merge branch 'master' into thesis-urns-expansion 2024-06-26 10:25:12 +03:00
Valtteri Kantanen b300a6a131 Make thesis type urns university-specific 2024-06-26 10:22:05 +03:00
Riku Rauhala 286c778756 Use named imports for lodash 2024-06-25 22:24:28 +03:00
Riku Rauhala 8f686200ca [Teachers] Fix crashing 2024-06-25 21:27:39 +03:00
Riku Rauhala 0e2c5462f6 Refactor mapToProviders 2024-06-25 20:36:36 +03:00
Riku Rauhala e86b327cfe [Backend] Rename snake_case files to camelCase 2024-06-25 16:34:54 +03:00
Riku Rauhala 6c964ce575 [Backend] Rename err variables
Let's use error for consistency with the rest of the code base
2024-06-25 16:34:54 +03:00
Riku Rauhala 7f555e72df [Backend] Refactor faculty 2024-06-25 16:34:54 +03:00
Riku Rauhala 7178c3ee0d [Backend] Refactor mapObject 2024-06-25 16:34:54 +03:00
Riku Rauhala 9406c6644d [Backend] Refactor mapToProviders 2024-06-25 16:34:53 +03:00
Riku Rauhala b70fe4f3bc [Backend] Organize utils 2024-06-25 16:34:53 +03:00
EssKayz eed77be183 [Backend] Add more thesis type urns 2024-06-25 15:44:42 +03:00
EssKayz afd38f4f91 [Backend] Make H50 configurable 2024-06-25 15:31:36 +03:00
Valtteri Kantanen 88d3536d6a [Study programme overview] Don't fetch the same student numbers again
No need to fetch them again as they've already been determined with `getCorrectStudentnumbers`. This also caused some students to be counted twice in the statistics (Studytracks and class statistics tab, column 'All')
2024-06-25 13:25:39 +03:00
Riku Rauhala 98b7ebe5d5 [Backend] Refactor openUniHelpers 2024-06-25 09:38:19 +03:00
Riku Rauhala c1eee9f175 [Open uni] Refactor one more time 2024-06-25 09:20:06 +03:00
Valtteri Kantanen 66ee9b8a3f [Students] Fix tests 2024-06-24 18:10:17 +03:00
Valtteri Kantanen e607d348e2 [Students] Add a test for Bachelor Honours visibility 2024-06-24 17:48:20 +03:00
Valtteri Kantanen 3079039d5d [Students] Fix Bachelor Honours not showing 2024-06-24 17:40:44 +03:00
Riku Rauhala 8d6a8be91f [Open uni] Refactor 2024-06-24 15:44:07 +03:00
Riku Rauhala 8a124cae9a [Open uni] Refactor filter logic 2024-06-24 15:44:07 +03:00
Riku Rauhala 74343af176 [Open uni] Big refactor 2024-06-24 15:44:07 +03:00
Riku Rauhala 5e7c8628c3 [Backend] Refactor 2024-06-24 15:44:07 +03:00
Valtteri Kantanen 462418c3eb [Course statistics] Big refactor 2024-06-24 14:28:24 +03:00
Riku Rauhala 8e2b7b2ce1 [Language center] Big refactor 2024-06-24 13:28:33 +03:00
Valtteri Kantanen 7b6c7aeecb [Course stats] Remove duplicated code 2024-06-24 13:10:58 +03:00
Valtteri Kantanen b1f34479eb Use SISStudyRight model to find students by programme code 2024-06-24 11:03:48 +03:00
Valtteri Kantanen 56e2462d30 Remove unused code 2024-06-24 10:52:02 +03:00
Valtteri Kantanen f7372e8125 Update axios from 0.27.2 to 0.28.1 2024-06-20 14:30:18 +03:00
Valtteri Kantanen a9f6cbe36e Change axios version from 0.28.0 to 0.27.2 2024-06-20 13:44:42 +03:00
Valtteri Kantanen 881e48f76c Update axios from 0.21.4 to 0.28.0 2024-06-20 13:27:05 +03:00
Valtteri KantanenandGitHub 0939e9129a Merge pull request #4553 from UniversityOfHelsinkiCS/fix-konedb-migrations
[Backend] Fix 20190821 kone-db migration
2024-06-20 12:39:26 +03:00
Valtteri KantanenandGitHub cb396623c6 Merge pull request #4552 from UniversityOfHelsinkiCS/fix-fix-migration
[Backend] Fix fix_faculty_code_constraint migration
2024-06-20 12:37:41 +03:00
Valtteri Kantanen 8716306083 Compress backend responses 2024-06-20 12:22:15 +03:00
Valtteri Kantanen 048ba916e4 Fix tests 2024-06-20 12:21:22 +03:00
Valtteri Kantanen c94549d0d3 [Students] Big refactor 2024-06-20 11:51:27 +03:00
Valtteri Kantanen 606ca3bfd5 [Updater] Remove manual fixing of graduations, they are already correct in snapshots 2024-06-20 11:36:26 +03:00
EssKayz 040a4de536 [Backend] Fix 20190821 kone-db migration 2024-06-20 10:55:37 +03:00
Riku Rauhala ec47d63c1b Extract time and date related common functions 2024-06-19 20:47:52 +03:00
Riku Rauhala bcad45a84b Use constants for ISO dates 2024-06-19 20:02:13 +03:00
EssKayz 91184ef9d6 [Backend] Fix fix_faculty_code_constraint migration 2024-06-19 16:08:50 +03:00
Riku Rauhala b946a9e87f [Backend] Refactor 2024-06-19 15:32:10 +03:00
Riku Rauhala cdde42e7d5 Use improved constants for dates 2024-06-19 14:55:53 +03:00
Riku Rauhala bcb115d648 Remove an unused function 2024-06-19 14:14:49 +03:00
Riku Rauhala d848030c6a [Class statistics] Remove combined Excel 2024-06-19 14:04:07 +03:00
Riku Rauhala c4cf9ba056 Rename variables 2024-06-19 11:43:06 +03:00
Riku Rauhala 93aabb97c5 [Courses tab] Include Hyv. grades in Excel 2024-06-19 10:39:11 +03:00
Riku RauhalaandGitHub 603774b287 Merge pull request #4550 from UniversityOfHelsinkiCS/facultycode-refactor
[Backend] Extract facultyCodes to separate file
2024-06-18 21:30:41 +03:00
Riku Rauhala d3e484100a [Courses tab] Fix study guidance groups population 2024-06-18 21:10:01 +03:00
Riku Rauhala 9f7f294fe1 [Backend] Remove filtering coursestatistics 2024-06-18 14:43:57 +03:00
EssKayz 2be9c3b47c [Backend] Extract facultyCodes to separate file 2024-06-18 14:21:52 +03:00
Riku Rauhala 79a43aae48 [Courses tab] Revert back to old logic for substitutions 2024-06-18 14:15:12 +03:00
Riku Rauhala 4859ab197a [Backend] Refactor allStudents 2024-06-18 14:15:12 +03:00
Riku Rauhala f155b13995 [Backend] Rename variables with typos 2024-06-18 14:15:12 +03:00
Riku Rauhala 76f2c2a276 Rename variables 2024-06-18 14:15:12 +03:00
Valtteri Kantanen e66aa244d6 Merge branch 'big-refactor-close-to-graduation' 2024-06-18 10:55:00 +03:00
Valtteri Kantanen cd5af9571a [Close to graduation] If a student has more then one primary study plan that qualifies, show them all 2024-06-18 10:54:11 +03:00
Riku Rauhala 9ca473ca8e [Documentation] Fix typos 2024-06-17 22:09:55 +03:00
Riku RauhalaandGitHub f9b9d7ed53 Update README.md
Add an emoji for tietosuoja too
2024-06-17 22:04:49 +03:00
Riku Rauhala a8ad765dc6 Refactor ProgressTab
Make it more readable and eliminate some copy-paste
2024-06-17 21:35:26 +03:00
Riku Rauhala 7cbc11a3e5 [Backend] Refactor bottlenecksOf.js 2024-06-17 20:14:52 +03:00
Valtteri Kantanen 2813afb625 [Students] Allow search terms that are at least three characters long 2024-06-17 14:44:42 +03:00
Valtteri Kantanen bbe1ee899a [Students] Remove unused functions 2024-06-17 14:44:42 +03:00
Valtteri Kantanen bda81c8b39 Add a separate job for updating close to graduation data 2024-06-17 14:44:42 +03:00
Valtteri Kantanen 7abd945c5d Remove functionality for updating students individually (#4489) 2024-06-17 14:44:42 +03:00
Riku Rauhala 5f332344d6 [Courses tab] Rename show substitutions to include substitutions 2024-06-17 14:04:22 +03:00
Riku Rauhala 832546cd73 [Courses tab] Refactor getPassedStudents
Combine the functions to reduce copy-paste
2024-06-17 13:59:19 +03:00
Riku Rauhala a23c638416 [Courses tab] Include completion date for courses with grades 2024-06-17 13:34:45 +03:00
Riku Rauhala e9bd6ceae9 [Courses tab] Refactor substitution passed logic 2024-06-17 12:39:52 +03:00
Valtteri Kantanen 40079bf963 [Completed courses] Add missing secondaryEmail field 2024-06-17 12:39:09 +03:00
Valtteri Kantanen 44ffcf878d [Updater] Fix some SISStudyRightElements not being parsed correctly 2024-06-17 09:55:23 +03:00
Riku Rauhala 29bd2c8ab5 [Courses tab] Refactor hasPassed 2024-06-14 17:01:13 +03:00
Valtteri Kantanen 3ca2d6934c [Teachers] Fix and improve tests 2024-06-14 13:46:44 +03:00
Riku Rauhala e75fc3c6ae [Courses tab] Check if subststitutions are shown 2024-06-14 13:39:11 +03:00
Valtteri Kantanen bce4e333c5 [Teachers] Try to make searches more efficient 2024-06-14 13:23:24 +03:00
Riku Rauhala bc951800d5 [Courses tab] Include substitutions in the summary row 2024-06-14 11:25:09 +03:00
Riku Rauhala 505ea3254c [Courses tab] Use the new logic for checking substitution course passed 2024-06-14 11:25:09 +03:00
Riku Rauhala b75bf7cbbf [Courses tab] Include substitutions in total passed 2024-06-14 11:25:09 +03:00
Riku Rauhala da6aeb2cb4 [Course tab] Refactor getPassedStudents 2024-06-14 11:25:09 +03:00
Riku Rauhala 3b33c95625 [Progress tab] Refactor 2024-06-14 11:25:08 +03:00
Riku Rauhala 5348e86bd2 Reduce copy-paste 2024-06-14 11:25:08 +03:00
Valtteri Kantanen 557663e5e1 [Backend] Remove unused code and move splitByEmptySpace function to utils 2024-06-13 17:05:32 +03:00
Valtteri Kantanen d4073a836c Update some programme codes 2024-06-13 15:27:42 +03:00
Riku Rauhala e8ad1bb08d [Population statistics] Update iinfobox texts 2024-06-13 09:58:11 +03:00
Riku Rauhala 75195de6cc [StudentInfoItem] Remove unused parameters
Parameters tab and view are no longer used
2024-06-12 15:23:04 +03:00
Riku Rauhala 3de924f6a6 [Courses tab] Rename mandatory courses
The courses are really not mandatory. Instead, the courses are part of the programme's curriculum
2024-06-12 15:18:25 +03:00
Valtteri Kantanen f9ab5f57fd [Close to graduation] Big refactor 2024-06-12 13:06:59 +03:00
Valtteri Kantanen 05f7204863 Add associations for SISStudyRight and SISStudyRightElement 2024-06-12 13:05:50 +03:00
Riku Rauhala 75b20c9750 [Courses tab] Enable the Show substitutions toggle 2024-06-12 12:51:28 +03:00
Valtteri Kantanen 346b4a29c7 Add sis_study_right_id to studyplan table 2024-06-12 12:00:14 +03:00
Valtteri Kantanen 7011849143 [Updater] Fix error caused by education not being found by id 2024-06-12 10:43:19 +03:00
Valtteri Kantanen 5ee2c3a9c9 [Study programme overview] Fix tests 2024-06-12 10:31:06 +03:00
Valtteri Kantanen 5b2649c1bf [Faculty statistics] Display graph and table side by side if they fit 2024-06-12 10:14:12 +03:00
Valtteri Kantanen 14c1de6a38 [Faculty statistics] Change 'special' to 'separate' so that credits show up correctly 2024-06-12 10:11:23 +03:00
Valtteri Kantanen 5a96a08e68 [Faculty statistics] Use Highcharts exports instead of own xlsx implementation 2024-06-11 20:07:53 +03:00
Riku Rauhala 4c700b7043 [Students table] Add the Combine substitutions toggle 2024-06-11 19:40:16 +03:00
Riku Rauhala a4e1afcc1c [Courses tab] Take the enrollments into account when sorting by a course 2024-06-11 19:40:16 +03:00
Riku Rauhala 4ade93b4d7 [Courses tab] Display gray checkmarks for substituted grades 2024-06-11 19:40:16 +03:00
Riku Rauhala ebd05a11be [Courses tab] Update tooltips 2024-06-11 19:40:16 +03:00
Riku Rauhala 86d2b85e21 [Courses tab] Refactor to make the code more readable 2024-06-11 19:40:16 +03:00
Riku Rauhala 15e940b77b Combine .gitignore 2024-06-11 19:40:16 +03:00
Riku Rauhala ad562b45b7 [Courses tab] Add icons for courses with enrollments 2024-06-11 19:40:16 +03:00
Valtteri Kantanen 390ff61162 Get rid of console-filling defaultProps warnings 2024-06-11 17:14:58 +03:00
Valtteri Kantanen ed44f617c0 [Faculty statistics] Fix tests 2024-06-11 16:16:24 +03:00
Valtteri Kantanen 9e990023b7 Add exporting and exportData modules for Highcharts 2024-06-11 15:38:50 +03:00
Valtteri Kantanen 7575dc85a3 Update braces from 3.0.2 to 3.0.3 2024-06-11 12:05:32 +03:00
Valtteri Kantanen c5694840b9 [Updater] Only create study right elements if there's a corresponding study right 2024-06-11 12:05:32 +03:00
Riku RauhalaandGitHub 81146f9866 Merge pull request #4528 from UniversityOfHelsinkiCS/OOD-11
OOD-11: admin can fetch users from Sisu and add to Oodikone user-db
2024-06-11 09:41:51 +03:00
Matti Räsänen cedad01fec OOD-11: Created own users route for fd and moved fd relevant routes there. Add serviceProvider check to routes module 2024-06-11 08:54:00 +03:00
Valtteri Kantanen 1ea162ef7e [Updater] Start updating SISStudyRights and SISStudyRightElements 2024-06-10 17:22:01 +03:00
Valtteri Kantanen 7e69fcc5ee [Updater] Improve logging 2024-06-10 17:22:01 +03:00
Valtteri Kantanen 58f69f50f7 [Updater] Rename studyTracks to studyTrack in SISStudyRightElements 2024-06-10 17:22:01 +03:00
Valtteri Kantanen 8d01562b2d [Updater] Remove facultyCode from SISStudyRightElement 2024-06-10 17:22:01 +03:00
Riku Rauhala 298f119ad3 [Students table] Refactor tabs 2024-06-10 13:03:37 +03:00
Valtteri Kantanen 87f5ffc353 [Updater] Add a new extent code 2024-06-10 11:48:03 +03:00
Jan Rundt 05d4927b3c OOD-11: auth on route and empty roles array 2024-06-10 11:22:17 +03:00
Valtteri Kantanen 9ab9f0ca29 [Updater] Add faculty code to SISStudyRight model 2024-06-10 10:55:31 +03:00
Valtteri Kantanen 6145a76425 [Updater] Extract university organisation id into one place 2024-06-10 10:47:20 +03:00
Riku RauhalaandGitHub 13c86b3259 Merge pull request #4542 from UniversityOfHelsinkiCS/renovate/uuid-10.x
Update dependency uuid to v10
2024-06-10 09:42:54 +03:00
renovate[bot]andGitHub 625ae0cfe3 Update dependency uuid to v10 2024-06-10 06:26:18 +00:00
Riku RauhalaandGitHub 62c8294791 Merge pull request #4537 from UniversityOfHelsinkiCS/renovate/nyc-17.x
Update dependency nyc to v17
2024-06-10 09:15:21 +03:00
renovate[bot]andGitHub d0b7cd8c6c Update dependency nyc to v17 2024-06-10 06:08:22 +00:00
Riku RauhalaandGitHub 65332f5fac Merge pull request #4522 from UniversityOfHelsinkiCS/renovate/highcharts-11.x
Update dependency highcharts to v11
2024-06-10 09:07:35 +03:00
Valtteri Kantanen a6040ceb25 Create new models for studyRight and studyRightElement 2024-06-07 12:15:27 +03:00
Valtteri Kantanen c9adca395c Replace deprecated husky command 2024-06-07 10:37:24 +03:00
Valtteri Kantanen d33ed0653e [Updater] Make handling of attainments clearer 2024-06-07 10:37:24 +03:00
Riku RauhalaandGitHub 251f22bf92 Merge pull request #4533 from UniversityOfHelsinkiCS/renovate/query-string-9.x
Update dependency query-string to v9
2024-06-07 09:29:37 +03:00
Riku RauhalaandGitHub b7cbac6390 Merge pull request #4534 from UniversityOfHelsinkiCS/renovate/major-remark
Update dependency react-markdown to v9
2024-06-07 08:17:06 +03:00
renovate[bot]andGitHub 0d2a568e8c Update dependency react-markdown to v9 2024-06-06 19:08:25 +00:00
Riku RauhalaandGitHub cfa86c5c11 Merge pull request #4521 from UniversityOfHelsinkiCS/renovate/eslint-plugin-cypress-3.x
Update dependency eslint-plugin-cypress to v3
2024-06-06 19:35:48 +03:00
renovate[bot]andGitHub 81d761202d Update dependency query-string to v9 2024-06-06 16:35:34 +00:00
Riku RauhalaandGitHub 4aec52e21b Merge pull request #4527 from UniversityOfHelsinkiCS/renovate/lint-staged-15.x
Update dependency lint-staged to v15
2024-06-06 19:33:35 +03:00
renovate[bot]andGitHub 3fcd366888 Update dependency lint-staged to v15 2024-06-06 16:26:02 +00:00
Riku RauhalaandGitHub fb66ade27b Merge pull request #4523 from UniversityOfHelsinkiCS/renovate/husky-9.x
Update dependency husky to v9
2024-06-06 19:22:20 +03:00
Jan Rundt 0337e86b34 OOD-11: fix to new user section 2024-06-06 16:58:14 +03:00
Jan Rundt 6f92fbdc9a OOD-11: some fixes to new user section 2024-06-06 16:56:43 +03:00
Riku Rauhala ccf4569463 Remove dependabot config 2024-06-05 15:33:45 +03:00
Valtteri Kantanen 1b870de079 Improve logging 2024-06-05 12:56:13 +03:00
Valtteri Kantanen 2f9264b0a0 Prefer regular JS instead of lodash and moment.js in a couple of tests 2024-06-05 12:53:53 +03:00
Jan Rundt 05f182274e OOD-11: no filter or sort for nothing 2024-06-05 10:14:26 +03:00
Jan Rundt a74e495ea5 OOD-11: no filter or sort 2024-06-05 10:09:29 +03:00
Riku Rauhala 9aafd50a66 Update README.md 2024-06-05 09:37:32 +03:00
Jan Rundt 884a4056a3 OOD-11: clean up 2024-06-05 09:25:17 +03:00
Jan Rundt aa46c759e8 OOD-11: using real isDefaultServiceProvider function 2024-06-05 09:24:19 +03:00
Jan Rundt 48001b1f30 Merge branch 'master' into OOD-11 2024-06-05 09:17:42 +03:00
Jan Rundt 882d3c5846 OOD-11: lint fix 2024-06-05 09:07:59 +03:00
Jan Rundt 55f669a86d OOD-11: code cleanup, mostly 2024-06-05 09:05:00 +03:00
Jan Rundt c021c31157 Merge branch 'OOD-11' of github.com:UniversityOfHelsinkiCS/oodikone into OOD-11 2024-06-05 08:57:35 +03:00
Riku RauhalaandGitHub 7af4cd4668 Merge pull request #4526 from UniversityOfHelsinkiCS/OOD-9-hide-features-from-ui-dependening-service-provider
OOD-9: hide features from UI dependening service provider
2024-06-05 08:55:52 +03:00
Jan RundtandGitHub 69918b1728 Merge branch 'master' into OOD-11 2024-06-05 08:52:20 +03:00
Jan Rundt 35c36cffc6 OOD-11: notification that user has been added 2024-06-05 08:47:17 +03:00
Jan Rundt 520436e32f OOD-11: added listening to fetching changes 2024-06-05 08:15:46 +03:00
Jan Rundt 6b187b8a60 OOD-11: new user section sync and fail logic 2024-06-04 23:30:28 +03:00
Riku Rauhala c8a93cbf57 Include JSX files in the coverage report 2024-06-04 21:06:41 +03:00
Jan Rundt b4da19e4e6 OOD-11: backend fix, front NewUserSection still not working correctly 2024-06-04 19:49:31 +03:00
Matti Räsänen 6132ea5f57 OOD-11: Changed useGetAllUsersQuery to useLazyGetAllUsersQuery. Moved UsersSearchList api call to parent component and passing users, isLoading and isError as props to child component 2024-06-04 16:08:59 +03:00
Matti Räsänen bebeae8fb6 OOD-11: Searching users from sisu and adding them to Users db 2024-06-04 15:23:51 +03:00
renovate[bot]andGitHub 933fbd1689 Update dependency husky to v9 2024-06-04 08:36:10 +00:00
renovate[bot]andGitHub c0165fb02d Update dependency eslint-plugin-cypress to v3 2024-06-04 08:35:57 +00:00
Riku Rauhala 3465051e6b Fix package.json 2024-06-04 11:30:04 +03:00
Riku Rauhala 09f78e4e38 Update the instrumenting logic 2024-06-04 11:25:27 +03:00
Riku Rauhala 795c00a258 Add initial setup for code coverage 2024-06-04 11:25:26 +03:00
Jan Rundt 9789693dce OOD-11: WIP frontend part 2024-06-04 10:36:41 +03:00
Matti Räsänen 93f95d040c OOD-9: Refactored defining columns 2024-06-04 09:00:02 +03:00
Matti Räsänen e1246113b3 Merge branch 'master' into OOD-9-hide-features-from-ui-dependening-service-provider 2024-06-04 08:43:48 +03:00
Riku RauhalaandGitHub f1a5627180 Merge pull request #4525 from UniversityOfHelsinkiCS/OOD-10
OOD-10: user access logic depends on service provider env variable
2024-06-04 08:04:35 +03:00
Riku RauhalaandGitHub 993d8fa7be Merge pull request #4514 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-06-03 17:54:00 +03:00
Jan Rundt 719d3ee2c6 OOD-11: oriProvider and endpoint for fetching person 2024-06-03 15:56:09 +03:00
Riku Rauhala 44f622f306 Move tietosuoja.md to docs folder 2024-06-03 15:30:58 +03:00
Riku Rauhala a1f8ed8554 Update documentation 2024-06-03 14:45:12 +03:00
Matti Räsänen ec57728e80 Merge branch 'OOD-10' into OOD-9-hide-features-from-ui-dependening-service-provider 2024-06-03 12:54:46 +03:00
Matti Räsänen 054ce51c5f OOD-9: Changed SERVICE_PROVIDER env variable value to Fd 2024-06-03 12:42:07 +03:00
Matti Räsänen ecf4933e79 Merge branch 'master' into OOD-10-hide-features-from-ui-dependening-service-provider 2024-06-03 12:40:23 +03:00
Tereshchenko Aleksandr 0d96f022d9 Only open renovate PRs for LTS Node versions 2024-06-03 12:11:03 +03:00
Jan Rundt 6044bc70cc OOD-10: eslint fixes 2024-06-03 10:25:35 +03:00
Jan RundtandGitHub af8cd065cc Merge branch 'master' into OOD-10 2024-06-03 10:04:17 +03:00
renovate[bot]andGitHub 67020d34ea Update all non-major dependencies 2024-06-01 18:06:04 +00:00
Riku Rauhala d6c1cb3f49 [Faculties] Basic information: Filter out commissioned programme students 2024-05-31 16:48:24 +03:00
Riku Rauhala 85c315ea6f Rename some kisakoodari variables 2024-05-31 16:48:24 +03:00
Riku Rauhala f0aaa6755c [Backend] Refactor faculty functions 2024-05-31 16:48:24 +03:00
Riku Rauhala 7010014c4e Rearrange some functions for consistency 2024-05-31 16:48:24 +03:00
Riku Rauhala 2c2fc92103 [Faculties] Simplify transferShortTitle logic 2024-05-31 16:48:24 +03:00
Riku Rauhala 37cf9d2591 Remove commented out console.log 2024-05-31 16:48:24 +03:00
Riku Rauhala 47c5ae215c [Faculties] Update message texts 2024-05-31 16:48:24 +03:00
Riku Rauhala 05db26dd93 Fix backend/updater import order
The rule previously did not properly take into account the hierarchy
2024-05-31 16:48:24 +03:00
Jan RundtandGitHub a765cf0a9f Merge branch 'master' into OOD-10 2024-05-31 14:09:38 +03:00
Matti Räsänen 609a7f0be1 Merge branch 'master' into OOD-9-hide-features-from-ui-dependening-service-provider 2024-05-31 08:48:46 +03:00
Matti Räsänen fab749e3bd OOD-9: Added env variable SERVICE_PROVIDER as const serviceProvider to conf. Created function which will check is serviceProvider default (Toska) and taken that into use in few components. 2024-05-31 08:25:51 +03:00
Jan Rundt d6031a84ae OOD-10: mocked user implementation 2024-05-30 23:45:17 +03:00
renovate[bot]andGitHub aaeb577ffd Update dependency highcharts to v11 2024-05-30 15:01:07 +00:00
Jan Rundt bb1b1e6673 OOD-10: user access logic depending on service provider 2024-05-30 17:57:53 +03:00
Valtteri Kantanen 9433eb2abb Fix fetching students in user's study guidance groups 2024-05-30 15:33:10 +03:00
Valtteri KantanenandGitHub fa4b78720d Merge pull request #4516 from UniversityOfHelsinkiCS/OOD-2-fd-local-dev-enviroment
Created own docker compose file for funidata to run local dev enviroment
2024-05-30 14:45:02 +03:00
Matti Räsänen 1a833a627b OOD-2: Created own docker compose file for funidata to run local dev enviroment 2024-05-30 14:37:13 +03:00
Valtteri Kantanen 2b8ded490d Update Node.js to version 20 in all Dockerfiles 2024-05-30 14:06:29 +03:00
Valtteri KantanenandGitHub 10d1fefc07 Merge pull request #4513 from UniversityOfHelsinkiCS/renovate/node-20.x
Update Node.js to v20
2024-05-30 14:04:06 +03:00
renovate[bot]andGitHub fc0e4b51d6 Update Node.js to v20 2024-05-30 10:48:42 +00:00
Valtteri KantanenandGitHub 8eb75e476d Merge pull request #4511 from UniversityOfHelsinkiCS/renovate/npm-sequelize-vulnerability
Update dependency sequelize to v6 [SECURITY]
2024-05-30 13:23:50 +03:00
renovate[bot]andGitHub 4e3119dd9d Update dependency sequelize to v6 [SECURITY] 2024-05-30 10:09:31 +00:00
Tereshchenko Aleksandr aff75fbd7d Add renovate.json 2024-05-30 13:08:03 +03:00
Valtteri Kantanen f568255119 Revert "Use programmes instead of element details"
This reverts commit a5f7cd297c.
2024-05-30 12:37:38 +03:00
Valtteri Kantanen 283f390e70 Revert "[Faculties] Remove hard-coded faculty codes"
This reverts commit 84d331322a.
2024-05-30 11:43:01 +03:00
Valtteri Kantanen 03f838c5d2 [Study programme overview] Fix tests 2024-05-30 11:42:12 +03:00
Valtteri Kantanen 2bb8dfdde2 Fix ESLint error 2024-05-30 10:43:43 +03:00
Valtteri Kantanen 2d5be46161 [Class statistics] Update infotexts and simplify QueryCard 2024-05-30 10:39:10 +03:00
Valtteri Kantanen 396584b5d7 [Filters] Simplify styles on filter cards 2024-05-30 10:39:10 +03:00
Valtteri Kantanen 0f5bfd3466 [Filters] Make tag filter work with both include and exclude 2024-05-30 10:39:10 +03:00
Valtteri Kantanen c963261ee3 [Class statistics] Fix credits earned filter 2024-05-30 10:39:10 +03:00
Valtteri Kantanen 2a37467d0c [Class statistics] Only include tags from programme being observed 2024-05-30 10:39:10 +03:00
Valtteri Kantanen b20abbbf93 [Class statistics] Fix tag not being taken into account 2024-05-30 10:39:10 +03:00
Valtteri Kantanen a30b0655b9 [Class statistics] Show user if the deletion or creation of the tag was successful 2024-05-30 10:39:10 +03:00
Valtteri Kantanen 84d331322a [Faculties] Remove hard-coded faculty codes 2024-05-30 10:39:10 +03:00
Valtteri Kantanen 28c360d708 [Backend] Specify shared parts of API routes in routes.js 2024-05-30 10:39:10 +03:00
Valtteri Kantanen a5f7cd297c Use programmes instead of element details 2024-05-30 10:39:10 +03:00
Riku Rauhala 19906c5725 [Faculties] Write tests for file downloads 2024-05-30 09:12:56 +03:00
Riku Rauhala b948b4183a Ignore Cypress output directories 2024-05-30 08:43:41 +03:00
Riku Rauhala 5ffce68742 [Faculties] Basic information: Fix credits produced Excel export 2024-05-29 22:09:30 +03:00
Riku Rauhala 6a2bf5c108 [Faculties] Rename y to median 2024-05-29 15:49:57 +03:00
Riku Rauhala 83ef6f5760 [Faculties] Graduation times: Include median study times in Excel exports 2024-05-28 18:08:44 +03:00
Valtteri Kantanen 59de371d9a [Guidance groups] Add info about the Close to graduation feature 2024-05-28 17:42:37 +03:00
Valtteri Kantanen 362250868d [Class statistics] Remove code that is no longer used 2024-05-28 17:41:40 +03:00
Valtteri Kantanen 846d4b9ec7 [Sortable table] Remove check for headerRowData type: it's no longer a function 2024-05-28 17:39:34 +03:00
Valtteri Kantanen 6d8f4e0992 Remove unused CSS files 2024-05-28 17:38:03 +03:00
Valtteri Kantanen b41bfbd47e [Close to graduation] Allow user see students in their study guidance groups (#4503) 2024-05-28 17:22:32 +03:00
Riku Rauhala d774d5f073 [Faculties] Graduation times: set the year label based on grouping 2024-05-28 15:28:56 +03:00
Valtteri Kantanen 6259cfface [Class statistics] Fix automatic curriculum selection 2024-05-28 14:17:17 +03:00
Riku Rauhala 7cd101bb57 [Faculties] Add support for exporting in the Graduation Times tab 2024-05-28 14:01:59 +03:00
Riku Rauhala 329bd4696a [Updater] Sort model imports 2024-05-28 14:01:19 +03:00
Valtteri Kantanen 750a12e14b Remove unused backend route 2024-05-28 13:26:51 +03:00
Valtteri Kantanen f46a8fee5b [Teachers] Add provider code as a description 2024-05-28 13:25:55 +03:00
Valtteri Kantanen 0d8f51bc4f [Course statistics] Fix redux error and simplify the component 2024-05-28 13:25:34 +03:00
Valtteri Kantanen d3c01133b7 [Class statistics] Obfuscate some new fields 2024-05-28 13:24:41 +03:00
Valtteri Kantanen abb7d2211f [Class statistics] Allow to get course information if the student numbers are encrypted 2024-05-28 13:23:52 +03:00
Valtteri Kantanen dd7088c332 Add userId back 2024-05-27 14:44:44 +03:00
Riku RauhalaandGitHub 27bbfc6519 Merge pull request #4501 from UniversityOfHelsinkiCS/backend-imports
[Backend, Updater] Update and enforce a consistent import order
2024-05-27 11:00:45 +03:00
Riku Rauhala c1433392d4 [Backend, Updater] Update and enforce a consistent import order 2024-05-27 10:29:54 +03:00
Riku Rauhala f7131d361c [Class statistics] Fix broken access rights 2024-05-24 15:39:37 +03:00
Riku Rauhala 5840889fa9 Rename functions for consistency 2024-05-24 15:39:37 +03:00
Valtteri Kantanen 887b57fca7 Fix setting language 2024-05-24 15:21:43 +03:00
Valtteri Kantanen c496912d88 [Users] Remove sessions table from user-db 2024-05-24 14:53:17 +03:00
Valtteri Kantanen 442a58c74a [Users] Remove unnecessary tables and columns from user-db 2024-05-24 14:13:13 +03:00
Valtteri Kantanen ac1632c1e1 [Close to graduation] Add master's students 2024-05-24 11:49:07 +03:00
Valtteri Kantanen 25dc072b2e [Users] Take userService changes into account in the frontend 2024-05-24 10:01:04 +03:00
Valtteri Kantanen 46b575ae6d Refactor userService completely 2024-05-24 09:59:16 +03:00
Valtteri Kantanen 24fafbe4ff Upgrade lru-cache version 2024-05-24 09:58:13 +03:00
Valtteri Kantanen 18f6d6157c Add roles and programmeRights to users table 2024-05-24 09:57:54 +03:00
Riku Rauhala 3c27f19ea1 Format curriculum periods in the frontend 2024-05-23 17:04:34 +03:00
Riku Rauhala ad7c36f235 [Users] Update email button text 2024-05-23 14:51:39 +03:00
Riku Rauhala 6caebdab25 [Class statistics] Fix curriculum year ranges 2024-05-22 20:23:16 +03:00
Riku Rauhala 58423df517 Refactor CourseTab
Rename variables and improve readability
2024-05-22 15:52:29 +03:00
Riku Rauhala a547382f1c Use consistent spelling for email
All other instances use the spelling email instead of e-mail
2024-05-22 09:40:03 +03:00
Valtteri Kantanen bf178fb086 [Class statistics] Fix small percentages not showing in credit gain stats 2024-05-21 18:33:57 +03:00
Valtteri Kantanen fc023f09cb [Class statistics] Add median to credit gain stats 2024-05-21 18:32:28 +03:00
Valtteri Kantanen 3fe9683c98 [Class statistics] Fix filtering by admission type 2024-05-21 18:31:28 +03:00
Riku Rauhala 8d20225a57 [Class statistics] Update pagination icons 2024-05-21 17:27:14 +03:00
Riku Rauhala b83c90dca1 [Class statistics] Fix ghost border 2024-05-21 11:49:39 +03:00
Riku Rauhala 47de8e89c3 Add a default value for the search history header 2024-05-21 09:14:05 +03:00
Riku Rauhala 64606c2a98 Refactor search history 2024-05-20 20:00:51 +03:00
Valtteri Kantanen b5e913d85e Fix tests 2024-05-20 19:50:43 +03:00
Valtteri Kantanen 8e5dcb3e78 Add retries to flaky filters tests 2024-05-20 19:39:26 +03:00
Valtteri Kantanen ebd03acff0 [Courses tab] Fix table flowing over its borders 2024-05-20 19:35:56 +03:00
Valtteri Kantanen 0fe06bfd12 [Course statistics] Exclude selected primary groups from comparison group options 2024-05-20 19:15:49 +03:00
Valtteri Kantanen 3f564ab66a [Course statistics] Fix filtering statistics by study programme 2024-05-20 19:15:49 +03:00
Valtteri Kantanen 9e6349739a [Course population] Fix visibility for users with full Sisu access 2024-05-20 19:15:49 +03:00
Valtteri Kantanen d1936c6358 [Close to graduation] Only show current study tracks 2024-05-20 19:15:49 +03:00
Riku Rauhala a075df412c Update some titles for consistency 2024-05-20 17:51:59 +03:00
Valtteri Kantanen b53d84f706 [Updater] Increase Sequelize acquire timeout closer to the one in the backend 2024-05-20 10:52:12 +03:00
Valtteri Kantanen 0312174e40 Add a button for refreshing the close to graduation data 2024-05-20 10:51:14 +03:00
Valtteri Kantanen 442ced9124 Add logging to see how long a big db query takes to finish 2024-05-17 15:06:20 +03:00
Valtteri Kantanen ea9345d82c [Students] Fix tests 2024-05-17 10:15:11 +03:00
Valtteri Kantanen 132bf930fb Upgrade React 16 to React 18 2024-05-17 10:04:12 +03:00
Valtteri Kantanen 980fbef339 [Students] Fix calculation of average grades 2024-05-16 23:15:56 +03:00
Valtteri Kantanen c68e9abfd4 [General tab] Fix page crashing if no correct studyright is found 2024-05-16 17:58:50 +03:00
Valtteri Kantanen 530435a069 [Class statistics] Fix filtering with credit distribution development 2024-05-16 17:09:32 +03:00
Valtteri Kantanen 8cbd4fd957 [Study guidance groups] Fix tests 2024-05-16 17:01:50 +03:00
Valtteri Kantanen 3152e5a76d [Close to graduation] Fix column resizing over and over again 2024-05-16 16:39:58 +03:00
647 changed files with 23817 additions and 15242 deletions
+82
View File
@@ -0,0 +1,82 @@
version: '2'
checks:
argument-count:
enabled: true
config:
threshold: 4
complex-logic:
enabled: true
config:
threshold: 4
file-lines:
enabled: true
config:
threshold: 250
method-complexity:
enabled: true
config:
threshold: 10
method-count:
enabled: true
config:
threshold: 20
method-lines:
enabled: true
config:
threshold: 25
nested-control-flow:
enabled: true
config:
threshold: 4
return-statements:
enabled: false
similar-code:
enabled: true
config:
threshold: # Language-specific defaults. Overrides affect all languages.
identical-code:
enabled: true
config:
threshold: # Language-specific defaults. Overrides affect all languages.
exclude_patterns:
- '.github/'
- '.husky/'
- '.vscode/'
- 'adminer-plugins/'
- 'anonyymioodi/'
- 'cypress/'
- 'documentation/'
- 'scripts/'
- 'services/backend/src/database/migrations_kone/'
- 'services/backend/src/database/migrations_user/'
- 'services/backend/src/models/'
- 'updater/sis-updater-worker/src/db/migrations/'
- 'updater/sis-updater-worker/src/db/models/'
- '.codeclimate.yml'
- '.env.template'
- '.lintstagedrc.js'
- '.nycrc'
- '.prettierrc'
- '.stylelintrc'
- 'cypress.config.js'
- 'LICENSE'
- 'nginx.conf'
- 'nyc-config.js'
- '**/.dockerignore'
- '**/.eslintrc.json'
- '**/.gitignore'
- '**/dev.Dockerfile'
- '**/Dockerfile'
- '**/*.css'
- '**/*.csv'
- '**/*.html'
- '**/*.json'
- '**/*.md'
- '**/*.png'
- '**/*.sh'
- '**/*.sql'
- '**/*.svg'
- '**/*.txt'
- '**/*.yml'
+66 -2
View File
@@ -15,13 +15,70 @@
"plugin:import/recommended",
"plugin:prettier/recommended"
],
"overrides": [
{
"files": ["**/*.ts"],
"env": {
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"airbnb-typescript",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2021,
"sourceType": "module",
"project": true
},
"rules": {
"import/prefer-default-export": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/return-await": "off",
"@typescript-eslint/no-shadow": "off"
}
}
],
"rules": {
"camelcase": "off",
"consistent-return": "off",
"cypress/no-assigning-return-values": "off",
"cypress/unsafe-to-chain-command": "off",
"default-param-last": "off",
"import/order": "off",
"import/extensions": ["error", "never"],
"import/order": [
"error",
{
"alphabetize": { "order": "asc", "caseInsensitive": true },
"groups": [["builtin", "external"], ["internal"], ["parent"], ["sibling", "index"]],
"pathGroups": [
{
"pattern": "../../../**",
"group": "internal",
"position": "before"
},
{
"pattern": "../../**",
"group": "internal",
"position": "after"
},
{
"pattern": "../**",
"group": "parent",
"position": "before"
},
{
"pattern": "./**",
"group": "sibling",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": ["builtin", "external"]
}
],
"no-async-promise-executor": "off",
"no-await-in-loop": "off",
"no-console": "error",
@@ -43,5 +100,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*/**"]
}
-78
View File
@@ -1,78 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
schedule:
interval: weekly
time: '03:00'
labels:
- 'dependencies'
- 'infra'
allow:
- dependency-type: 'production'
ignore:
- dependency-name: '*'
update-types: ['version-update:semver-major']
- package-ecosystem: npm
directory: '/updater/sis-updater-scheduler'
schedule:
interval: weekly
time: '03:00'
labels:
- 'dependencies'
- 'updater'
allow:
- dependency-type: 'production'
ignore:
- dependency-name: '*'
update-types: ['version-update:semver-major']
- package-ecosystem: npm
directory: '/updater/sis-updater-worker'
schedule:
interval: weekly
time: '03:00'
labels:
- 'dependencies'
- 'updater'
allow:
- dependency-type: 'production'
ignore:
- dependency-name: '*'
update-types: ['version-update:semver-major']
- package-ecosystem: npm
directory: '/services/backend'
schedule:
interval: weekly
time: '03:00'
labels:
- 'dependencies'
- 'back'
allow:
- dependency-type: 'production'
ignore:
- dependency-name: '*'
update-types: ['version-update:semver-major']
- package-ecosystem: npm
directory: '/services/frontend'
schedule:
interval: weekly
time: '03:00'
labels:
- 'dependencies'
- 'front'
allow:
- dependency-type: 'production'
ignore:
- dependency-name: '*'
update-types: ['version-update:semver-major']
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: weekly
time: '03:00'
open-pull-requests-limit: 10
+11 -3
View File
@@ -33,6 +33,7 @@ jobs:
BUILT_AT=${{ steps.get_current_time.outputs.NOW }}
SENTRY_RELEASE=${{ steps.extract_version.outputs.VERSION }}
SENTRY_ENVIRONMENT=production
SENTRY_DSN=${{ secrets.OODIKONE_SENTRY_DSN }}
- name: Push to quay.io
uses: redhat-actions/push-to-registry@v2
@@ -81,6 +82,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- release_frontend
- release_backend
steps:
- uses: actions/checkout@v4
@@ -89,12 +91,18 @@ jobs:
id: extract_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Pull image and create a container
- name: Pull frontend image and create a container
run: docker create --name frontend quay.io/toska/oodikone-frontend:production
- name: Copy assets from the container
- name: Pull backend image and create a container
run: docker create --name backend quay.io/toska/oodikone-backend:production
- name: Copy assets from the frontend container
run: docker cp frontend:/opt/app-root/src/build ./build
- name: Copy assets from the backend container
run: docker cp backend:/opt/app-root/src/dist ./dist
- name: Create Sentry release
uses: getsentry/action-release@v1
env:
@@ -105,7 +113,7 @@ jobs:
with:
environment: production
set_commits: 'skip'
sourcemaps: './build/assets'
sourcemaps: './build/assets ./dist'
url_prefix: '~/assets'
version: ${{ steps.extract_version.outputs.VERSION }}
+1
View File
@@ -4,6 +4,7 @@ on:
push:
branches:
- master
- tsMerge
paths:
- '.github/workflows/test.yaml'
- '.github/workflows/staging.yaml'
+2 -2
View File
@@ -24,8 +24,8 @@ jobs:
run: npm ci && cd services/frontend && npm ci && cd ../backend && npm ci
- name: 'Run Stylelint for CSS files'
run: npm run stylelint "services/frontend/src/**/*.css"
- name: 'Run ESLint for JS files'
run: npm run eslint -- --ext .jsx,.js services --report-unused-disable-directives
- name: 'Run ESLint for JS and TS files'
run: npm run eslint -- --ext .js,.jsx,.ts,.tsx services --report-unused-disable-directives
# === Build database images ===
build_backend:
+24 -9
View File
@@ -16,16 +16,12 @@ pids
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# Production
dist/
# Dependency directories
node_modules/
jspm_packages/
node_modules/
# Optional npm cache directory
.npm
@@ -39,10 +35,29 @@ jspm_packages/
# Output of 'npm pack'
*.tgz
# dotenv environment variables file
# Misc
*.sqz
# Environment variable files
.env
.env.test
.env.production
# Oodikone related stuff
.databasedumps
.databasedumps/
# Cypress
cypress/downloads/
cypress/screenshots/
cypress/videos/
# Code coverage
.nyc_output/
coverage/
instrumented/
# pycharm
.idea
# fd specific
environments/
-1
View File
@@ -1 +0,0 @@
_
+3 -2
View File
@@ -4,8 +4,9 @@ const dockerCmdBase = `docker run --rm --volume ${cwd}:/oodikone --workdir /oodi
const relativeFilePaths = files => [...files.map(file => path.relative(cwd, file))].join(' ')
module.exports = {
'{services,updater}/**/*.{js,jsx}': files => `eslint --fix ${files.join(' ')} --report-unused-disable-directives`,
'*.{js,json,md,yml,yaml,html,css}': files => `prettier --write ${files.join(' ')}`,
'{services,updater}/**/*.{js,jsx,ts,tsx}': files =>
`eslint --fix ${files.join(' ')} --report-unused-disable-directives`,
'*.{js,jsx,ts,tsx,json,md,yml,yaml,html,css}': files => `prettier --write ${files.join(' ')}`,
'*.css': files => `stylelint --fix ${files.join(' ')}`,
Dockerfile: files =>
`${dockerCmdBase} hadolint/hadolint:v2.12.0 hadolint --ignore DL3006 ${relativeFilePaths(files)}`,
+5
View File
@@ -0,0 +1,5 @@
{
"all": true,
"include": ["services/frontend/src/**/*.js", "services/frontend/src/**/*.jsx"],
"extends": "./nyc-config.js"
}
+53 -46
View File
@@ -6,21 +6,23 @@ An application for analyzing university data, running at [https://oodikone.helsi
To run Oodikone locally, you will need the following:
1. Applications:
- [Docker](https://docs.docker.com/install/) (version 20.10+),
- [Docker Compose](https://docs.docker.com/compose/install/) (version 1.29+) and
- [npm](https://docs.npmjs.com/cli/v7) (version 7+)
2. Access to Toska Docker Hub,
3. For real data setup, access to Toskas production servers.
4. For some features to work, access to Toskas service tokens
1. Applications
- [Docker](https://docs.docker.com/install/) (version `20.10+`),
- [Docker Compose](https://docs.docker.com/compose/install/) (version `1.29+`)
- [npm](https://docs.npmjs.com/cli/v7) (version `7+`)
2. Access to Toska Docker Hub
3. For real data setup, access to Toska's production servers
4. For some features to work, access to Toska's service tokens
## 🚀 Installation
Pre-installation steps:
- Clone this repo
- Login to toskas docker hub (see Toskas Gitlab for more information)
- Populate `.env.template` file with correct tokens and rename it to `.env`
- `git@github.com:UniversityOfHelsinkiCS/oodikone.git`
- Login to Toska's Docker Hub (see Toska's GitLab for more information)
- Copy the `.env.template` file to `.env` and populate with correct tokens
- `cp .env.template .env`
Then launch the CLI and follow the instructions:
@@ -32,23 +34,26 @@ _Please use a terminal at least 80 characters wide, the CLI is a bit rudimentary
What different CLI options do:
1. Set up oodikone from scratch:
1. Set up Oodikone from scratch
- Cleans up any previous installations
- Installs all needed npm packages locally
- Sets up pre-commit linting hooks.
- Pulls and builds all needed Docker images and sets up dockerized development environment.
- Setup happens with anonymized data, which developers should use by default.
- **Note:** Running this option cleans up all real data too, so please don't run option 2 before this.
2. Reset all real data:
- Sets up pre-commit linting hooks
- Pulls and builds all needed Docker images and sets up dockerized development environment
- Setup happens with anonymized data, which developers should use by default
- **Note:** Running this option cleans up all real data too, so please don' run option 2 before this.
2. Reset all real data
- Cleans up any previous real data databases
- Downloads needed database dumps from production servers.
- Creates real data databases and populates them with downloaded dumps.
3. Reset single database
- Like option 2, but only for single database. Leaves other databases untouched.
3. Reset a single database
- Like option 2, but only for single database
- Leaves other databases untouched
4. Restore data from dumps
- Like option 2, but skips downloading, and simply restores databases from the dumps downloaded earlier.
- Like option 2, but skips downloading, and simply restores databases from the dumps downloaded earlier
5. Docker system prune
- Runs `docker system prune -a && docker volume prune -a`. This will remove all your docker data, also related to any other applications than oodikone! It is sometimes necessary after strange errors due to caching.
- Runs `docker system prune -a && docker volume prune -a`
- This will remove all your Docker data, also related to any other applications than Oodikone!
- Sometimes necessary after strange errors due to caching
## ⌨️ Development
@@ -94,9 +99,9 @@ graph TB
end
```
In development, backend and updater use the same redis, but in production they are separate.
In development, backend and updater use the same Redis, but in production they are separate.
See also [older graph](documentation/Oodikone.png), which presents more information.
See also the [older graph](documentation/img/oodikone.png) for more information.
To read a bit more details about what updater-scheduler and updater-worker do, see their own READMEs:
@@ -105,53 +110,57 @@ To read a bit more details about what updater-scheduler and updater-worker do, s
### Documentation
Folder `documentation` currently contains some notes on sis-db schema and is meant as a place for storing useful information on Oodikone to make life easier for future developers. Feel free to add anything relevant!
The `documentation` directory contains some notes on the sis-db schema and is meant as a place for storing useful information on Oodikone to make life easier for future developers. Feel free to add anything relevant!
For internal meeting notes and sensitive information, see Toska GitLab.
[SortableTable documentation](./services/frontend/src/components/SortableTable/README.md)
### Basics
The development environment is entirely configured in the `docker-compose.yml` file located in this repository. The file defines the services for oodikone's two main components: **oodikone** and **updater**.
The development environment is entirely configured in the `docker-compose.yml` file located in this repository. The file defines the services for Oodikone's two main components: **oodikone** and **updater**.
Running oodikone with real data requires separated databases and redis, which are defined in `docker-compose.real.yml` file. Otherwise real data development environment uses the configuration as anonymized data development environment.
Running Oodikone with real data requires separated databases and redis, which are defined in `docker-compose.real.yml` file. Otherwise real data development environment uses the configuration as anonymized data development environment.
Some useful commands are defined in `package.json` and can be run with `npm run <command>` as follows:
- `npm run oodikone`: starts oodikone with anonymized data
- `npm run oodikone:real`: starts oodikone with real data
- `npm run oodikone`: starts Oodikone with anonymized data
- `npm run oodikone:real`: starts Oodikone with real data
- `npm run updater`: starts updater with anonymized data
- `npm run updater:real`: starts oodikone with real data
- `npm run both`: starts oodikone and updater with anonymized data
- `npm run both:real`: starts oodikone and updater with real data
- `npm run updater:real`: starts updater with real data
- `npm run both`: starts Oodikone and updater with anonymized data
- `npm run both:real`: starts Oodikone and updater with real data
- `npm run docker:down`: stops the whole environment
- `npm run testupdater`: runs tests for updater
- `npm run flushredis`: clears redis, which forces big calculations to be redone in e.g. studyprogramme overview and faculty views.
- `npm run flushredis`: clears Redis, which forces big calculations to be redone in e.g. studyprogramme overview and faculty views
- `npm run rapodiff`: runs [Rapodiff](./services/backend/src/rapodiff/)
- `npm run install:local`: installs `node_modules` for each package (backend, frontend, sis-updater-scheduler and sis-updater-worker) locally
- `npm run install:docker`: installs `node_modules` for each package inside containers
- `npm run install:both`: runs both `npm run install:local` and `npm run install:docker`
Once you have ran setup for oodikone , you can just execute the first one (`npm run oodikone`). After starting and waiting for a while for containers to compile, oodikone can be accessed at [http://localhost:3000/](http://localhost:3000/) and Adminer (database investigation tool) at [http://localhost:5050/](http://localhost:5050/). Adminer requires you to login with username `postgres` and with any password you choose (for example `p`).
Once you have ran setup for Oodikone , you can just execute the first one (`npm run oodikone`). After starting and waiting for a while for containers to compile, Oodikone can be accessed at [http://localhost:3000](http://localhost:3000/) and Adminer (database investigation tool) at [http://localhost:5050](http://localhost:5050/). Adminer requires you to login with username `postgres` and with any password you choose (for example `p`).
### Run.sh script
As said, the development environment runs entirely inside docker containers. To keep `package.json` clean and not filled with predefined scripts, we have created a simple helper script called `./run.sh`. The script allows you to use docker-compose commands without the need to write long list of parameters. Try to run `./run.sh` in the root of the project and see what happens!
As said, the development environment runs entirely inside Docker containers. To keep `package.json` clean and not filled with predefined scripts, we have created a simple helper script called `./run.sh`. The script allows you to use docker-compose commands without the need to write long list of parameters. Try to run `./run.sh` in the root of the project and see what happens!
`./run.sh` is simply a wrapper script to run oodikone, updater or both services in either anon or real mode. If you take a look at `package.json`, you can see that most of the predefined scripts above use `./run.sh` under the hood.
`./run.sh` is simply a wrapper script to run Oodikone, updater or both services in either anon or real mode. If you take a look at `package.json`, you can see that most of the predefined scripts above use `./run.sh` under the hood.
It is recommended to spend some time to become familiar with `docker` and `docker compose` cli commands. You can then use them directly or with `./run.sh` wrapper. Here is some examples for day-to-day development situations:
- `./run.sh oodikone anon pull`: Pull all images related to oodikone development
- `./run.sh updater real up --build --force-recreate --detach`: Start updater detached (=in the background) in real data mode, but build new images before starting
- `./run.sh oodikone anon pull`: pull all images related to Oodikone development
- `./run.sh updater real up --build --force-recreate --detach`: start updater detached (in the background) in real data mode, but build new images before starting
- `docker compose ps`: view the containers in the running environment
- `docker compose logs frontend`: print logs for just frontend
- `docker compose logs --follow --tail 100 backend`: print last hundred rows of backend logs and begin to follow them in your terminal window
- `docker exec -it backend sh`: open bash terminal inside backend container
- `docker exec -it sis-db psql -U postgres sis-db-real`: open psql client to investigate sis real data database.
- `docker exec -it sis-db psql -U postgres sis-db-real`: open psql client to investigate sis real data database
### User types for development
By default, you're login as `mluukkai` dev user when running oodikone in development mode. If you want to debug with certain type of user, you can use mocking: go to "Users" -page, click on "edit" on user you want to use and then click icon on the right corner of user card. Another way is to mock user headers - see frontend's api configuration for how this is done.
By default, you're login as `mluukkai` dev user when running Oodikone in development mode. If you want to debug with certain type of user, you can use mocking: go to the "Users" page, click "edit" on the user you want to use and then click the icon on the right corner of user card. Another way is to mock user headers - see frontend's API configuration for how this is done.
Anon data contains some preset user types, corresponding to most usual user types in real data. These are used in testing too (see "Testing" below for more info).
Anonymous data contains some preset user types, corresponding to most usual user types in real data. These are used in testing too (see "Testing" below for more info).
### Linting
@@ -177,8 +186,8 @@ Continuous integration (CI) works with Github actions and is defined in workflow
- Oodikone setup for cypress and other tests in CI is defined in `docker-compose.ci.yml`. Take a look at this too if debugging github action workflows.
- Tests are run on every push
- After a successful test run, oodikone is deployed to staging
- After creating a release, oodikone is deployed to production
- After a successful test run, Oodikone is deployed to staging
- After creating a release, Oodikone is deployed to production
- Updater is deployed to production when pushed if tests pass
- Test databases can be updated, instructions in [anonyymioodi README](anonyymioodi/README.md)
@@ -198,14 +207,12 @@ Make sure you have your VPN on.
First: Try `npm run cli` option 1: _Set up oodikone from scratch_ and then option 2 to download and restore databases (or option 4 to skip downloading, if you've done it recently).
If that does not help, try option 5: _Docker system prune_. Notice that this clears up **all docker data**, including data related to other projects. If this is not ok, you'll need to identify the oodikone-specific docker volumes yourself and caches and clear those invididually.
If that does not help, try option 5: _Docker system prune_. Notice that this clears up **all Docker data**, including data related to other projects. If this is not ok, you'll need to identify the Oodikone-specific Docker volumes yourself and caches and clear those invididually.
## ✌🏼 Maintainers and contribution
[Toska](https://toska.dev) of course.
[Toska](https://toska.dev) - University of Helsinki
University of Helsinki
## 🛡️ Tietosuoja / data protection
## Tietosuoja / data protection
See [tietosuoja.md](tietosuoja.md).
See [tietosuoja.md](./documentation/tietosuoja.md).
-2
View File
@@ -1,2 +0,0 @@
*.sqz
+4 -4
View File
@@ -9,7 +9,7 @@ services:
PGDATA: /data
POSTGRES_DB: kone-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.3
image: postgres:15.7
profiles:
- oodikone
@@ -20,7 +20,7 @@ services:
PGDATA: /data
POSTGRES_DB: sis-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.3
image: postgres:15.7
shm_size: 2gb
sis-importer-db:
@@ -30,7 +30,7 @@ services:
PGDATA: /data
POSTGRES_DB: sis-importer-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.3
image: postgres:15.7
profiles:
- updater
@@ -41,6 +41,6 @@ services:
PGDATA: /data
POSTGRES_DB: user-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.3
image: postgres:15.7
profiles:
- oodikone
+7 -1
View File
@@ -1,3 +1,7 @@
/* eslint-disable global-require */
/* eslint-disable import/extensions */
/* eslint-disable import/no-extraneous-dependencies */
const { defineConfig } = require('cypress')
module.exports = defineConfig({
@@ -11,7 +15,9 @@ module.exports = defineConfig({
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
require('@cypress/code-coverage/task')(on, config)
require('./cypress/plugins/index.js')(on, config)
return config
},
baseUrl: 'http://localhost:3000',
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
-2
View File
@@ -1,2 +0,0 @@
screenshots
videos
+9 -9
View File
@@ -4,6 +4,13 @@ const openCompletedCoursesModal = () => {
cy.get('button').contains('Search completed courses of students').click()
}
const hasLanded = () => {
cy.contains('Search completed courses')
cy.contains(
'Here you can search by a list of student and course numbers to see whether students have completed certain courses yet'
)
}
const openSearch = () => {
cy.init('/completedcoursessearch')
hasLanded()
@@ -12,13 +19,6 @@ const selectSavedCourselist = name => {
cy.get('[data-cy="history-search"]').children().eq(0).type(name).type('{enter}')
}
const hasLanded = () => {
cy.contains('Search completed courses')
cy.contains(
'Here you can search by a list of student and course numbers to see whether students have completed certain courses yet'
)
}
const deleteAllSearches = () => {
cy.contains('Saved courselists')
.get('.dropdown')
@@ -143,11 +143,11 @@ describe('Courselist saving-related functions work', () => {
openSearch()
openCompletedCoursesModal()
selectSavedCourselist(courseListNames[0])
cy.get('[data-cy="course-list-input"]').type(`,TKT10001`)
cy.get('[data-cy="course-list-input"]').type(',TKT10001')
cy.get('button').contains('Save').click()
openSearch()
openCompletedCoursesModal()
selectSavedCourselist(courseListNames[0])
cy.get('[data-cy="course-list-input"]').contains(`TKT10001`)
cy.get('[data-cy="course-list-input"]').contains('TKT10001')
})
})
+67 -59
View File
@@ -1,15 +1,13 @@
/// <reference types="Cypress" />
const openCustomPopupForm = () => {
cy.get('button').contains('Custom population').click()
cy.get('[data-cy="custom-pop-search-button"]').click()
}
const fillName = () => {
const name = `TEST-${new Date().getTime()}`
cy.contains('Insert name for this custom population if you wish to save it')
.siblings()
.get('input[placeholder=name]')
.type(name)
cy.get('[data-cy="custom-population-name-input"]').type(name)
return name
}
@@ -40,20 +38,17 @@ const deleteAllSearches = () => {
})
}
const fillForm = content => {
cy.contains('Insert student numbers you wish to use for population here')
.siblings()
.get('textarea')
.type(content.join('\n'))
const fillForm = (content, separator) => {
cy.get('[data-cy="student-number-input"]').type(content.join(separator))
}
const search = () => {
cy.get('button').contains('Search population').click()
cy.get('[data-cy="search-button"]').click()
}
const searchFor = studentnumbers => {
const searchFor = (studentnumbers, separator) => {
openCustomPopupForm()
fillForm(studentnumbers)
fillForm(studentnumbers, separator)
search()
}
@@ -74,8 +69,19 @@ const containsSpecificStudents = (studentnumbers = []) => {
studentnumbers.forEach(s => cy.contains(s))
}
const checkRightsNotification = studentNumbers => {
cy.get('[data-cy="rights-notification"]').within(() => {
cy.contains(
'The following students information could not be displayed. This could be either because they do not exist, or you do not have the right to view their information.'
)
cy.get('ul').within(() => {
studentNumbers.forEach(number => cy.get('li').contains(number))
})
})
}
describe('Custom population tests', () => {
const nonExistentStudentNumbers = ['123', 'X', '-', ' ']
const nonExistentStudentNumbers = ['123', 'X', '-']
beforeEach(() => {
cy.init('/custompopulation')
cy.url().should('include', '/custompopulation')
@@ -83,7 +89,7 @@ describe('Custom population tests', () => {
})
after(() => {
cy.visit(`${Cypress.config().baseUrl}/custompopulation`)
cy.visit('/custompopulation')
cy.url().should('include', '/custompopulation')
cy.contains('Custom population')
cy.get('button').contains('Custom population').click()
@@ -92,9 +98,8 @@ describe('Custom population tests', () => {
describe('Custom population searching', () => {
it('Finds a proper population', () => {
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1 }) => {
const students = studentNumbersForCSStudentsSet1
searchFor(students)
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1: students }) => {
searchFor(students, '\n')
hasLanded()
containsAmountOfStudents(students.length)
containsSpecificStudents(students)
@@ -102,17 +107,18 @@ describe('Custom population tests', () => {
})
it("Doesn't return non-existing students", () => {
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1 }) => {
const students = [...studentNumbersForCSStudentsSet1, ...nonExistentStudentNumbers]
searchFor(students)
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1: students }) => {
searchFor([...students, ...nonExistentStudentNumbers], ' ')
hasLanded()
containsAmountOfStudents(studentNumbersForCSStudentsSet1.length)
containsSpecificStudents(studentNumbersForCSStudentsSet1)
containsAmountOfStudents(students.length)
containsSpecificStudents(students)
checkRightsNotification(nonExistentStudentNumbers)
})
})
it("Doesn't find empty custom population", () => {
searchFor(nonExistentStudentNumbers)
searchFor(nonExistentStudentNumbers, ',')
checkRightsNotification(nonExistentStudentNumbers)
cy.contains('Credit accumulation').should('not.exist')
cy.contains('Programme distribution').should('not.exist')
cy.contains('Courses of population').should('not.exist')
@@ -120,10 +126,11 @@ describe('Custom population tests', () => {
})
it("Doesn't return students user has no right to", () => {
// TODO: check that this doesn't create false positives, since this test goes
// through even if following students are not in anon sis-db.
// These students exist in the database, but the user doesn't have the right to view them
const studentsForEduBachStudents = ['014990067', '013069465', '014853890']
searchFor(studentsForEduBachStudents)
// Two semicolons on purpose, the page should be able to handle it
searchFor(studentsForEduBachStudents, ';;')
checkRightsNotification(studentsForEduBachStudents)
cy.contains('Credit accumulation').should('not.exist')
cy.contains('Programme distribution').should('not.exist')
cy.contains('Courses of population').should('not.exist')
@@ -133,11 +140,14 @@ describe('Custom population tests', () => {
describe('Custom population search saving', () => {
it('Saves a custom population search', { retries: 2 }, () => {
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1 }) => {
const students = studentNumbersForCSStudentsSet1
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1: students }) => {
openCustomPopupForm()
cy.contains(
'Insert student numbers you wish to use for population. Separate each number with a comma, semicolon, space, or newline.'
)
const name = fillName()
fillForm(students)
// A comma and a newline on purpose, the page should be able to handle it
fillForm(students, ',\n')
save()
// Round 1
@@ -148,10 +158,9 @@ describe('Custom population tests', () => {
containsSpecificStudents(students)
// Round 2
cy.visit(`${Cypress.config().baseUrl}/custompopulation`)
cy.url().should('include', '/custompopulation')
cy.visit('/custompopulation')
cy.contains('Custom population')
cy.get('button').contains('Custom population').click()
openCustomPopupForm()
selectSavedPopulation(name)
search()
cy.contains(`Custom population "${name}"`)
@@ -161,35 +170,34 @@ describe('Custom population tests', () => {
})
it('Updates a custom population search', () => {
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1, studentNumbersForCSStudentsSet2 }) => {
const students1 = studentNumbersForCSStudentsSet1
const students2 = studentNumbersForCSStudentsSet2
openCustomPopupForm()
const name = fillName()
fillForm(students1)
save()
cy.fixture('customPopulations').then(
({ studentNumbersForCSStudentsSet1: students1, studentNumbersForCSStudentsSet2: students2 }) => {
openCustomPopupForm()
const name = fillName()
fillForm(students1, ' ')
save()
selectSavedPopulation(name)
search()
cy.contains(`Custom population "${name}"`)
containsAmountOfStudents(students1.length)
containsSpecificStudents(students1)
cy.contains('Custom population')
cy.get('button').contains('Custom population').click()
selectSavedPopulation(name)
fillForm([...students1, ...students2])
save()
selectSavedPopulation(name)
search()
cy.contains(`Custom population "${name}"`)
containsAmountOfStudents(students1.length)
containsSpecificStudents(students1)
cy.contains('Custom population')
openCustomPopupForm()
selectSavedPopulation(name)
fillForm(['\n', ...students2], ', ')
save()
cy.visit(`${Cypress.config().baseUrl}/custompopulation`)
cy.url().should('include', '/custompopulation')
cy.contains('Custom population')
cy.get('button').contains('Custom population').click()
selectSavedPopulation(name)
search()
cy.contains(`Custom population "${name}"`)
containsAmountOfStudents(students1.length + students2.length)
containsSpecificStudents([...students1, ...students2])
})
cy.visit('/custompopulation')
cy.contains('Custom population')
openCustomPopupForm()
selectSavedPopulation(name)
search()
cy.contains(`Custom population "${name}"`)
containsAmountOfStudents(students1.length + students2.length)
containsSpecificStudents([...students1, ...students2])
}
)
})
})
})
+35 -7
View File
@@ -1,5 +1,10 @@
/// <reference types="Cypress" />
const path = require('path')
const timestamp = new Date().toISOString().split('T')[0] // YYYY-MM-DD
const downloadsFolder = Cypress.config('downloadsFolder')
describe('Faculty overview', () => {
describe('Faculty can be selected', () => {
it('Faculties are listed and one can be chosen', () => {
@@ -11,7 +16,7 @@ describe('Faculty overview', () => {
})
})
describe('Faculty basic information: admin user', () => {
describe('Basic information: admin user', () => {
beforeEach(() => {
cy.init('/faculties', 'admin')
cy.contains('a', 'Eläinlääketieteellinen tiedekunta').click()
@@ -27,7 +32,7 @@ describe('Faculty overview', () => {
})
})
describe('Faculty basic information: basic user', () => {
describe('Basic information: basic user', () => {
beforeEach(() => {
cy.init('/faculties')
cy.contains('a', 'Maatalous-metsätieteellinen tiedekunta').click()
@@ -76,6 +81,23 @@ describe('Faculty overview', () => {
cy.get('[data-cy="StudentsOfTheFaculty-close-info"]').click()
cy.get('[data-cy="StudentsOfTheFaculty-info-content"]').should('not.exist')
})
it('Data can be exported to Excel files', () => {
const sections = [
'StudentsOfTheFaculty',
'GraduatedOfTheFaculty',
'ThesisWritersOfTheFaculty',
'CreditsProducedByTheFaculty',
]
sections.forEach(section => {
cy.get(`[data-cy="Graph-${section}"]`).within(() => {
cy.get('[aria-label="View chart menu, Chart"]').click()
cy.contains('li.highcharts-menu-item', 'Download XLS').click()
const downloadedFile = `oodikone_${section}_H80_${timestamp}.xls`
cy.readFile(path.join(downloadsFolder, downloadedFile))
})
})
})
})
describe('Study programme information: users with access', () => {
@@ -142,7 +164,7 @@ describe('Faculty overview', () => {
cy.get('[data-cy="Section-bachelor"]').should('be.visible')
cy.get('[data-cy="Section-master"]').should('be.visible')
cy.get('[data-cy="Section-master"]').within(() => {
cy.contains('Graduation year').should('be.visible')
cy.contains('text.highcharts-axis-title', 'Graduation year')
cy.contains('.message', "Click a bar to view that year's programme level breakdown").should('be.visible')
})
})
@@ -168,7 +190,7 @@ describe('Faculty overview', () => {
cy.get('div[class="programmes-graph"]').should('be.visible')
cy.get('div[class="programmes-graph"]').within(() => {
cy.contains('EDUK')
cy.contains('1 graduated').trigger('mouseover')
cy.get('[aria-label="EDUK, 40."]').trigger('mouseover')
cy.contains('Kasvatustieteiden kandiohjelma')
cy.contains('KH60_001')
})
@@ -189,7 +211,7 @@ describe('Faculty overview', () => {
cy.get('div[class="programmes-graph"]').should('be.visible')
cy.get('div[class="programmes-graph"]').within(() => {
cy.contains('EDUM')
cy.contains('24 graduated').trigger('mouseover')
cy.get('[aria-label="EDUM, 22."]').trigger('mouseover')
cy.contains('Kasvatustieteiden maisteriohjelma')
cy.contains('MH60_001')
})
@@ -211,17 +233,23 @@ describe('Faculty overview', () => {
cy.get('[data-cy="GraduationTimeToggle"]').click()
cy.get('[data-cy="Section-master"]').within(() => {
cy.contains('16').click()
cy.get('[aria-label="2018, 16. On time."]').click()
cy.get('div[class="programmes-breakdown-graph"]').should('be.visible')
cy.get('div[class="programmes-breakdown-graph"]').within(() => {
cy.contains('Year 2018 by start year')
cy.contains('EDUM')
cy.contains('16').trigger('mouseover')
cy.get('[aria-label="EDUM, 3. Max. year overtime."]').trigger('mouseover')
cy.contains('Kasvatustieteiden maisteriohjelma')
cy.contains('MH60_001')
})
})
})
it('Data can be exported to an Excel file', () => {
cy.get('[data-cy="DownloadButton-AverageGraduationTimes"]').click()
const downloadedFile = `oodikone_H60_graduation_times_${timestamp}.xlsx`
cy.readFile(path.join(downloadsFolder, downloadedFile))
})
})
describe('Faculty Progress and student population tab', () => {
+3 -3
View File
@@ -162,13 +162,13 @@ describe('Population Statistics', () => {
})
})
it('Credit filter works', () => {
it('Credit filter works', { retries: 2 }, () => {
runTestStepWithPreAndPostParts('CreditsEarned', () => {
testRangeFilter('CreditsEarned-filter-card', 50, 150, 118)
})
})
it('Age filter works', () => {
it('Age filter works', { retries: 2 }, () => {
cy.clock(MOCKED_DATE, ['Date'])
runTestStepWithPreAndPostParts('Age', () => {
testRangeFilter('Age-filter-card', 20, 40, 47)
@@ -346,7 +346,7 @@ describe('Custom Population Statistics', () => {
cy.fixture('customPopulations').then(({ studentNumbersForCSStudentsSet1, studentNumbersForCSStudentsSet2 }) => {
const students = [...studentNumbersForCSStudentsSet1, ...studentNumbersForCSStudentsSet2]
runTestStepWithPreAndPostParts = createRunTestStepWithPreAndPostPartsFunction(students.length)
cy.cs('student-no-input').click().type(students.join('\n'))
cy.cs('student-number-input').click().type(students.join('\n'))
})
cy.cs('search-button').click()
})
+1 -1
View File
@@ -57,7 +57,7 @@ describe('When language center is opened', () => {
it('"Access denied" is shown', () => {
cy.init('/languagecenterview', 'norights')
cy.contains('Access denied')
cy.contains("You're currently not allowed to see this page.")
cy.contains("You don't currently have permission to view this page.")
cy.contains('Special populations').click()
cy.contains('Language center view').should('not.exist')
})
+9 -13
View File
@@ -1,7 +1,5 @@
/// <reference types="Cypress" />
// Now "Class statistics" in UI
const moment = require('moment')
const _ = require('lodash')
describe('Population Statistics tests', () => {
const pathToCSBach2017 =
@@ -102,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')
@@ -141,12 +141,8 @@ describe('Population Statistics tests', () => {
})
cy.contains('Credits gained').click()
cy.get("[data-cy='credits-gained-main-table']").should('contain', 'All students of the class')
const today = moment().endOf('month')
const months = Math.round(moment.duration(moment(today).diff(moment('2017-08-01'))).asMonths())
// Months are capped to goal time of the degree, 36 months is the goal time for bachelor's degree
const monthsForLimits = Math.min(36, months)
const limits = [1, ..._.range(1, 5).map(p => Math.ceil(monthsForLimits * ((p * 15) / 12))), null]
const ranges = _.range(1, limits.length).map(i => _.slice(limits, i - 1, i + 1))
const limits = [45, 90, 135, 180, null]
const ranges = limits.map((limit, i) => (i === 0 ? [1, limit] : [limits[i - 1], limit]))
cy.get('.credits-gained-table').should('contain', '(n = 170)')
@@ -165,7 +161,7 @@ describe('Population Statistics tests', () => {
}
cy.get("[data-cy='credits-gained-table-body'] td:nth-child(3)").then($els => {
const sum = [...$els].map($el => parseInt($el.innerText)).reduce((a, b) => a + b, 0)
const sum = [...$els].map($el => parseInt($el.innerText, 10)).reduce((a, b) => a + b, 0)
expect(sum).to.equal(170)
})
@@ -182,7 +178,6 @@ describe('Population Statistics tests', () => {
if (!$parentDiv.hasClass('active')) cy.get("[data-cy='Credit statistics']").click()
})
cy.contains('Credits gained').click()
cy.get('.credits-gained-divider').click()
cy.get("[data-cy='credits-gained-table-Avoin väylä']").should('exist')
cy.get("[data-cy='credits-gained-table-Yhteispisteet']").should('exist')
cy.get("[data-cy='credits-gained-table-Muu']").should('exist')
@@ -198,8 +193,9 @@ describe('Population Statistics tests', () => {
})
cy.get("[data-cy='credit-stats-tab'] > .menu > :nth-child(2)").click()
cy.get("[data-cy='credit-stats-table-name-header']").should('contain', 'Statistic for n = 170 Students')
cy.get("[data-cy='credit-stats-mean']").should('contain', '114.74')
cy.get("[data-cy='credit-stats-population-size']").should('contain', 'n = 170')
cy.get("[data-cy='credit-stats-average']").should('contain', '114.74')
cy.get("[data-cy='credit-stats-median']").should('contain', '109.00')
cy.get("[data-cy='credit-stats-stdev']").should('contain', '65.53')
cy.get("[data-cy='credit-stats-min']").should('contain', '5')
cy.get("[data-cy='credit-stats-max']").should('contain', '314')
+13 -2
View File
@@ -39,7 +39,7 @@ describe('Students tests', () => {
cy.url().should('include', '/students')
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.contains('Tietojenkäsittelytieteen kandiohjelma (01.08.2017 - 31.07.2025')
cy.contains('Tietojenkäsittelytieteen kandiohjelma (01.08.2017–31.07.2025')
cy.contains(student.lastname).should('not.exist')
cy.contains(student.firstnames).should('not.exist')
@@ -64,6 +64,7 @@ describe('Students tests', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.contains('Introduction to Machine Learning (DATA11002)')
.parent()
.siblings()
.last()
.within(() => {
@@ -81,6 +82,14 @@ describe('Students tests', () => {
.and('include', 'https://sisu.helsinki.fi/tutor/role/staff/student/hy-hlo-84830887/basic/basic-info')
})
it('Bachelor Honours section is shown', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.contains('h4', 'Bachelor Honours')
cy.contains('.tag.label', 'Not qualified for Honours')
cy.contains('.tag.label', 'Has not graduated')
})
it('Searching with bad inputs doesnt yield results', () => {
cy.get('.prompt').type('SWAG LITTINEN')
cy.contains('Student number').should('not.exist')
@@ -92,7 +101,9 @@ describe('Students tests', () => {
it('Can jump to population page', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.get('i.level.up.alternate.icon').eq(0).click()
cy.contains('.ui.table', 'Completed').within(() => {
cy.get('i.level.up.alternate.icon').click()
})
cy.contains('Tietojenkäsittelytieteen kandiohjelma')
cy.contains('class size')
})
+2 -12
View File
@@ -15,7 +15,7 @@ describe('Study guidance group tests', () => {
cy.init('/studyguidancegroups')
cy.contains('Access denied')
cy.contains(
"You're currently not allowed to see this page. Please contant oodikone@helsinki.fi, if this is a mistake."
"You don't currently have permission to view this page. If you believe this is a mistake, please contact oodikone@helsinki.fi."
)
})
})
@@ -79,14 +79,7 @@ describe('Study guidance group tests', () => {
})
it('general tab of the students table has the correct columns', () => {
const firstRowHeadings = [
'Phone number',
'Student number',
'Credits',
'Start year at uni',
'Study programmes',
'Tags',
]
const firstRowHeadings = ['Student number', 'Credits', 'Start year at uni', 'Study programmes', 'Tags']
const secondRowHeadings = ['All', 'Since 1.1.1970']
cy.cs('Students (2)').click()
cy.get('[data-cy="student-table-tabs"] table thead tr').then($tr => {
@@ -137,17 +130,14 @@ describe('Study guidance group tests', () => {
it('general tab of the students table has the correct columns', () => {
const firstRowHeadings = [
'Phone number',
'Student number',
'Credits',
'Start of studyright',
'Started in programme',
'Semesters present',
'Enrollment status',
'Graduation date',
'Start year at uni',
'Other programmes',
'Semesters present amount',
'Transferred from',
'Gender',
'Citizenship',
+55 -50
View File
@@ -23,7 +23,7 @@ const getEmptyYears = isAcademicYear => {
}
describe('Studyprogramme overview', () => {
/* Basic information overview -tests*/
/* Basic information overview -tests */
describe('Basic information -view works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
@@ -42,13 +42,13 @@ describe('Studyprogramme overview', () => {
it('Basic information contains correct students', () => {
const years = getEmptyYears()
const tableContents = [
// [Year, Started, Graduated, Transferred Away, Transferred to]
...years.map(year => [year, 0, 0, 0, 0]),
[2021, 0, 0, 1, 0],
[2020, 10, 35, 2, 2],
[2019, 86, 17, 1, 1],
[2018, 161, 1, 0, 0],
[2017, 171, 0, 0, 0],
// [Year, Started studying, Accepted, Graduated, Transferred Away, Transferred to]
...years.map(year => [year, 0, 0, 0, 0, 0]),
[2021, 0, 0, 0, 1, 0],
[2020, 10, 10, 35, 2, 2],
[2019, 83, 86, 17, 1, 1],
[2018, 150, 161, 1, 0, 0],
[2017, 158, 171, 0, 0, 0],
]
cy.checkTableStats(tableContents, 'StudentsOfTheStudyprogramme')
@@ -62,7 +62,7 @@ describe('Studyprogramme overview', () => {
[2020, 4511, 4511, 0, 0, 406],
[2019, 7117, 7097, 0, 0, 434],
[2018, 5742, 5737, 0, 0, 150],
[2017, 1882, 1882, 0, 0, 186],
[2017, 1887, 1882, 0, 0, 186],
]
cy.checkTableStats(tableContents, 'CreditsProducedByTheStudyprogramme')
@@ -87,13 +87,13 @@ describe('Studyprogramme overview', () => {
cy.get('[data-cy=StudentToggle]').click()
const years = getEmptyYears()
const studentTableContents = [
// [Year, Started, Graduated]
...years.map(year => [year, 0, 0]),
[2021, 0, 0],
[2020, 10, 35],
[2019, 86, 17],
[2018, 161, 1],
[2017, 171, 0],
// [Year, Started studying, Accepted, Graduated]
...years.map(year => [year, 0, 0, 0]),
[2021, 0, 0, 0],
[2020, 10, 10, 35],
[2019, 83, 86, 17],
[2018, 150, 161, 1],
[2017, 158, 171, 0],
]
cy.checkTableStats(studentTableContents, 'StudentsOfTheStudyprogramme')
@@ -104,7 +104,7 @@ describe('Studyprogramme overview', () => {
[2020, 4511, 4511, 0, 0, 406],
[2019, 7117, 7097, 0, 0, 434],
[2018, 5742, 5737, 0, 0, 150],
[2017, 1882, 1882, 0, 0, 186],
[2017, 1887, 1882, 0, 0, 186],
]
cy.checkTableStats(creditTableContents, 'CreditsProducedByTheStudyprogramme')
@@ -128,13 +128,13 @@ describe('Studyprogramme overview', () => {
const isAcademicYear = true
const years = getEmptyYears(isAcademicYear)
const studentTableContents = [
// [Year, Started, Graduated, Transferred away, Transferred to]
...years.map(year => [year, 0, 0, 0, 0]),
['2021 - 2022', 0, 0, 0, 0],
['2020 - 2021', 10, 6, 3, 2],
['2019 - 2020', 86, 38, 1, 0],
['2018 - 2019', 161, 8, 0, 1],
['2017 - 2018', 171, 1, 0, 0],
// [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', 10, 10, 6, 1, 2],
['2019 - 2020', 83, 86, 38, 3, 0],
['2018 - 2019', 150, 161, 8, 0, 1],
['2017 - 2018', 158, 171, 1, 0, 0],
]
cy.checkTableStats(studentTableContents, 'StudentsOfTheStudyprogramme')
@@ -144,7 +144,7 @@ describe('Studyprogramme overview', () => {
['2020 - 2021', 352, 352, 0, 0, 296],
['2019 - 2020', 6966, 6966, 0, 0, 430],
['2018 - 2019', 7797, 7777, 0, 0, 259],
['2017 - 2018', 4137, 4132, 0, 0, 191],
['2017 - 2018', 4142, 4132, 0, 0, 191],
]
cy.checkTableStats(creditTableContents, 'CreditsProducedByTheStudyprogramme')
@@ -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(() => {
@@ -221,7 +223,7 @@ describe('Studyprogramme overview', () => {
})
})
/* Studytrack overview -tests*/
/* Studytrack overview -tests */
describe('Studytrack overview works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
@@ -238,27 +240,28 @@ describe('Studyprogramme overview', () => {
it('Students of the studyprogramme are shown correctly', () => {
const tableContents = [
// [Year, All, Started studying, Currently enrolled, Absent, Inactive, Graduated, Men, Women, other/unknown Finnish, other]
// [Year, All, Started studying, Present, Absent, Inactive, Graduated, Men, Women, Other/Unknown, Finland, Other]
['2020 - 2021', 12, 10, 0, 0, 12, 0, 10, 2, 0, 12, 0],
['2019 - 2020', 86, 86, 0, 0, 85, 1, 65, 21, 0, 86, 0],
['2018 - 2019', 162, 161, 0, 0, 151, 11, 118, 44, 0, 161, 1],
['2017 - 2018', 171, 171, 0, 0, 129, 42, 135, 36, 0, 166, 5],
['Total', 431, 428, 0, 0, 377, 54, 328, 103, 0, 425, 6],
['2019 - 2020', 86, 83, 0, 0, 85, 1, 65, 21, 0, 86, 0],
['2018 - 2019', 162, 150, 0, 0, 151, 11, 118, 44, 0, 161, 1],
['2017 - 2018', 171, 158, 0, 0, 130, 41, 135, 36, 0, 166, 5],
['Total', 431, 401, 0, 0, 378, 53, 328, 103, 0, 425, 6],
]
cy.checkTableStats(tableContents, 'StudytrackOverview')
})
it('Student progress data is shown correctly', () => {
const years = getEmptyYears(true)
const tableContents = [
['2023 - 2024', 0, 0, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 0, 0, 0, 0, 0, 0, 0, 0],
// [Year, All, < 30 credits, 30–60 credits, 60–90 credits, 90–120 credits, 120–150 credits, 150–180 credits, ≥ 180 credits]
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2021 - 2022', 0, 0, 0, 0, 0, 0, 0, 0],
['2020 - 2021', 12, 12, 0, 0, 0, 0, 0, 0],
['2019 - 2020', 86, 26, 34, 21, 5, 0, 0, 0],
['2018 - 2019', 162, 10, 27, 47, 36, 26, 12, 4],
['2018 - 2019', 162, 10, 28, 47, 35, 26, 12, 4],
['2017 - 2018', 171, 20, 21, 26, 22, 22, 33, 27],
['Total', 431, 68, 82, 94, 63, 48, 45, 31],
['Total', 431, 68, 83, 94, 62, 48, 45, 31],
]
cy.checkTableStats(tableContents, 'StudytrackProgress')
@@ -276,15 +279,15 @@ describe('Studyprogramme overview', () => {
cy.get('[data-cy=graduation-times-graphBachelor]').within(() => {
cy.contains('Start year')
cy.contains('2019 - 2020')
cy.contains('42 graduated').trigger('mouseover')
cy.contains('study time: 31.5 months')
cy.contains('39 graduated on time')
cy.contains('2017 - 2018')
cy.contains('41 graduated').trigger('mouseover')
cy.contains('study time: 31 months')
cy.contains('36 graduated on time')
})
})
})
/* Programme courses -tests*/
/* Programme courses -tests */
describe('Programme courses works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
@@ -397,7 +400,9 @@ describe('Studyprogramme overview', () => {
cy.get('.form > .field > .dropdown > .visible').contains(name).click()
cy.get('textarea').type('010113437')
cy.get('.positive').click()
cy.contains('Add tags').click()
cy.contains('Successfully added tags to students.')
cy.contains('Students').click()
cy.get('.prompt').type(student)
+13 -7
View File
@@ -11,7 +11,7 @@ describe('Teachers page tests', () => {
it('Check Statistics', () => {
cy.get(':nth-child(1) > .ui > .search').click()
cy.contains('Autumn 2020').click()
cy.contains('Syksy 2020').click()
cy.cs('course-providers').click()
cy.contains('Tietojenkäsittelytieteen kandiohjelma').click()
cy.cs('course-providers').children('.icon').click()
@@ -25,15 +25,21 @@ describe('Teachers page tests', () => {
cy.url().should('include', '/teachers')
cy.get('.borderless > :nth-child(3)').click()
cy.get('.prompt').type(teacher1.split()[0])
cy.contains(teacher1)
cy.contains('td', teacher1)
})
it('Can check teacher page', () => {
cy.url().should('include', '/teachers')
cy.get('.borderless > :nth-child(3)').click()
cy.get('.prompt').type(teacher1.split()[0])
cy.contains('td', teacher1).click()
cy.contains('Aineopintojen harjoitustyö: Tietokantasovellus')
cy.visit('/teachers/hy-hlo-51367956')
cy.contains('Landgraf Leo')
cy.get('.ok-sortable-table tbody tr')
.first()
.within(() => {
cy.contains('td:nth-child(1)', 'TKT20011')
cy.contains('td:nth-child(2)', 'Aineopintojen harjoitustyö: Tietokantasovellus')
cy.contains('td:nth-child(3)', '96')
cy.contains('td:nth-child(4)', '0')
cy.contains('td:nth-child(5)', '88.89%')
})
})
it('Check leaderboad works', () => {
+2
View File
@@ -13,6 +13,8 @@
// https://on.cypress.io/configuration
// ***********************************************************
// eslint-disable-next-line import/no-extraneous-dependencies
require('@cypress/code-coverage/support')
require('./commands')
// This error is benign and seems to be only present in Cypress
+3 -3
View File
@@ -44,7 +44,7 @@ services:
<<: *defaults
command: >
sh -c 'redis-server --appendonly yes && redis-cli flushall'
image: redis:6.2.13
image: redis:6.2.14
ports:
- 6379:6379
@@ -81,7 +81,7 @@ services:
jami-db:
<<: *defaults
image: postgres:13.2
image: postgres:15.7
environment:
- PGDATA=/data
- POSTGRES_PASSWORD=postgres
@@ -161,6 +161,6 @@ services:
<<: *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'
container_name: sis-updater-nats
image: nats-streaming:0.16.2
image: nats-streaming:0.25.6
profiles:
- updater
+182
View File
@@ -0,0 +1,182 @@
x-defaults: &defaults
restart: unless-stopped
x-build_defaults: &build_defaults
dockerfile: dev.Dockerfile
services:
adminer:
<<: *defaults
container_name: adminer
environment:
ADMINER_DESIGN: pepa-linha
image: adminer:4.8.1
ports:
- 5050:8080
volumes:
- ./adminer-plugins:/var/www/html/plugins-enabled
backend:
<<: *defaults
build:
<<: *build_defaults
context: services/backend
depends_on:
- kone-db
- redis
- sis-db
- user-db
env_file:
- .env
environment:
DB_URL_KONE: postgres://postgres@kone-db:5432/kone-db
DB_URL_USER: postgres://postgres@user-db:5432/user-db
FRONT_URL: http://localhost:3000
REDIS: redis
SECRET_TOKEN: dev
SIS_DB_URL: postgres://postgres@sis-db:5432/sis-db
SIS_UPDATER_URL: http://sis-updater-scheduler:8082
IMPORTER_DB_API_URL: 'https://localhost:3005'
JAMI_URL: 'http://jami:3003/'
SERVICE_PROVIDER: 'Fd'
SIS_HOST: ${SIS_HOST}
SIS_GRAPHQL_APPLICATION_ACCOUNT: ${SIS_GRAPHQL_APPLICATION_ACCOUNT}
SIS_GRAPHQL_APPLICATION_KEY: ${SIS_GRAPHQL_APPLICATION_KEY}
ports:
- 8080:8080
volumes:
- ./environments/ha:/opt/app-root/src/src/environment
- ./services/backend/src:/opt/app-root/src/src
frontend:
<<: *defaults
build:
<<: *build_defaults
context: services/frontend
container_name: frontend
ports:
- 3000:3000
environment:
REACT_APP_SERVICE_PROVIDER: 'Fd'
volumes:
- ./services/frontend/src/:/opt/app-root/src/src
kone-db:
<<: *defaults
container_name: kone-db
environment:
PGDATA: /data
POSTGRES_DB: kone-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
ports:
- 12349:5432
volumes:
- kone-db-data:/data
redis:
<<: *defaults
command: 'redis-server --appendonly yes'
container_name: redis
image: redis:6.2.14
ports:
- '6379:6379'
volumes:
- redis-data:/data
sis-db:
<<: *defaults
container_name: sis-db
environment:
PGDATA: /data
POSTGRES_DB: sis-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
volumes:
- sis-db-data:/data
shm_size: 2gb
ports:
- 12345: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'
container_name: sis-updater-nats
image: nats-streaming:0.25.6
sis-updater-scheduler:
<<: *defaults
build:
<<: *build_defaults
context: updater/sis-updater-scheduler
container_name: sis-updater-scheduler
extra_hosts:
- 'host.docker.internal:host-gateway'
depends_on:
- redis
- sis-updater-nats
environment:
REDIS_URI: //redis:6379
SECRET_TOKEN: dev
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:
- ./updater/sis-updater-scheduler/:/opt/app-root/src
sis-updater-worker:
<<: *defaults
build:
<<: *build_defaults
context: updater/sis-updater-worker
depends_on:
- redis
- sis-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_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:
- ./updater/sis-updater-worker/:/opt/app-root/src
user-db:
<<: *defaults
container_name: user-db
environment:
PGDATA: /data
POSTGRES_DB: user-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
volumes:
- user-db-data:/data
ports:
- 12347:5432
volumes:
kone-db-data:
redis-data:
sis-db-data:
user-db-data:
networks:
default:
name: oodikone
importer:
name: importer_network
external: true
+10
View File
@@ -0,0 +1,10 @@
services:
backend:
volumes:
- backend-nodemod:/opt/app-root/src/node_modules
- ./services/backend/instrumented:/opt/app-root/src/src
frontend:
volumes:
- frontend-nodemod:/opt/app-root/src/node_modules
- ./services/frontend/instrumented:/opt/app-root/src/src
+5 -6
View File
@@ -1,5 +1,3 @@
version: '2.1'
x-defaults: &defaults
restart: unless-stopped
@@ -12,7 +10,7 @@ services:
container_name: adminer
environment:
ADMINER_DESIGN: pepa-linha
image: adminer:4.8.0
image: adminer:4.8.1
ports:
- 5050:8080
volumes:
@@ -30,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
@@ -75,7 +74,7 @@ services:
<<: *defaults
command: 'redis-server --appendonly yes'
container_name: redis
image: redis:6.2.13
image: redis:6.2.14
ports:
- '6379:6379'
volumes:
@@ -106,7 +105,7 @@ services:
<<: *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'
container_name: sis-updater-nats
image: nats-streaming:0.16.2
image: nats-streaming:0.25.6
profiles:
- updater
@@ -188,7 +187,7 @@ services:
jami-db:
<<: *defaults
image: postgres:13.2
image: postgres:15.7
environment:
- PGDATA=/data
- POSTGRES_PASSWORD=postgres
+3
View File
@@ -0,0 +1,3 @@
# Documentation
Oodikone public documentation. For internal documentation, see Toska GitLab.
+3 -3
View File
@@ -25,7 +25,7 @@ Basic rule of thumb is that for the master/licentiate programme I used names `co
All things listed below should work automatically, if new combinations are done.
- Credits produced byt the studyprogramme
- Credits produced by the studyprogramme
- credits are computed for both programmes separately
- Average graduation times
- For bachelor + master/licentiate studyright the time is the whole study time starting from bachelor
@@ -50,7 +50,7 @@ All things listed below should work automatically, if new combinations are done.
- Possibility to add criteria for whole bachelor-master study time
- For eläinlääkis this criteria is saved by bachelor programme code. Other cases may need other approaches.
In eläinklääkis this done by adding the criteria for 6 years -> the whole study time criteria is saved with the bachelor programme code. That is, same criteria is found, if bachelor or combined programme view is shown. This approach might need extra thinking for different programmes.
In eläinlääkis this done by adding the criteria for 6 years -> the whole study time criteria is saved with the bachelor programme code. That is, same criteria is found, if bachelor or combined programme view is shown. This approach might need extra thinking for different programmes.
- Show own tables for both programmes
- To fetch data **needs manual work** here `services/backend/src/services/studyprogrammeHelpers.js` in the constant `combinedStudyprogrammes`.
@@ -93,4 +93,4 @@ All things listed below should work automatically, if new combinations are done.
- Backend: student data is fetched by bachelor study programme code in `/v3/populationstatisticsbystudentnumbers`. Separation from code 'KHxx_xxx+MHxx_xxx' is done in backend.
- Frontend: things are done in the same way as with class stats.
- **Need manual work**: `./oodikone/services/frontend/src/redux/elementdetails.js` add combined programme codes (both bachelor and master) to `combinedProgrammeCodes` and modify `combinedOptions` list accordinly in `useFilterAndFormattedElementDetails()` function.
- **Need manual work**: `./oodikone/services/frontend/src/redux/elementdetails.js` add combined programme codes (both bachelor and master) to `combinedProgrammeCodes` and modify `combinedOptions` list accordingly in `useFilterAndFormattedElementDetails()` function.
+104
View File
@@ -0,0 +1,104 @@
# Databases
Oodikone uses three separate databases, each for different purposes. This document contains a simplified overview of each database.
Please note that each database may contain additional information such as migration tables or indices that are not depicted here. For more details, inspect the actual databases via the commandline.
In the tables, `PK` marks primary key(s), `FK` marks foreign keys and `UK` mark unique keys. Refer to the official [Mermaid.js documentation](https://mermaid.js.org/syntax/entityRelationshipDiagram.html) for full syntax.
## kone-db
The `kone-db` database contains information used in various features of Oodikone. This functionality is native to Oodikone and not necessarily directly related to Sisu.
```mermaid
erDiagram
custom_population_searches {
bigint id PK "not null"
bigint userId
varchar(255) name "not null"
varchar(255)[] students
timestamp createdAt
timestamp updatedAt
}
excluded_courses {
integer id PK "not null"
varchar(255) programme_code
varchar(255) course_code
timestamp created_at
timestamp updated_at
varchar(255) curriculum_version
}
open_uni_population_searches {
bigint id PK "not null"
bigint user_id
varchar(255) name "not null"
varchar(255)[] course_codes
timestamp created_at
timestamp updated_at
}
progress_criteria {
varchar(255) code PK "not null"
varchar(255)[] courses_year_one
varchar(255)[] courses_year_two
varchar(255)[] courses_year_three
integer credits_year_one
integer credits_year_two
integer credits_year_three
timestamp created_at
timestamp updated_at
varchar(255)[] courses_year_four
varchar(255)[] courses_year_five
varchar(255)[] courses_year_six
integer credits_year_four
integer credits_year_five
integer credits_year_six
varchar(255) curriculum_version
}
study_guidance_group_tags {
bigint id PK "no null"
varchar(255) study_guidance_group_id UK
varchar(255) study_programme
varchar(255) year
timestamp created_at
timestamp updated_at
}
study_programme_pins {
integer user_id PK "not null"
varchar(255)[] study_programmes
}
tag ||--o{ tag_student : "belongs to"
tag {
timestamp createdAt
timestamp updatedAt
varchar(255) tagname
varchar(255) studytrack "not null"
bigint tag_id PK "not null"
varchar(255) year
bigint personal_user_id
}
tag_student {
timestamp createdAt
timestamp updatedAt
varchar(255) studentnumber PK "not null"
bigint tag_id PK, FK "not null"
}
```
## user-db
The `user-db` database contains information about users of Oodikone.
```mermaid
erDiagram
users {
bigint id PK "not null"
varchar(255) full_name
varchar(255) username UK
varchar(255) language "default: 'fi'"
varchar(255) email
varchar(255) sisu_person_id
timestamp last_login
varchar(255)[] roles "not null, default: []"
varchar(255)[] programme_rights "not null, default: []"
}
```

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

+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 |
+50
View File
@@ -0,0 +1,50 @@
# Resources
A collection of useful resources and documentation.
## AI tools
- [ChatGPT](https://chatgpt.com)
- [CurreChat](https://curre.helsinki.fi/chat)
- [GitHub Copilot](https://github.com/features/copilot)
- Free for students!
- [Microsoft Copilot](https://copilot.microsoft.com)
## Code style and maintainability
- [CodeClimate documentation](https://docs.codeclimate.com/docs)
- [Advanced configuration](https://docs.codeclimate.com/docs/advanced-configuration)
- [ESLint documentation](https://eslint.org/docs/v8.x)
- [Prettier documentation](https://prettier.io/docs/en)
## Database
- [Postgres documentation](https://www.postgresql.org/docs/15/index.html)
- [Sequelize documentation](https://sequelize.org/docs/v6/)
## GitHub
- [GitHub documentation](https://docs.github.com/en)
- [GitHub Actions](https://docs.github.com/en/actions)
- [GitHub Education](https://github.com/education/students)
- Free benefits for students!
- [Mermaid.js documentation](https://mermaid.js.org/intro/syntax-reference.html)
## TypeScript
- [TypeScript documentation](https://www.typescriptlang.org/docs)
- [`tsconfig` reference](https://www.typescriptlang.org/tsconfig)
- [TypeScript instructions for Sequelize](https://sequelize.org/docs/v6/other-topics/typescript)
- [`sequelize-typescript` instructions](https://github.com/sequelize/sequelize-typescript?tab=readme-ov-file#readme)
## User interface
- [Semantic UI React documentation](https://react.semantic-ui.com)
## Visual Studio Code
_Todo: add relevant plugins_
## Web technologies
- [MDN Web Docs](https://developer.mozilla.org/en-US/)
+3
View File
@@ -0,0 +1,3 @@
# Tietosuoja
Oodikoneessa noudatetaan Helsingin yliopiston [yleistä linjausta](https://www.helsinki.fi/fi/tutustu-meihin/tietosuoja#opiskelijoiden-henkil-tietojen-k-sittely--title) opiskelijoiden henkilötietojen käsittelystä.
+7
View File
@@ -0,0 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
const { parserPlugins } = require('@istanbuljs/schema').defaults.nyc
module.exports = {
cache: false,
parserPlugins: parserPlugins.concat('typescript', 'jsx'),
}
+4659 -242
View File
File diff suppressed because it is too large Load Diff
+14 -6
View File
@@ -3,14 +3,16 @@
"description": "An application for analyzing university data.",
"scripts": {
"cypress": "cypress",
"instrument": "./scripts/instrument.sh",
"eslint": "eslint",
"lint:all": "eslint --ext .js,.jsx services/ updater/ --report-unused-disable-directives",
"lint:all": "eslint --ext .js,.jsx,.ts,.tsx services/ updater/ --report-unused-disable-directives",
"stylelint": "stylelint",
"actionlint": "node-actionlint",
"prettier": "prettier",
"cli": "./cli.sh",
"oodikone": "./run.sh oodikone anon up --detach",
"oodikone:real": "./run.sh oodikone real up --detach",
"oodikone:test": "./run.sh oodikone test up --detach",
"updater": "./run.sh updater anon up --detach",
"updater:real": "./run.sh updater real up --detach",
"both": "./run.sh both anon up --detach",
@@ -18,7 +20,7 @@
"docker:down": "./run.sh both anon down --remove-orphans; ./run.sh both real down --remove-orphans",
"testupdater": "docker-compose --file docker-compose.ci.yml up --detach --no-recreate sis-updater-worker && SCHEDULE_IMMEDIATE=weekly EXIT_AFTER_IMMEDIATES=yes docker-compose --file docker-compose.ci.yml run sis-updater-scheduler npm start",
"flushredis": "docker exec -it redis redis-cli flushall",
"prepare": "husky install",
"prepare": "husky",
"rapodiff": "docker exec oodikone_backend_1 node /opt/app-root/src/src/rapodiff/rapodiff.js",
"install:local": "npm i && cd services/frontend && npm i && cd ../backend && npm i && cd ../../updater/sis-updater-scheduler && npm i && cd ../sis-updater-worker && npm i && cd ../..",
"install:docker": "docker exec frontend npm i && docker exec oodikone_backend_1 npm i && docker exec sis-updater-scheduler npm i && docker exec oodikone_sis-updater-worker_1 npm i",
@@ -34,21 +36,27 @@
},
"homepage": "https://github.com/UniversityOfHelsinkiCS/oodikone#readme",
"devDependencies": {
"@cypress/code-coverage": "^3.12.39",
"@istanbuljs/schema": "^0.1.3",
"@typescript-eslint/eslint-plugin": "^7.16.1",
"@typescript-eslint/parser": "^7.16.1",
"cypress": "^13.2.0",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^18.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-custom-alias": "^1.3.2",
"eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-cypress": "^3.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"node-actionlint": "^1.2.2",
"prettier": "3.2.5",
"nyc": "^17.0.0",
"prettier": "3.3.0",
"stylelint": "^16.2.1",
"stylelint-config-standard": "^36.0.0"
},
+19
View File
@@ -0,0 +1,19 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{
"groupName": "all non-major dependencies",
"groupSlug": "all-minor-patch",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"matchDatasources": ["docker"],
"matchPackageNames": ["node"],
"versionCompatibility": "^(?<version>[^-]+)(?<compatibility>-.*)?$",
"versioning": "node"
}
],
"rebaseWhen": "never"
}
+4 -1
View File
@@ -47,7 +47,7 @@ parse_params() {
compose_command=${args[*]:1}
else
version=${args[1]}
[[ "$version" != "anon" && "$version" != "real" ]] && usage && \
[[ "$version" != "anon" && "$version" != "real" && "$version" != "test" ]] && usage && \
die "Wrong version $version"
compose_command=${args[*]:2}
fi
@@ -70,6 +70,9 @@ parse_env() {
if [[ "$version" == "real" ]]; then
env="--file docker-compose.yml --file docker-compose.real.yml"
fi
if [[ "$version" == "test" ]]; then
env="--file docker-compose.yml --file docker-compose.test.yml"
fi
return 0
}
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# This script is used for instrumenting the frontend code
# Counters are added for calculating the code coverage
# Instrumented code is used for generating code coverage reports
# Ensure the script exits if any command fails
set -euo pipefail
# Print status
echo "Instrumenting code..."
# Define the directories
SOURCE_DIR="services/frontend/src"
INSTRU_DIR="services/frontend/instrumented"
# Create the instrumented directory if it does not exist
mkdir -p $INSTRU_DIR
# Instrument the code
nyc instrument --all --compact=false $SOURCE_DIR $INSTRU_DIR
# Copy remaining files without overwriting instrumented files
#
# Images, CSS and other static files are not instrumended
# but they are still needed to run the application
rsync -av --quiet --ignore-existing $SOURCE_DIR/ $INSTRU_DIR/
# Print status
echo "Done!"
+8 -4
View File
@@ -1,4 +1,4 @@
FROM registry.access.redhat.com/ubi9/nodejs-18-minimal
FROM registry.access.redhat.com/ubi9/nodejs-20-minimal
ENV TZ="Europe/Helsinki"
@@ -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"]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

-141
View File
@@ -1,141 +0,0 @@
// Map programme codes to "letter indentifier"
const codes = {
KH10_001: 'tuk',
MH10_001: 'tum',
KH20_001: 'on',
MH20_001: 'otm',
MH20_002: 'ibl',
MH20_003: 'ggl',
KH30_001: 'psyk',
MH30_004: 'psym',
KH30_002: 'logk',
MH30_005: 'logm',
MH30_001: 'll',
MH30_002: 'tmed',
MH30_003: 'hll',
MH30_006: 'terv',
KH40_001: 'filk',
KH40_002: 'ttk',
KH40_003: 'kik',
KH40_004: 'kok',
KH40_005: 'kuka',
KH40_006: 'hisk',
MH40_001: 'ttm',
MH40_002: 'kim',
MH40_003: 'eng',
MH40_004: 'rus',
MH40_005: 'lingdig',
MH40_006: 'tra',
MH40_007: 'suku',
MH40_008: 'nor',
MH40_009: 'kir',
MH40_010: 'kuma',
MH40_011: 'ice',
MH40_012: 'alku',
MH40_014: 'spt',
MH40_015: 'hism',
KH50_001: 'mat',
KH50_002: 'fys',
KH50_003: 'kemk',
KH50_004: 'mfkk',
KH50_005: 'tkt',
KH50_006: 'geok',
KH50_007: 'maa',
KH50_008: 'bsc',
MH50_001: 'mast',
MH50_002: 'lsi',
MH50_003: 'tcm',
MH50_004: 'paras',
MH50_005: 'matres',
MH50_006: 'atm',
MH50_007: 'kem',
MH50_008: 'mfkm',
MH50_009: 'csm',
MH50_010: 'data',
MH50_011: 'geom',
MH50_012: 'geog',
MH50_013: 'usp',
KH55_001: 'farm',
MH55_001: 'prov',
MH55_002: 'mpharm',
KH57_001: 'bio',
KH57_002: 'mole',
KH57_003: 'env',
MH57_001: 'eeb',
MH57_002: 'ips',
MH57_003: 'gmb',
MH57_004: 'neuro',
MH57_005: 'ecgs',
KH60_001: 'eduk',
MH60_001: 'edum',
MH60_002: 'ce',
KH70_001: 'pvk',
KH70_002: 'yk',
KH70_003: 'sosk',
KH70_004: 'ecok',
MH70_001: 'film',
MH70_002: 'pvm',
MH70_003: 'gpc',
MH70_004: 'ym',
MH70_005: 'cos',
MH70_006: 'ens',
MH70_007: 'msv',
MH70_008: 'sosm',
MH70_009: 'econ',
MH70_011: 'sote',
KH74_001: 'ksv',
KH80_001: 'maatk',
KH80_002: 'metsak',
KH80_003: 'etk',
KH80_004: 'yet',
MH80_001: 'agri',
MH80_002: 'agere',
MH80_003: 'for',
MH80_004: 'food',
MH80_005: 'hnfb',
MH80_006: 'ekm',
MH80_007: 'mmb',
MH80_008: 'emplant',
KH90_001: 'elk',
MH90_001: 'ell',
T920101: 'dptheol',
T920102: 'dplaw',
T920111: 'philartsoc',
T920103: 'dphistcult',
T920104: 'dplang',
T920105: 'sky',
T920106: 'dpsocs',
T920107: 'pyam',
T920108: 'dpe',
T920109: 'seduce',
T920110: 'clic',
T921101: 'dpbm',
T921102: 'klto',
T921103: 'docpop',
T921104: 'findos',
T921105: 'dpdr',
T921106: 'ils',
T921107: 'bandm',
T921108: 'cvm',
T921109: 'dphub',
T922101: 'luova',
T922102: 'dpps',
T922103: 'denvi',
T922104: 'agforee',
T922105: 'mbdp',
T922106: 'foodhealth',
T923101: 'papu',
T923102: 'geodoc',
T923103: 'atm-dp',
T923104: 'chems',
T923105: 'domast',
T923106: 'matrena',
T923107: 'docs',
// manually added programs with no IAM, either abolished, or special co-operation programmes
MH50_014: 'ech',
MH40_013: 'mkk',
MH70_010: 'imes',
}
module.exports = { codes }
+2 -2
View File
@@ -1,4 +1,4 @@
FROM node:18-alpine
FROM node:20-alpine
ENV TZ="Europe/Helsinki"
@@ -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())
+2843 -327
View File
File diff suppressed because it is too large Load Diff
+13 -5
View File
@@ -2,36 +2,44 @@
"name": "backend",
"private": true,
"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": {
"@sentry/node": "^5.30.0",
"@sentry/tracing": "^6.18.2",
"axios": "^0.21.4",
"axios": "^0.28.1",
"bluebird": "^3.7.2",
"bullmq": "^4.14.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
"cron": "^1.8.2",
"express": "^4.19.2",
"express-async-errors": "^3.1.1",
"lodash": "^4.17.21",
"lru-cache": "^5.1.1",
"lru-cache": "^10.2.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.37",
"morgan": "^1.10.0",
"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/express": "^4.17.21",
"@types/lodash": "^4.17.6",
"@types/node": "^20.14.2",
"@types/umzug": "^2.3.7",
"@types/validator": "^13.12.0",
"tsx": "^4.16.2",
"typescript": "^5.5.3"
},
"overrides": {
"winston-gelf-transporter": {
+6 -5
View File
@@ -1,9 +1,10 @@
require('express-async-errors')
const express = require('express')
const { baseUrl, backendPort } = require('./conf-backend')
const routes = require('./routes')
const { startCron } = require('./events')
const { baseUrl, backendPort } = require('./config')
const { initializeDatabaseConnection, dbConnections } = require('./database/connection')
const { startCron } = require('./events')
const routes = require('./routes')
const logger = require('./util/logger')
require('./worker/worker')
@@ -30,7 +31,7 @@ initializeDatabaseConnection()
})
})
})
.catch(e => {
.catch(error => {
process.exitCode = 1
logger.error(e)
logger.error(error)
})
@@ -6,6 +6,7 @@ const isProduction = !isStaging && process.env.NODE_ENV === 'production'
// Sentry
const sentryRelease = process.env.SENTRY_RELEASE || ''
const sentryEnvironment = process.env.SENTRY_ENVIRONMENT || ''
const sentryDSN = process.env.SENTRY_DSN || ''
const runningInCI = process.env.CI === 'true'
// IAM group
@@ -23,13 +24,24 @@ const jamiUrl = process.env.JAMI_URL || ''
const importerUrl = process.env.IMPORTER_DB_API_URL || ''
const importerToken = process.env.IMPORTER_DB_API_TOKEN || ''
// Sisu
const sisUrl = process.env.SIS_HOST
const sisGrapqlAppAccount = process.env.SIS_GRAPHQL_APPLICATION_ACCOUNT
const sisGrapqlAppKey = process.env.SIS_GRAPHQL_APPLICATION_KEY
// Networking: Urls & ports
const baseUrl = isDev ? '/api' : ''
const frontUrl = process.env.FRONT_URL
const backendPort = 8080
// System run in whose environment
const serviceProvider = process.env.SERVICE_PROVIDER || 'Toska'
// Optional logout-url configuration
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)) {
@@ -60,10 +72,12 @@ module.exports = {
USER_PASSWORD,
SIS_PASSWORD,
requiredGroup,
SIS_DB_URL,
SIS_UPDATER_URL,
runningInCI,
sentryRelease,
sentryEnvironment,
sentryDSN,
isProduction,
pateToken,
jamiUrl,
@@ -73,4 +87,10 @@ module.exports = {
importerUrl,
importerToken,
isDev,
isStaging,
serviceProvider,
configLogoutUrl,
sisUrl,
sisGrapqlAppAccount,
sisGrapqlAppKey,
}
@@ -0,0 +1,4 @@
// Linter was not having any of this so neeeded to add resolveJsonModule:true to tsconfig
// export const { facultyCodes, ignoredFacultyCodes, magicFacultyCode } = require('../environment/organizationConfig.json')
export { facultyCodes, ignoredFacultyCodes, magicFacultyCode } from '../environment/organizationConfig.json'
@@ -0,0 +1,138 @@
// Map programme codes to "letter indentifier"
export const programmeCodes = {
KH10_001: 'TUK',
MH10_001: 'TUM',
KH20_001: 'ON',
MH20_001: 'OTM',
MH20_002: 'IBL',
MH20_003: 'GGL',
KH30_001: 'PSYKK',
KH30_002: 'LOGOK',
KH30_003: 'SPK',
MH30_001: 'LL',
MH30_002: 'TMED',
MH30_003: 'HLL',
MH30_004: 'PSYK',
MH30_005: 'LOGO',
MH30_006: 'TERV',
KH40_001: 'FILK',
KH40_002: 'TTK',
KH40_003: 'KIK',
KH40_004: 'KOK',
KH40_005: 'KUKA',
KH40_006: 'HISK',
MH40_001: 'TTM',
MH40_002: 'KIM',
MH40_003: 'ENG',
MH40_004: 'MAREEES',
MH40_005: 'LingDig',
MH40_006: 'TRA',
MH40_007: 'SUKU',
MH40_008: 'NOR',
MH40_009: 'KIR',
MH40_010: 'KUMA',
MH40_011: 'ICE',
MH40_012: 'ALKU',
MH40_014: 'SPT',
MH40_015: 'HISM',
KH50_001: 'MAT',
KH50_002: 'FYS',
KH50_003: 'KEMK',
KH50_004: 'MFKK',
KH50_005: 'TKT',
KH50_006: 'GEOK',
KH50_007: 'MAA',
KH50_008: 'BSC',
MH50_001: 'MAST',
MH50_002: 'LSI',
MH50_003: 'TCM',
MH50_004: 'PARAS',
MH50_005: 'MATRES',
MH50_006: 'ATM',
MH50_007: 'KEM',
MH50_008: 'MFKM',
MH50_009: 'CSM',
MH50_010: 'DATA',
MH50_011: 'GEOM',
MH50_012: 'GEOG',
MH50_013: 'USP',
MH50_014: 'EnCHiL',
KH55_001: 'FARM',
MH55_001: 'PROV',
MH55_002: 'MPHARM',
KH57_001: 'BIO',
KH57_002: 'MOLE',
KH57_003: 'ENV',
MH57_001: 'EEB',
MH57_002: 'IPS',
MH57_003: 'GMB',
MH57_004: 'NEURO',
MH57_005: 'ECGS',
KH60_001: 'EDUK',
MH60_001: 'EDUM',
MH60_002: 'CE',
KH70_001: 'PVK',
KH70_002: 'YK',
KH70_003: 'SOSK',
KH70_004: 'ECOK',
MH70_001: 'FILM',
MH70_002: 'PVM',
MH70_003: 'GPC',
MH70_004: 'YM',
MH70_005: 'COS',
MH70_006: 'ENS',
MH70_007: 'MSV',
MH70_008: 'SOSM',
MH70_009: 'ECON',
MH70_010: 'IMESS',
MH70_011: 'SOTE',
KH74_001: 'KSV',
KH80_001: 'MAATK',
KH80_002: 'METSAK',
KH80_003: 'ETK',
KH80_004: 'YET',
MH80_001: 'AGRI',
MH80_002: 'AGERE',
MH80_003: 'FOR',
MH80_004: 'FOOD',
MH80_005: 'HNFB',
MH80_006: 'EKM',
MH80_007: 'MMB',
MH80_008: 'emPlant',
KH90_001: 'ELK',
MH90_001: 'ELL',
T920101: 'DPTHEOL',
T920102: 'DPLAW',
T920103: 'DPHISTCULT',
T920104: 'DPLANG',
T920105: 'SKY',
T920106: 'DPSOCS',
T920107: 'PYAM',
T920108: 'DPE',
T920109: 'SEDUCE',
T920110: 'CLIC',
T920111: 'PHILARTSOC',
T921101: 'DPBM',
T921102: 'KLTO',
T921103: 'DocPop',
T921104: 'FINDOS',
T921105: 'DPDR',
T921106: 'ILS',
T921107: 'BM',
T921108: 'CVM',
T921109: 'DPHUB',
T922101: 'LUOVA',
T922102: 'DPPS',
T922103: 'DENVI',
T922104: 'AGFOREE',
T922105: 'MBDP',
T922106: 'FoodHealth',
T923101: 'PAPU',
T923102: 'GeoDoc',
T923103: 'ATM-DP',
T923104: 'CHEMS',
T923105: 'DOMAST',
T923106: 'MATRENA',
T923107: 'DOCS',
} as const
+10
View File
@@ -0,0 +1,10 @@
export const roles = [
'admin',
'courseStatistics',
'facultyStatistics',
'fullSisuAccess',
'katselmusViewer',
'openUniSearch',
'studyGuidanceGroups',
'teachers',
] as const
@@ -1,18 +1,59 @@
const Sequelize = require('sequelize')
const EventEmitter = require('events')
const Umzug = require('umzug')
const conf = require('../conf-backend')
const logger = require('../util/logger')
import EventEmitter from 'events'
import { Sequelize } from 'sequelize-typescript'
import Umzug from 'umzug'
const { SIS_DB_URL } = process.env
import conf from '../config'
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'
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,
@@ -22,6 +63,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,
],
})
}
@@ -30,40 +95,53 @@ class DbConnection extends EventEmitter {
await this.sequelize.authenticate()
this.emit('connect')
this.established = true
} catch (e) {
} catch (error) {
if (attempt > this.RETRY_ATTEMPTS) {
this.emit('error', e)
this.emit('error', error)
return
}
logger.error({ message: `Sis database connection failed! Attempt ${attempt}/${this.RETRY_ATTEMPTS}`, meta: e })
logger.error({
message: `Sis database connection failed! Attempt ${attempt}/${this.RETRY_ATTEMPTS}`,
meta: error,
})
setTimeout(() => this.connect(attempt + 1), 1000 * attempt)
}
}
}
// Old-style kone + user db connections
const sequelizeKone = new Sequelize(conf.DB_URL_KONE, {
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}`)
const sequelizeUser = new Sequelize(conf.DB_URL_USER, {
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 {
@@ -92,12 +170,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
@@ -107,9 +184,4 @@ const initializeDatabaseConnection = async () => {
const dbConnections = new DbConnection()
module.exports = {
dbConnections,
sequelizeKone,
sequelizeUser,
initializeDatabaseConnection,
}
export { dbConnections, sequelizeKone, sequelizeUser, initializeDatabaseConnection }
@@ -1,13 +1,12 @@
module.exports = {
up: async queryInterface => {
return queryInterface.sequelize.transaction(async transaction => {
await queryInterface.sequelize.query('ALTER TABLE tag_student DROP CONSTRAINT "tag_student_tag_id_fkey"', {
transaction,
})
await queryInterface.removeConstraint('tag_student', 'tag_student_tag_id_fkey', { transaction })
await queryInterface.sequelize.query('DELETE FROM tag_student WHERE tag_id IS NULL', { transaction })
await queryInterface.removeConstraint('tag', 'tag_tag_id_key', { transaction })
await queryInterface.addConstraint('tag', ['tag_id'], {
await queryInterface.addConstraint('tag', {
type: 'primary key',
fields: ['tag_id'],
transaction,
})
await queryInterface.sequelize.query(
@@ -15,8 +14,9 @@ module.exports = {
{ transaction }
)
await queryInterface.removeColumn('tag_student', 'id', { transaction })
await queryInterface.addConstraint('tag_student', ['studentnumber', 'tag_id'], {
await queryInterface.addConstraint('tag_student', {
type: 'primary key',
fields: ['studentnumber', 'tag_id'],
transaction,
})
})
@@ -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
@@ -0,0 +1,41 @@
const { ARRAY, STRING } = require('sequelize')
module.exports = {
up: async queryInterface => {
await queryInterface.addColumn('users', 'roles', {
type: ARRAY(STRING),
allowNull: false,
defaultValue: [],
})
await queryInterface.sequelize.query(`
UPDATE users
SET roles = COALESCE(subquery.roles, '{}')
FROM (
SELECT uag."userId" AS "userId", ARRAY_AGG(ag.group_code) AS roles
FROM user_accessgroup uag
JOIN access_groups ag ON uag."accessGroupId" = ag.id
GROUP BY uag."userId"
) AS subquery
WHERE users.id = subquery."userId"
`)
await queryInterface.addColumn('users', 'programme_rights', {
type: ARRAY(STRING),
allowNull: false,
defaultValue: [],
})
await queryInterface.sequelize.query(`
UPDATE users
SET programme_rights = subquery.programme_rights
FROM (
SELECT "userId", ARRAY_AGG("elementDetailCode") AS programme_rights
FROM "user_elementdetails"
GROUP BY "userId"
) AS subquery
WHERE users.id = subquery."userId"
`)
},
down: async queryInterface => {
await queryInterface.removeColumn('users', 'roles')
await queryInterface.removeColumn('users', 'programme_rights')
},
}
@@ -0,0 +1,6 @@
module.exports = {
up: async queryInterface => {
await queryInterface.dropTable('user_faculties')
},
down: async () => {},
}
@@ -0,0 +1,6 @@
module.exports = {
up: async queryInterface => {
await queryInterface.dropTable('user_elementdetails')
},
down: async () => {},
}
@@ -0,0 +1,6 @@
module.exports = {
up: async queryInterface => {
await queryInterface.dropTable('user_accessgroup')
},
down: async () => {},
}
@@ -0,0 +1,6 @@
module.exports = {
up: async queryInterface => {
await queryInterface.dropTable('access_groups')
},
down: async () => {},
}
@@ -0,0 +1,6 @@
module.exports = {
up: async queryInterface => {
await queryInterface.removeColumn('users', 'iam_groups')
},
down: async () => {},
}
@@ -0,0 +1,6 @@
module.exports = {
up: async queryInterface => {
await queryInterface.dropTable('"Sessions"')
},
down: async () => {},
}
@@ -0,0 +1,23 @@
{
"facultyCodes": [
"H10",
"H20",
"H30",
"H40",
"H50",
"H55",
"H57",
"H60",
"H70",
"H74",
"H80",
"H90",
"H92",
"H930",
"H99",
"Y",
"Y01"
],
"magicFacultyCode": "H50",
"ignoredFacultyCodes": ["Y", "H99", "Y01", "H92", "H930"]
}
+28 -41
View File
@@ -1,23 +1,22 @@
const { CronJob } = require('cron')
const { refreshAssociationsInRedis } = require('./services/studyrights')
const { getAllProgrammes } = require('./services/studyrights')
const { updateBasicView, updateStudytrackView } = require('./services/studyprogramme/studyprogrammeUpdates')
const { findAndSaveTeachers } = require('./services/topteachers')
const { faculties } = require('./services/organisations')
const { combinedStudyprogrammes } = require('./services/studyprogramme/studyprogrammeHelpers')
const { isProduction, runningInCI } = require('./config')
const { getFaculties } = require('./services/faculty/facultyHelpers')
const { updateFacultyOverview, updateFacultyProgressOverview } = require('./services/faculty/facultyUpdates')
const { isProduction, runningInCI } = require('./conf-backend')
const { getCurrentSemester } = require('./services/semesters')
const logger = require('./util/logger')
const { getAssociations } = require('./services/studyrights')
const { redisClient } = require('./services/redis')
const { computeLanguageCenterData, LANGUAGE_CENTER_REDIS_KEY } = require('./services/languageCenterData')
const { jobMaker } = require('./worker/queue')
const { deleteOutdatedUsers } = require('./services/userService')
const {
findStudentsCloseToGraduation,
CLOSE_TO_GRADUATION_REDIS_KEY,
} = require('./services/populations/closeToGraduation')
const { redisClient } = require('./services/redis')
const { getCurrentSemester } = require('./services/semesters')
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')
const { deleteOutdatedUsers } = require('./services/userService')
const logger = require('./util/logger')
const { jobMaker } = require('./worker/queue')
const schedule = (cronTime, func) => new CronJob({ cronTime, onTick: func, start: true, timeZone: 'Europe/Helsinki' })
@@ -28,37 +27,22 @@ const refreshStudyrightAssociations = async () => {
const refreshFaculties = async () => {
logger.info('Adding jobs to refresh all faculties')
const facultyList = (await faculties()).filter(f => !['Y', 'H99', 'Y01', 'H92', 'H930'].includes(f.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 (e) {
logger.error({ message: `Failed to update stats for faculty ${code} with type ALL`, meta: `${e}` })
}
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 codes = (await getAllProgrammes())
.map(p => p.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 programmes = await getProgrammesFromStudyRights()
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()
@@ -69,13 +53,12 @@ const refreshProgrammes = async () => {
}
const refreshProgramme = async code => {
const associations = await getAssociations()
await updateBasicView(code, '')
await updateStudytrackView(code, '', associations)
await updateStudytrackView(code, '')
const combinedProgramme = combinedStudyprogrammes[code] || ''
await updateBasicView(code, combinedProgramme)
await updateStudytrackView(code, combinedProgramme, associations)
await updateStudytrackView(code, combinedProgramme)
}
const refreshTeacherLeaderboard = async () => {
@@ -112,7 +95,6 @@ const dailyJobs = () => {
refreshProgrammes()
jobMaker.languagecenter()
jobMaker.statistics()
refreshCloseToGraduating()
}
const startCron = () => {
@@ -122,10 +104,14 @@ const startCron = () => {
logger.info('Running daily jobs from cron')
dailyJobs()
})
// This needs to be its own job because refreshing study right associations in `refreshStatistics` causes some study right info to be not available, causing this job to fail
schedule('0 3 * * *', async () => {
jobMaker.closeToGraduation()
})
schedule('0 4 * * 3', async () => {
logger.info("Deleting users who haven't logged in for 18 months")
const numberOfDeletedUsers = await deleteOutdatedUsers()
logger.info(`Deleted ${numberOfDeletedUsers} users.`)
const [, result] = await deleteOutdatedUsers()
logger.info(`Deleted ${result.rowCount} users.`)
})
schedule('0 19 * * 1', async () => {
logger.info('Updating students whose studyplans have not been updated recently')
@@ -146,4 +132,5 @@ module.exports = {
refreshProgramme,
refreshProgrammes,
refreshLanguageCenterData,
refreshCloseToGraduating,
}
@@ -1,7 +1,8 @@
const { omit } = require('lodash')
const morgan = require('morgan')
const { getFullStudyProgrammeRights } = require('../util')
const logger = require('../util/logger')
const _ = require('lodash')
const { getFullStudyProgrammeRights } = require('../util/utils')
// So this appears to be a hack to get neatly formatted stats like response-time etc. from morgan
// without actually using morgan what it's used for (LOGGING REQUESTS!).
@@ -24,17 +25,18 @@ const accessLogger = morgan((tokens, req, res) => {
`${(tokens['response-time'](req, res) || '0').split('.')[0]} ms`.padEnd(8, ' '),
tokens.status(req, res),
tokens.method(req, res),
tokens.url(req, res),
decodeURIComponent(tokens.url(req, res)),
].join(' ')
const fullStudyProgrammeRights = getFullStudyProgrammeRights(user.programmeRights)
const iamBasedRights = user.programmeRights.filter(right => right.isIamBased)
const usingIamRights = user.iamRights.some(programmeCode => tokens.url(req, res).includes(programmeCode))
const usingIamRights = iamBasedRights.some(right => tokens.url(req, res).includes(right.code))
const onlyIamRights = !user.isAdmin && fullStudyProgrammeRights.length === 0
logger.info(message, {
// don't log student list which might be huge
..._.omit(meta, ['studentsUserCanAccess']),
...omit(meta, ['studentsUserCanAccess']),
// pass this as a custom field so we can filter by it in graylog
isUsageStats: true,
// needed for Grafana IAM users panel
@@ -1,16 +1,18 @@
const Sentry = require('@sentry/node')
const { ApplicationError } = require('../util/customErrors')
const { intersection } = require('lodash')
const { requiredGroup, serviceProvider, configLogoutUrl, isDev } = require('../config')
const { getUser, getMockedUser, getOrganizationAccess } = require('../services/userService')
const { requiredGroup } = require('../conf-backend')
const _ = require('lodash')
const { ApplicationError } = require('../util/customErrors')
const logger = require('../util/logger')
const parseIamGroups = iamGroups => iamGroups?.split(';').filter(Boolean) ?? []
const parseIamGroups = iamGroups => iamGroups?.split(';') ?? []
const hasRequiredIamGroup = (iamGroups, iamRights) =>
_.intersection(iamGroups, requiredGroup).length > 0 || iamRights.length > 0
const hasRequiredIamGroup = (iamGroups, iamRights) => {
return intersection(iamGroups, requiredGroup).length > 0 || iamRights.length > 0
}
const currentUserMiddleware = async (req, _res, next) => {
const toskaUserMiddleware = async (req, _res, next) => {
const {
'shib-session-id': sessionId,
'x-show-as-user': showAsUser,
@@ -36,27 +38,23 @@ const currentUserMiddleware = async (req, _res, next) => {
}
const iamGroups = parseIamGroups(hygroupcn)
const { access = {}, specialGroup = {} } = await getOrganizationAccess({ id: sisId, iamGroups })
const { access = {}, specialGroup = {} } = await getOrganizationAccess(sisId, iamGroups)
const iamRights = Object.keys(access)
if (!hasRequiredIamGroup(iamGroups, iamRights)) {
logger.error({ message: 'User does not have required iam group', meta: { iamGroups, iamRights } })
logger.error({
message: 'User does not have required iam group',
meta: { username, name, email, iamGroups, iamRights },
})
throw new ApplicationError(`User '${username}' does not have required iam group`, 403, { logoutUrl })
}
let user = await getUser({
username,
name,
email,
iamGroups,
iamRights,
specialGroup,
sisId,
access,
})
let user
if (showAsUser && user.isAdmin) {
if (showAsUser && specialGroup?.superAdmin) {
user = await getMockedUser({ userToMock: showAsUser, mockedBy: username })
} else {
user = await getUser({ username, name, email, iamGroups, specialGroup, sisId, access })
}
Sentry.setUser({ username: user.mockedBy ?? username })
@@ -67,4 +65,32 @@ const currentUserMiddleware = async (req, _res, next) => {
next()
}
const fdUserMiddleware = async (req, _res, next) => {
const { remote_user: remoteUser, 'x-show-as-user': showAsUser } = req.headers
if (!remoteUser) {
throw new ApplicationError('Not enough data in request headers, remote_user was missing', 403, { configLogoutUrl })
}
let user
// getMockedUser in production requires the superAdmin-role, which is only available via iamGroups, so it's now only implemented for the dev environment
if (showAsUser && isDev) {
user = await getMockedUser({ userToMock: showAsUser, mockedBy: remoteUser })
} else {
user = await getUser({ username: remoteUser })
}
if (!user) {
throw new ApplicationError(`Could not grant access with the eppn ${remoteUser}.`, 403, { configLogoutUrl })
}
req.user = user
req.logoutUrl = configLogoutUrl
next()
}
const currentUserMiddleware = serviceProvider === 'Toska' ? toskaUserMiddleware : fdUserMiddleware
module.exports = currentUserMiddleware
@@ -1,9 +1,10 @@
const Sentry = require('@sentry/node')
const { ApplicationError } = require('../util/customErrors')
const logger = require('../util/logger')
const errorHandler = (error, _req, res, next) => {
logger.error(`${error.message} ${error.name} ${error.stack}`)
logger.error(error.message, { error })
Sentry.captureException(error)
@@ -1,5 +1,10 @@
const headersMiddleware = require('unfuck-utf8-headers-middleware')
const headers = ['uid', 'displayName', 'shib-session-id', 'hyGroupCn', 'mail', 'hyPersonSisuId']
const serviceProvider = require('../config')
const headers =
serviceProvider === 'Toska'
? ['uid', 'displayName', 'shib-session-id', 'hyGroupCn', 'mail', 'hyPersonSisuId']
: ['remote_user']
module.exports = headersMiddleware(headers)
@@ -0,0 +1,87 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
HasMany,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { EnrollmentType, ExtentCode } from '../types'
import { Organization } from './organization'
import { SISStudyRightElement } from './SISStudyRightElement'
import { Student } from './student'
import { Studyplan } from './studyplan'
import { StudyrightExtent } from './studyrightExtent'
export type SemesterEnrollment = {
type: EnrollmentType
semester: number
statutoryAbsence?: boolean
}
@Table({
underscored: true,
tableName: 'sis_study_rights',
})
export class SISStudyRight extends Model<InferAttributes<SISStudyRight>> {
@PrimaryKey
@Column(DataType.STRING)
id!: string
@ForeignKey(() => Organization)
@Column(DataType.STRING)
facultyCode!: 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
@ForeignKey(() => StudyrightExtent)
@Column(DataType.INTEGER)
extentCode!: ExtentCode
@Column(DataType.STRING)
admissionType!: string
@Column(DataType.JSONB)
semesterEnrollments!: SemesterEnrollment[] | null
@CreatedAt
@Column(DataType.DATE)
createdAt!: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt!: Date
}
@@ -0,0 +1,65 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
import {
BelongsTo,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { Name, Phase, StudyTrack } from '../types'
import { SISStudyRight } from './SISStudyRight'
@Table({
underscored: true,
tableName: 'sis_study_right_elements',
})
export class SISStudyRightElement extends Model<InferAttributes<SISStudyRightElement>> {
@PrimaryKey
@Column(DataType.STRING)
id!: string
@Column(DataType.DATE)
startDate!: Date
@Column(DataType.DATE)
endDate!: Date
@Column(DataType.BOOLEAN)
graduated!: boolean
@Column(DataType.INTEGER)
phase!: Phase
@ForeignKey(() => SISStudyRight)
@Column(DataType.STRING)
studyRightId!: string
@BelongsTo(() => SISStudyRight, { foreignKey: 'studyRightId' })
studyRight!: SISStudyRight
@Column(DataType.STRING)
code!: string
@Column(DataType.JSONB)
name!: Name
@Column(DataType.JSONB)
studyTrack!: StudyTrack | null
@Column(DataType.STRING)
degreeProgrammeType!: string
@CreatedAt
@Column(DataType.DATE)
createdAt!: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt!: Date
}
-66
View File
@@ -1,66 +0,0 @@
const { Model, STRING, DATE, JSONB, BOOLEAN } = 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
+100
View File
@@ -0,0 +1,100 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
DataType,
ForeignKey,
HasMany,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { Name } from '../types'
import { CourseProvider } from './courseProvider'
import { CourseType } from './courseType'
import { Credit } from './credit'
import { Enrollment } from './enrollment'
import { Organization } from './organization'
@Table({
underscored: false,
modelName: 'course',
tableName: 'course',
})
export class Course extends Model<InferAttributes<Course>> {
@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!: Name
@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!: string[]
@Column(DataType.STRING)
course_unit_type!: string
@Column({
field: 'main_course_code',
type: DataType.STRING,
})
mainCourseCode!: string
}
@@ -1,54 +0,0 @@
const { Model, STRING, DATE, JSONB } = 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,42 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
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<InferAttributes<CourseProvider>> {
@PrimaryKey
@Column(DataType.STRING)
composite!: string
@ForeignKey(() => Course)
@Column(DataType.STRING)
coursecode!: string
@Column(DataType.JSONB)
shares!: Array<{ share: number; startDate?: string; endDate?: string }> | null
@ForeignKey(() => Organization)
@Column(DataType.STRING)
organizationcode!: string
@CreatedAt
@Column(DataType.DATE)
createdAt!: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt!: Date
}
-30
View File
@@ -1,30 +0,0 @@
const { Model, STRING, DATE, JSONB } = 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
+31
View File
@@ -0,0 +1,31 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
import { Column, CreatedAt, DataType, HasMany, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { Name } from '../types'
import { Course } from './course'
@Table({
underscored: true,
modelName: 'course_type',
tableName: 'course_types',
})
export class CourseType extends Model<InferAttributes<CourseType>> {
@PrimaryKey
@Column(DataType.STRING)
coursetypecode!: string
@HasMany(() => Course)
courses!: Course[]
@Column(DataType.JSONB)
name!: Required<Name>
@CreatedAt
@Column(DataType.DATE)
createdAt!: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt!: Date
}
-101
View File
@@ -1,101 +0,0 @@
const { Model, DATE, INTEGER, STRING, DOUBLE, BOOLEAN } = 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
+117
View File
@@ -0,0 +1,117 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
import {
BelongsTo,
BelongsToMany,
Column,
CreatedAt,
DataType,
ForeignKey,
Model,
PrimaryKey,
Table,
UpdatedAt,
} from 'sequelize-typescript'
import { CreditTypeCode } from '../types'
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<InferAttributes<Credit>> {
@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,45 +0,0 @@
const { Model, STRING, DATE } = 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,44 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
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<InferAttributes<CreditTeacher>> {
@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
}
-30
View File
@@ -1,30 +0,0 @@
const { Model, INTEGER, DATE, 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
+26
View File
@@ -0,0 +1,26 @@
import { InferAttributes } from 'sequelize'
import { Column, CreatedAt, DataType, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { CreditTypeCode, Name } from '../types'
@Table({
underscored: true,
modelName: 'credit_type',
tableName: 'credit_types',
})
export class CreditType extends Model<InferAttributes<CreditType>> {
@PrimaryKey
@Column(DataType.INTEGER)
credittypecode!: CreditTypeCode
@Column(DataType.JSONB)
name!: Required<Name>
@CreatedAt
@Column(DataType.DATE)
createdAt!: Date
@UpdatedAt
@Column(DataType.DATE)
updatedAt!: Date
}
@@ -1,25 +0,0 @@
const { Model, INTEGER, DATE, 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,34 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
import { Column, CreatedAt, DataType, HasMany, Model, PrimaryKey, Table, UpdatedAt } from 'sequelize-typescript'
import { ElementDetailType, Name } from '../types'
import { StudyrightElement } from './studyrightElement'
@Table({
underscored: false,
modelName: 'element_detail',
tableName: 'element_details',
})
export class ElementDetail extends Model<InferAttributes<ElementDetail>> {
@PrimaryKey
@Column(DataType.STRING)
code!: string
@Column(DataType.JSONB)
name!: Name
@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
}
-77
View File
@@ -1,77 +0,0 @@
const { Model, DATE, INTEGER, STRING, BOOLEAN } = 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
+80
View File
@@ -0,0 +1,80 @@
/* eslint-disable import/no-cycle */
import { InferAttributes } from 'sequelize'
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<InferAttributes<Enrollment>> {
@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,37 +0,0 @@
const { Model, INTEGER, STRING, DATE } = 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
-145
View File
@@ -1,145 +0,0 @@
const Organization = require('./organization')
const CourseType = require('./courseType')
const Course = require('./course')
const Student = require('./student')
const CourseProvider = require('./courseProvider')
const Semester = require('./semester')
const SemesterEnrollment = require('./semesterEnrollment')
const Teacher = require('./teacher')
const CreditType = require('./creditType')
const Credit = require('./credit')
const CreditTeacher = require('./creditTeacher')
const ElementDetail = require('./elementDetail')
const Studyplan = require('./studyplan')
const StudyrightExtent = require('./studyrightExtent')
const Studyright = require('./studyright')
const StudyrightElement = require('./studyrightElement')
const Transfer = require('./transfer')
const ProgrammeModule = require('./programmeModule')
const ProgrammeModuleChild = require('./programmeModuleChild')
const ExcludedCourse = require('./excludedCourse')
const Enrollment = require('./enrollment')
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' })
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' })
StudyrightElement.belongsTo(Studyright, { foreignKey: 'studyrightid', targetKey: 'studyrightid' })
Studyright.hasMany(StudyrightElement, { foreignKey: 'studyrightid', sourceKey: 'studyrightid' })
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 = {
Organization,
Course,
CourseType,
Student,
CourseProvider,
Semester,
SemesterEnrollment,
Teacher,
CreditType,
Credit,
CreditTeacher,
ElementDetail,
Studyplan,
StudyrightExtent,
Studyright,
StudyrightElement,
Transfer,
ProgrammeModule,
ProgrammeModuleChild,
ExcludedCourse,
Enrollment,
}
+53
View File
@@ -0,0 +1,53 @@
import { CreditTypeCode } from '../types'
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 { Semester } from './semester'
import { SemesterEnrollment } from './semesterEnrollment'
import { SISStudyRight } from './SISStudyRight'
import { SISStudyRightElement } from './SISStudyRightElement'
import { Student } from './student'
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,26 @@
import { CreationOptional, InferAttributes, InferCreationAttributes } from 'sequelize'
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<
InferAttributes<CustomPopulationSearch>,
InferCreationAttributes<CustomPopulationSearch>
> {
@PrimaryKey
@AutoIncrement
@Column(DataType.BIGINT)
id!: CreationOptional<bigint>
@Column(DataType.BIGINT)
userId!: bigint
@Column(DataType.STRING)
name!: string
@Column(DataType.ARRAY(DataType.STRING))
students!: string[]
}
@@ -0,0 +1,28 @@
import { InferAttributes } from 'sequelize'
import { Column, DataType, Model, PrimaryKey, Table } from 'sequelize-typescript'
@Table({
underscored: true,
modelName: 'excluded_course',
tableName: 'excluded_courses',
})
export class ExcludedCourse extends Model<InferAttributes<ExcludedCourse>> {
@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,27 @@
import { CreationOptional, InferAttributes, InferCreationAttributes } from 'sequelize'
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<
InferAttributes<OpenUniPopulationSearch>,
InferCreationAttributes<OpenUniPopulationSearch>
> {
@PrimaryKey
@AutoIncrement
@Column(DataType.BIGINT)
id!: CreationOptional<bigint>
@Column(DataType.BIGINT)
userId!: bigint
@Column(DataType.STRING)
name!: string
@Column(DataType.ARRAY(DataType.STRING))
courseCodes!: string[]
}
@@ -0,0 +1,53 @@
import { InferAttributes } from 'sequelize'
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<InferAttributes<ProgressCriteria>> {
@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,25 @@
import { InferAttributes } from 'sequelize'
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<InferAttributes<StudyGuidanceGroupTag>> {
@PrimaryKey
@AutoIncrement
@Column(DataType.BIGINT)
id!: bigint
@Unique
@Column(DataType.STRING)
studyGuidanceGroupId!: string
@Column(DataType.STRING)
studyProgramme!: string
@Column(DataType.STRING)
year!: string
}

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