Compare commits

...
817 Commits
Author SHA1 Message Date
Riku Rauhala 6bf083cc89 [Course statistics] Add retries to a flaky test 2024-11-27 09:09:47 +02:00
Riku Rauhala c88d5984ef [Navigation bar] Turn the user button orange when mocking 2024-11-27 08:49:33 +02:00
Riku Rauhala e328258071 [Shared] Extract type ProgrammeMedians 2024-11-26 21:10:58 +02:00
Riku Rauhala 37a16b54e1 [University] Type AllProgressStats response and request 2024-11-26 21:06:04 +02:00
Riku Rauhala 4729fa457c [Frontend] Move graduation time colors to theme 2024-11-26 20:16:14 +02:00
Riku RauhalaandGitHub 4b37fd4536 Merge pull request #4688 from UniversityOfHelsinkiCS/material-ui-university
Material UI: University
2024-11-26 19:54:22 +02:00
Riku Rauhala ec5cfcc6c5 [University] Update tests 2024-11-26 19:45:18 +02:00
Riku Rauhala b41846a51e [TypeScript] Fix issues 2024-11-25 22:06:06 +02:00
Riku Rauhala 5b866377a9 [University] Disable toggles while errors or loading 2024-11-25 20:44:02 +02:00
Riku Rauhala febba2752c [Evaluation Overview] Remove UniversityPage 2024-11-25 20:42:51 +02:00
Riku Rauhala 71f733817a [Section] Add a loading spinner on top of the skeleton 2024-11-25 20:41:41 +02:00
Riku Rauhala 82d2885d99 [University] Improve faculty graduations 2024-11-25 19:44:36 +02:00
dad7ddc1f2 [CourseStatistics, StudyProgramme, Teachers] Added serviceProvider checks to hide some tables, tabs and routes when not running in toska enviroment (#4685)
* [CourseStatistics, StudyProgramme, Teachers] Added serviceProvider checks to hide some tables and tabs when not running in toska enviroment

* [StudentsTable] Use cloneDeep for toskaColumns to prevent mutation

* [Frontend] Replace serviceProvider checks with isDefaultServiceProvider function for consistency

---------

Co-authored-by: Valtteri Kantanen <valtteri.kantanen@iki.fi>
2024-11-25 17:04:05 +02:00
Riku Rauhala ee9c2c8eb8 [University] Improve Faculty graduations 2024-11-25 15:31:40 +02:00
Riku Rauhala 6c9b11a105 [Language] Type useLanguage 2024-11-25 11:48:30 +02:00
Riku Rauhala afa52f7e54 [University] Rewrite Faculty graduations tab 2024-11-25 10:51:03 +02:00
Riku Rauhala f2865b9145 [Faculty stats] Type allgraduationstats query 2024-11-25 10:39:14 +02:00
Riku Rauhala 0514f4fdfa [Shared] Extract more types 2024-11-25 10:37:21 +02:00
Riku Rauhala 170a2e40e1 [University] Extract tabs and improve query param handling 2024-11-22 21:47:18 +02:00
Riku Rauhala 5849e89ac6 [University] Type ProgrammeProgressChart 2024-11-22 21:35:27 +02:00
Riku Rauhala bc6cccfe03 [University] Move ba+ma progress message to info box 2024-11-22 20:48:42 +02:00
Riku Rauhala 53d1890a4a [University] Improve Faculty progress tab 2024-11-22 20:39:32 +02:00
Riku Rauhala 4a93c4dce4 [University] Implement FacultyProgressTable in Material UI 2024-11-22 20:18:51 +02:00
Riku Rauhala be79d8bb3f [University] Type FacultyBarChart 2024-11-22 18:00:55 +02:00
Riku Rauhala 97df3bee41 [Frontend] Install types for Highcharts 2024-11-22 14:08:54 +02:00
Riku Rauhala e1c4e792f1 [University] Add loading and error handling for Faculty progress tab 2024-11-22 14:00:22 +02:00
Riku Rauhala 230d07cbde [University] Separate the main sections into tabs 2024-11-22 13:45:29 +02:00
Riku Rauhala 92f848bf21 [University] Begin converting to Material UI 2024-11-21 19:07:06 +02:00
Riku Rauhala ae8aeede1b [Toggle] Create a new version with MUI 2024-11-21 11:46:23 +02:00
Riku Rauhala 2db12dcaac [InfoBox] Add a mini variant with smaller button 2024-11-21 11:40:08 +02:00
Riku Rauhala 9672b542b8 [Section] Create new Section wrapper
Contains a title of the section and an optional InfoBox
2024-11-21 11:27:52 +02:00
Riku Rauhala e6a8946b65 [InfoBox] Create a new version with MUI 2024-11-21 11:24:01 +02:00
Valtteri Kantanen 3b0ccb5424 [Changelog] Fix time of release
Previously the date of the latest commit in the release was used instead of when the release was published
2024-11-21 11:23:11 +02:00
Valtteri Kantanen 0c54ef578f [Custom population search] Fix unselecting a saved population 2024-11-19 13:07:47 +02:00
Valtteri Kantanen 4ee0f74361 Add ESLint rule for 'prefer-const'
https://eslint.org/docs/latest/rules/prefer-const
2024-11-19 13:07:05 +02:00
Valtteri Kantanen 362850c2de [Class statistics] Display additional info in Credit accumulation graph if 'show full study path' is chosen
Now all credits that have been attained during the selected education will be shown. Also all graduations in the correct study right will be shown in the graph.
2024-11-19 13:06:13 +02:00
Valtteri Kantanen 42c41345a0 Upgrade Node.js version to 22 in all packages 2024-11-19 10:45:27 +02:00
Riku Rauhala 301208cf99 [Front page] Only display feedback link for HY 2024-11-19 10:40:42 +02:00
Riku Rauhala b173e27f70 [Footer] Update timestamp format and add release version 2024-11-19 10:14:01 +02:00
Riku Rauhala 1bdbec963b [Front page] Center notification icon vertically 2024-11-18 22:00:32 +02:00
Riku Rauhala 80cf4acf01 [AccessDeniedMessage] Use the Alert component instead 2024-11-18 21:59:14 +02:00
Riku Rauhala fdd5cbd2e5 [Front page] Add a temporary notification about new UI 2024-11-18 21:52:05 +02:00
Riku RauhalaandGitHub 38e398598f Merge pull request #4680 from UniversityOfHelsinkiCS/material-ui-footer
Material UI: Footer
2024-11-18 17:49:38 +02:00
Riku Rauhala 0b01d223ae [Frontend] Delete ProtectedRoute.jsx
Replaced by .tsx
2024-11-18 17:39:17 +02:00
Riku Rauhala c4fd257b42 [Frontend] Restore theme 2024-11-18 17:37:52 +02:00
Riku Rauhala e7ee8749c8 [Footer] Update JSDoc 2024-11-18 17:33:33 +02:00
Riku Rauhala fb21d171ff [Footer] Fix alignment 2024-11-18 17:24:07 +02:00
Riku Rauhala a6905313c3 [Changelog] Remove redundant elevation prop 2024-11-18 17:24:07 +02:00
Riku Rauhala 7e8e5bc2e5 [Access denied] Use the previous icon 2024-11-18 17:24:07 +02:00
Riku Rauhala c84259e67a [Footer] Extract logo component and make responsive 2024-11-18 17:24:07 +02:00
Riku Rauhala 63ccd5adc1 [Footer] Implement initial version 2024-11-18 17:24:07 +02:00
Riku Rauhala 2d7bc42a9e [Frontend] Update AccessDenied message to Material UI 2024-11-18 17:24:07 +02:00
Riku Rauhala 0c6077d287 [Documentation] Add Material icons to resources.md 2024-11-18 17:20:13 +02:00
Riku Rauhala cf24936bc9 [Backend] Move type Role to shared 2024-11-18 17:20:13 +02:00
Valtteri Kantanen d0134192c6 [Filters] Fix tests 2024-11-18 17:14:51 +02:00
Valtteri Kantanen f7609dca3d [Teachers] Grant access to more users 2024-11-18 16:16:42 +02:00
Riku RauhalaandGitHub 4add4409eb Merge pull request #4679 from UniversityOfHelsinkiCS/material-ui-frontpage
Material UI: Front page
2024-11-15 17:16:30 +02:00
Riku Rauhala 2b5bf09514 [PageTitle] Tweak subtitle 2024-11-15 17:08:39 +02:00
Riku Rauhala 8ea1308c77 [Frontend] Remove build at
Each release has an associated time stamp now. This could still be used in a possible footer?
2024-11-15 16:59:51 +02:00
Riku Rauhala faba796303 [FrontPage] Remove old Semantic UI implementation 2024-11-15 16:56:21 +02:00
Riku Rauhala 04715e4503 [Changelog] Create a new page for changelog 2024-11-15 16:49:06 +02:00
Riku Rauhala 39b77e43d7 [Frontend] Update FrontPage route 2024-11-15 16:49:06 +02:00
Riku Rauhala b6a99dda83 [Backend] Update fake changelog data 2024-11-15 16:49:06 +02:00
Riku Rauhala ff25524d32 [Frontpage] Rewrite with Material UI 2024-11-15 16:49:06 +02:00
Riku Rauhala d37228e68e [Changelog] Rename ChangelogData to Release 2024-11-15 16:49:06 +02:00
Riku Rauhala 779c1a9627 [Changelog] Extract ChangelogData and type changelogApi 2024-11-15 16:49:06 +02:00
Riku Rauhala f6d8ca87f1 [Redux] Convert changelogApi to TypeScript 2024-11-15 16:49:06 +02:00
Riku Rauhala 1903f74764 [PageTitle] Center by default and add optional subtitle 2024-11-15 16:49:06 +02:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
181d39a9f9 Update all non-major dependencies (#4672)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-15 13:05:40 +02:00
Valtteri Kantanen f16b211a88 [Updater] Add error event listeners for Redis 2024-11-15 12:21:07 +02:00
Valtteri Kantanen 71f3abcf3e [Close to graduation] Add earliest HOPS attainment and number of absent semesters 2024-11-14 16:56:49 +02:00
Valtteri Kantanen 32197643ef [Study programme overview] Fix tests 2024-11-14 15:56:54 +02:00
Valtteri Kantanen d84a02d659 Fix a couple of ESLint errors 2024-11-14 15:42:29 +02:00
Valtteri Kantanen 5d2b52cfaf [Curriculum picker] Fetch curriculum periods from db instead of hardcoding them 2024-11-14 15:41:46 +02:00
Valtteri Kantanen 3d09abb518 [Faculties] Add retries to a flaky test 2024-11-14 14:14:24 +02:00
Valtteri Kantanen a69c0b6d00 Always run 'Check services run correctly' job in GitHub Actions 2024-11-14 14:00:06 +02:00
Valtteri Kantanen c7185bf3d7 [Updater] Check that the teacher exists before trying to create a new creditTeacher 2024-11-14 13:58:40 +02:00
Riku Rauhala 920895c583 [StatusNotification] Increase autoHideDuration to 30 seconds 2024-11-14 13:26:20 +02:00
Valtteri Kantanen f277ca758d [Filters] Add retries for a flaky age filter test 2024-11-14 13:24:13 +02:00
Valtteri Kantanen afffd17485 [Frontend] Take type for StatusNotification severity from MUI 2024-11-14 13:22:56 +02:00
Valtteri Kantanen 3b83a59b1a Ensure tests always run regardless of previous job outcomes 2024-11-14 12:14:04 +02:00
Valtteri Kantanen f92dab550d Re-enable ESLint rule 2024-11-14 10:24:33 +02:00
Riku Rauhala 7b8a160cc9 [Documentation] Add link to MUI docs in resources.md 2024-11-13 12:45:37 +02:00
Riku Rauhala 44f40373cc [Feedback] Acually make the success message work 2024-11-12 21:13:59 +02:00
Riku RauhalaandGitHub 2ab3dbf861 Merge pull request #4678 from UniversityOfHelsinkiCS/renovate/migrate-config
Migrate renovate config
2024-11-12 12:51:49 +02:00
renovate[bot]andGitHub be10cefba7 Migrate config renovate.json 2024-11-12 10:31:25 +00:00
Riku RauhalaandGitHub 3d0b6cf864 Merge pull request #4677 from UniversityOfHelsinkiCS/material-ui-feedback
Material UI: Feedback
2024-11-12 11:27:46 +02:00
Riku Rauhala af9ceda816 [ESLint] Update rules 2024-11-12 11:16:04 +02:00
Riku Rauhala ec58964032 Merge branch 'master' into material-ui-feedback 2024-11-12 11:10:16 +02:00
Riku Rauhala b3f7cccae4 [Feedback] Update modal button disabling logic 2024-11-12 10:21:17 +02:00
Riku Rauhala 73ff9f585b [Feedback] Remove old Semantic UI component 2024-11-12 10:20:49 +02:00
Riku Rauhala 1c81efd4b6 [Feedback] Rewrite with Material UI components 2024-11-12 10:16:09 +02:00
Valtteri KantanenandGitHub bdda5dcf01 Migrate ESLint to v9 (#4676) 2024-11-11 16:08:16 +02:00
Valtteri Kantanen cd2cd18eea Ignore node minor and patch updates on Renovate 2024-11-11 13:22:17 +02:00
Riku Rauhala a7810cf774 [Documentation] Add Code Climate screenshots 2024-11-11 12:35:54 +02:00
Riku RauhalaandGitHub b17b119a59 Merge pull request #4675 from UniversityOfHelsinkiCS/material-ui
Material UI: Navigation bar
2024-11-11 12:17:26 +02:00
Riku Rauhala eb60d1bc5d [Frontend] Delete old NavigationBar component 2024-11-11 11:59:34 +02:00
Riku Rauhala 3c09f2186d [Users] Update tests 2024-11-11 11:57:40 +02:00
Riku Rauhala 9ef0d8b59b [Navigation bar] Mark current paths as active 2024-11-11 09:54:53 +02:00
Riku Rauhala 9aa4150b9a [Navigation bar] Add arrows for items with sub-items 2024-11-08 23:37:38 +02:00
Riku Rauhala 0c5cac1c8c [Navigation bar] Rename logout to log out
Logout is a noun, log out is a verb
2024-11-08 23:20:21 +02:00
Riku Rauhala a52997fbd4 [NavigationBar] Hide admin button from non-admins 2024-11-08 23:16:20 +02:00
Riku Rauhala b6d68f6b3d [NavigationBar] Implement mocking 2024-11-08 22:55:46 +02:00
Riku Rauhala 5e750a4f40 [NavigationBar] Make the buttons functional 2024-11-08 22:17:47 +02:00
Riku Rauhala 0b81a0dbf5 [Navigation bar] Implement language selection 2024-11-08 21:01:08 +02:00
Riku Rauhala 12996de95a [Navigation bar] Implement initial MUI version 2024-11-08 18:45:27 +02:00
Riku Rauhala 7995635034 [Frontend] Install Material UI and other dependencies 2024-11-08 17:11:23 +02:00
Valtteri Kantanen b4e176a1a6 [Updater] Replace unmaintained winston-sentry-log with winston-transport-sentry-node 2024-11-08 12:10:23 +02:00
Valtteri Kantanen ce982e38d0 Add 'dependencies' label for Renovate PRs 2024-11-08 12:09:11 +02:00
Valtteri Kantanen 6c1f49838b [Updater] Include Sentry env variables in GHA workflow 2024-11-08 12:08:51 +02:00
Valtteri Kantanen 3739a17b78 Refactor GHA workflow to check for new migration files before downloading and building images 2024-11-07 14:41:59 +02:00
Valtteri Kantanen a1ecc12042 [Faculties] Fix tests 2024-11-07 13:59:33 +02:00
Riku Rauhala e78f2d64a4 [Course statistics] Update tests 2024-11-07 13:47:23 +02:00
Riku Rauhala 6991713112 [AutoSubmitSearchInput] Convert to TypeScript and remove unnecessary props 2024-11-07 13:47:23 +02:00
Valtteri Kantanen 60fc1d135d [Class statistics] Display 'Show full study path' toggle for admins 2024-11-07 13:41:11 +02:00
Valtteri Kantanen 89cc24fb81 Update patch versions in package.json files 2024-11-07 13:34:49 +02:00
Valtteri Kantanen 1243e09dfe [Backend] Upgrade Sentry from v5 to v8 2024-11-07 13:09:11 +02:00
Riku Rauhala 0dff5d89c0 [Course statistics] Remove new feature message
This feature is already 9 months old
2024-11-07 13:00:07 +02:00
Riku Rauhala b3cc8e3054 [Filters] Fix tests 2024-11-07 12:40:36 +02:00
Riku Rauhala b89f8a33f1 [Frotend] Use more consistent spelling of study right 2024-11-07 12:16:17 +02:00
Valtteri Kantanen 5279a1f6b7 Automatically merge non-major dependency updates after the tests have passed 2024-11-07 12:13:28 +02:00
Valtteri Kantanen 6f853d0248 [Updater] Replace uuid with node:crypto 2024-11-07 11:58:57 +02:00
Riku Rauhala bf8aec914e [Class statistics] Test study track selection 2024-11-07 10:56:09 +02:00
Valtteri Kantanen e190a66f6e Fix loading the updater images in GHA workflow 2024-11-07 10:50:36 +02:00
Valtteri Kantanen dec58cba25 [Updater] Debug loading the Docker image in GHA workflow 2024-11-07 10:44:02 +02:00
Valtteri Kantanen a7743cfe06 Upgrade actions/upload-artifact and actions/download-artifact to v4 for the rest of the files 2024-11-07 10:36:47 +02:00
Riku Rauhala 7e2f6b212f [Study guidance groups] Remove graduation filter
Graduated students are removed from study guidance groups so there is no need for this filter here
2024-11-07 10:22:16 +02:00
Valtteri Kantanen eb2665b034 Fix loading the backend image in GHA workflow 2024-11-07 10:14:19 +02:00
Valtteri Kantanen e5eb58aee6 Fix downloading the backend image in GHA workflow 2024-11-07 10:09:49 +02:00
Valtteri Kantanen cb820fd2e9 Try upgrading actions/upload-artifact to v4 in test.yaml 2024-11-07 10:03:31 +02:00
Valtteri Kantanen d3bc063e39 Try upgrading actions/upload-artifact to v4 2024-11-06 16:58:24 +02:00
Valtteri Kantanen fe25985eec [Class statistics] Capture the full stack trace of errors instead of creating a new one 2024-11-06 15:58:29 +02:00
Valtteri Kantanen 361d5da6a4 [Class statistics] Fix syntax error in SQL query caused by casing inconsistencies 2024-11-06 15:57:37 +02:00
Valtteri Kantanen 7fc06a5534 [Faculties] Refactor programme sorting in interactive data tables 2024-11-05 17:31:17 +02:00
Riku RauhalaandGitHub 98bcf78fc7 Merge pull request #4671 from UniversityOfHelsinkiCS/graduation-times-percentages
Hide percentages for bachelor + master and master study rights
2024-11-05 13:30:27 +02:00
Riku Rauhala fc42613625 [Faculties] Update tests 2024-11-05 13:23:37 +02:00
Riku Rauhala 62419f0fe4 [Faculties] Type new function calculateStats 2024-11-05 13:13:46 +02:00
Riku Rauhala ad6bd96af6 [TSConfig] Update ES2020 lib to ES2023 2024-11-05 13:06:24 +02:00
Riku Rauhala cad1fe590c Merge branch 'master' into graduation-times-percentages 2024-11-05 13:01:12 +02:00
Riku Rauhala f911ae9d1c [Graduation times] Display percentages only for bachelor study rights 2024-11-05 12:58:02 +02:00
Riku Rauhala 8c05c1c2f3 [StudyTrackOverview] Convert most components to TypeScript 2024-11-05 12:58:02 +02:00
Riku Rauhala b2a92d9003 [ProgressOfStudents] Convert to TypeScript 2024-11-05 12:58:02 +02:00
Riku Rauhala 44d15b5ed8 [facultyHelpers] Convert to TypeScript 2024-11-05 12:58:02 +02:00
Riku Rauhala 6468aaff87 [SegmentDimmer] Convert to TypeScript 2024-11-05 12:58:02 +02:00
Riku Rauhala cbf8663997 [Graduation times] Display percentages only for bachelor study rights 2024-11-05 12:55:33 +02:00
Valtteri Kantanen 388a605937 [Faculties] Move calculateStats into facultyHelpers to enable hot reloading 2024-11-05 12:36:33 +02:00
Riku Rauhala 80cffc5fbf [StudyTrackOverview] Convert most components to TypeScript 2024-11-05 11:32:15 +02:00
Valtteri Kantanen 0e66802c32 [Close to graduation] Use degree programme types to determine programme type 2024-11-05 10:54:44 +02:00
Riku Rauhala 49da696a0b [ProgressOfStudents] Convert to TypeScript 2024-11-05 10:26:52 +02:00
Riku Rauhala 517638a44e [facultyHelpers] Convert to TypeScript 2024-11-05 10:01:47 +02:00
Riku Rauhala abcd00cbad [SegmentDimmer] Convert to TypeScript 2024-11-05 10:00:41 +02:00
Valtteri Kantanen 3a697b94b1 [Students] Use an HTTP status code instead of throwing an error if student is not found or user does not have permission to view student's data 2024-11-05 09:57:35 +02:00
Riku Rauhala 807e5fb2f6 [YearFilter] Convert to TypeScript 2024-11-05 09:29:48 +02:00
Riku Rauhala 4e74a157d7 [Study programme] Update tests 2024-11-04 18:51:56 +02:00
Riku Rauhala 4ffde19997 [Frontend] Fix broken infobox contents 2024-11-04 16:57:45 +02:00
Riku Rauhala 40f3713130 [HoverableHelpPopup] Convert to TypeScript 2024-11-04 16:46:53 +02:00
Riku Rauhala 81d4921bf0 [Toggle] Convert to TypeScript 2024-11-04 16:45:58 +02:00
Riku RauhalaandGitHub 5a508636a0 Merge pull request #4670 from UniversityOfHelsinkiCS/ts-low-hanging
[Frontend] Low hanging TypeScript
2024-11-04 15:14:52 +02:00
Riku Rauhala 77b7fd960d Merge branch 'master' into ts-low-hanging 2024-11-04 14:59:15 +02:00
Riku Rauhala c2a1f25d8d [ProgrammeDropdown] Refactor and convert to TypeScript 2024-11-04 14:56:52 +02:00
Riku Rauhala 1ec3e78ebe [CompletedCoursesSearch] Convert to TypeScript 2024-11-04 14:52:03 +02:00
Riku Rauhala 27c071dca4 [SisuLinkItem] Convert to TypeScript 2024-11-04 14:51:07 +02:00
Valtteri Kantanen 7432f59a81 [Updater] Fix is_study_module column in course table 2024-11-04 13:48:22 +02:00
Valtteri Kantanen 4da174c5de [Close to graduation] Display when the data was last updated 2024-11-04 13:46:45 +02:00
Valtteri Kantanen 78f894660b [Close to graduation] Include timestamp of last update in data 2024-11-04 13:44:07 +02:00
Valtteri Kantanen 3ed1b9aaaf Add name for lint_backend_and_frontend job 2024-11-04 13:43:06 +02:00
Valtteri Kantanen df6175fb75 [Close to graduation] Use study right instead of course provider to identify thesis attainments 2024-11-04 13:42:46 +02:00
Riku Rauhala dbf176ff2c [Cypress] Update tests 2024-11-04 13:05:08 +02:00
Riku Rauhala f9398f8707 [ESLint] Disable no-unused-imports for js/jsx files
This is causing issues when importing from JavaScript into TypeScript files. This can be enabled again when there is a sufficient enough TypeScript coverage for the frontend
2024-11-04 12:49:17 +02:00
Riku RauhalaandGitHub fa05ca2bf8 Merge branch 'master' into ts-low-hanging 2024-11-04 11:48:52 +02:00
Riku Rauhala 25dcd4f009 [Shared] Update tsconfig 2024-11-04 11:40:59 +02:00
Riku Rauhala 1ac1f01c41 [CourseFilterToggle] Convert to TypeScript 2024-11-04 11:40:24 +02:00
Riku RauhalaandGitHub 85a64bdd2e Merge pull request #4668 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-11-04 10:06:15 +02:00
renovate[bot]andGitHub 911f1c6156 Update all non-major dependencies 2024-11-04 07:28:06 +00:00
Riku Rauhala 70f937dc74 [Shared] Move type Name from backend to shared 2024-11-01 17:36:33 +02:00
Riku Rauhala 191dd7c148 [FilterOldProgrammesToggle] Convert into TypeScript 2024-11-01 17:19:59 +02:00
Riku Rauhala caf62699f1 [FilterToggleIcon] Replace css with inline styling 2024-11-01 16:47:57 +02:00
Riku Rauhala 344dafdc88 [FilterToggleIcon] Convert to TypeScript 2024-11-01 16:41:29 +02:00
Riku Rauhala 4b79059809 [Frontend] Convert constants into TypeScript 2024-11-01 16:30:56 +02:00
Riku Rauhala 24d8a157b3 [Frontend] Extract common function formatContent 2024-11-01 16:15:52 +02:00
Riku Rauhala 05551f1f14 [InfoToolTips] Rename with camelCase for consistency 2024-11-01 16:03:55 +02:00
Valtteri Kantanen d075a1c24b [Study programme overview] Generalize doCombo 2024-11-01 14:54:53 +02:00
Riku Rauhala 82a9fac4cb [InfoToolTips] Convert to TypeScript 2024-11-01 10:03:46 +02:00
Riku Rauhala 1cb4a94772 [Population search] Test Study track selector 2024-10-31 20:52:52 +02:00
Riku RauhalaandGitHub da84c544c4 Merge pull request #4667 from UniversityOfHelsinkiCS/study-track-selector
Population search: Sort study tracks
2024-10-31 20:34:50 +02:00
Riku Rauhala 0c1959dfb8 [Population search] Sort study tracks 2024-10-31 20:26:09 +02:00
Valtteri Kantanen 5240a4bbe9 [Population search] Skip study tracks query if programme is null 2024-10-31 13:22:31 +02:00
Riku Rauhala 5277f71dd9 [InfoBox] Convert to TypeScript 2024-10-31 11:46:21 +02:00
Riku Rauhala a1610d15a9 [ESLint] Disable require-default-props rules for TypeScript 2024-10-31 11:44:03 +02:00
Riku RauhalaandGitHub 6a9d92e1f3 Merge pull request #4666 from UniversityOfHelsinkiCS/study-track-selector
[Class statistics] Add selector for study tracks in population search
2024-10-30 20:05:03 +02:00
Riku Rauhala 6808bfddc7 [Population search] Fix logic for clearing the selections 2024-10-30 19:56:22 +02:00
Riku Rauhala 062b81d537 [Population search] Update info box text
Add mention of the new study track selector
2024-10-30 19:31:22 +02:00
Riku Rauhala 48f5c58a3f [Population search] Replace LoadingIcons with disabling inputs
Prevents layout shift and disallows selecting a study track if there are no available study tracks
2024-10-30 19:15:39 +02:00
Riku Rauhala 943b78def4 [Population search] Extract LoadingIcon 2024-10-30 18:27:36 +02:00
Riku Rauhala 2a5f47da36 [Search history] Update styling 2024-10-30 18:11:58 +02:00
Valtteri KantanenandGitHub 0bdd010b2b [Class statistics] First version of 'Show full study path' feature 2024-10-30 16:55:26 +02:00
Riku Rauhala 576051dae3 [Class statistics] Remove redundant date from search history 2024-10-30 15:44:12 +02:00
Riku Rauhala fc70a24b41 [Class statistics] Remove error message from the search from 2024-10-30 15:28:08 +02:00
Riku Rauhala 84504ab0d2 [Class statistics] Add initial form for selecting study tracks 2024-10-30 15:22:17 +02:00
Riku Rauhala f2a8672c22 [Study programme] Add route for study tracks of a programmec 2024-10-30 14:23:56 +02:00
Valtteri Kantanen 65c6319e87 Revert "[Class statistics] First version of 'Show full study path' feature"
This reverts commit 3244499fc6.
2024-10-30 10:33:26 +02:00
Riku Rauhala 78e3957541 [Frontend] Rename useGetStudyTrackStatsQuery 2024-10-29 21:05:15 +02:00
Riku Rauhala 37fcd15f06 [Study programme] Rename a bunch of stuff 2024-10-29 20:08:44 +02:00
Valtteri Kantanen 3244499fc6 [Class statistics] First version of 'Show full study path' feature 2024-10-29 15:54:42 +02:00
Riku Rauhala 07c25cb9af [Backend] Rename a bunch of things for consistency 2024-10-29 15:26:38 +02:00
Riku Rauhala c9592ba954 [Study programmes] Fix crashing when excluding graduated 2024-10-29 14:15:44 +02:00
Riku RauhalaandGitHub 34a479c1f6 Merge pull request #4662 from UniversityOfHelsinkiCS/misc-updates
Misc updates
2024-10-29 09:03:22 +02:00
Riku Rauhala f34cc2cd44 [Study programme] Update Cypress attributes
[Study programme] Update tests

[Study programme] Update data-cy

[Evaluation overview] Update tests

[Evaluation overview] Update tests
2024-10-29 08:47:34 +02:00
Riku Rauhala 90beeda177 [Study programme] Update tests 2024-10-28 16:16:12 +02:00
Valtteri Kantanen 2dc74af290 [Close to graduation] Fix semester enrollments in Excel export 2024-10-28 14:57:49 +02:00
Riku Rauhala f488019790 [Update statistics] Make the loader match other status icons 2024-10-28 13:06:18 +02:00
Riku Rauhala 7fe7557f7c [Study programme] Simplify Update buttons
The buttons contain only the text 'Update' in the Update statistics tab for Faculties, let's make it consistent with it
2024-10-28 13:02:36 +02:00
Riku Rauhala feca2bbe0a [UpdateView] Extract common component UpdateStatusIcon 2024-10-28 12:55:25 +02:00
Riku Rauhala 179f9737be [Study programme] Clarify button text in Update statistics tab
It was not clear which tab/view this button was referring to
2024-10-28 12:28:02 +02:00
Riku Rauhala c2653c96a7 [Study programme] Fix infobox texts 2024-10-28 12:27:00 +02:00
Valtteri Kantanen d83a36206d [Class statistics] Refactor population search form 2024-10-28 12:13:30 +02:00
Riku Rauhala 6cdcbb5f5f [Frontend] Make spelling of study track / right more consistent 2024-10-28 11:44:57 +02:00
Riku RauhalaandGitHub 8337e9ac94 Merge pull request #4661 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-10-28 09:09:56 +02:00
renovate[bot]andGitHub 18fd991175 Update all non-major dependencies 2024-10-28 06:57:27 +00:00
Riku Rauhala cb3e953be3 [Population search] Extract common getMonths function 2024-10-24 14:00:52 +03:00
Riku Rauhala d45d2c5922 [Population search] Upgrade from connect to useSelector 2024-10-24 14:00:52 +03:00
Riku Rauhala 3f0e1d947a [PopulationSearchForm] Upgrade to useDispatch and useSelector 2024-10-24 14:00:52 +03:00
Riku Rauhala 55a475a322 [Population search] Upgrade from connect to useSelector 2024-10-24 14:00:52 +03:00
Riku Rauhala e4c703b877 [Population search] Update error message 2024-10-24 14:00:52 +03:00
Valtteri Kantanen f8496edcc4 [Class statistics] Fix a bug where change in filtered students didn't trigger a new request for course stats
Also added two tests for this scenario
2024-10-24 13:28:56 +03:00
Valtteri Kantanen 59cb4638ce [Updater] Remove startdate, enddate and latest_instance_date from Course model 2024-10-24 09:55:35 +03:00
Riku Rauhala 8a7426ae02 [Shared] Move formatQueryParamsToArrays to shared/util 2024-10-24 09:41:58 +03:00
Riku Rauhala 3d5ced89c6 [Class statistics] Fix pass rate 2024-10-22 19:49:00 +03:00
Riku Rauhala 6334fe5696 [TypeScript] Replace some type assertions with type annotations 2024-10-22 19:39:28 +03:00
Riku Rauhala cf280b0a9d [Class statistics] Fix broken totals for multi year stats 2024-10-22 19:04:52 +03:00
Riku Rauhala 7f3f4f5dfe [Backend] Convert statMerger service to TypeScript 2024-10-22 19:04:52 +03:00
Riku Rauhala f8e13c9fea [Class statistics] Rename passed-% to pass rate
This is called pass rate elsewhere in Oodikone
2024-10-22 19:04:52 +03:00
Riku RauhalaandGitHub e23b843375 Merge pull request #4659 from UniversityOfHelsinkiCS/OOD-66
OOD-66: added debugging logging to pin point courses stats problem
2024-10-22 17:25:31 +03:00
Jan Rundt 922f65cf81 OOD-66: similar message to make filtering logs easier 2024-10-22 13:53:48 +03:00
Jan Rundt cee1d0f6cb OOD-66: some more details 2024-10-22 13:45:08 +03:00
Valtteri KantanenandGitHub 0a3124e90f Merge pull request #4656 from UniversityOfHelsinkiCS/OOD-46 2024-10-22 13:19:15 +03:00
Jan Rundt 6065329c58 OOD-66: added debugging logging to pin point courses stats problem in demo environment 2024-10-22 13:19:06 +03:00
Valtteri KantanenandGitHub d521b8a99f Merge pull request #4658 from UniversityOfHelsinkiCS/OOD-68
OOD-68: [backend] continue if study right does not have a student
2024-10-22 12:56:37 +03:00
Valtteri Kantanen bb67309a7c Fix evaluation overview 2024-10-22 12:53:38 +03:00
Matti Räsänen fe190a83ea Merge branch 'master' into OOD-46 2024-10-22 07:22:45 +03:00
Valtteri Kantanen ed0bcd7fee Remove unused constant 2024-10-21 18:42:30 +03:00
Valtteri Kantanen 18df30c09a [Students] Fix tests 2024-10-21 18:32:31 +03:00
Valtteri Kantanen 17ef03d05d [Students] Improve appearance of credit graph tooltip 2024-10-21 18:19:51 +03:00
Valtteri Kantanen 2bf804d7c7 [Students] Fix credit target line 2024-10-21 18:16:43 +03:00
Valtteri Kantanen 04da6d92d3 [Class statistics] Fix course filter tooltip not showing 2024-10-21 18:13:09 +03:00
Jan Rundt 2e3d22baba OOD-68: prettier code 2024-10-21 17:04:19 +03:00
Jan Rundt 758d35c298 OOD-68: continue if study right does not have a student 2024-10-21 16:53:41 +03:00
Riku Rauhala f6323229f9 [Course statistics] Test each toggle case in Attempts tab with combine substitutions 2024-10-21 14:19:55 +03:00
Riku Rauhala 28a04770bc [Course statistics] Test each toggle case in Students tab with combine substitutions 2024-10-21 13:51:49 +03:00
Riku Rauhala 0a417bec49 [Course statistics] Test each toggle case in Attempts tab 2024-10-21 13:31:41 +03:00
Riku Rauhala 2423bd45da [Course statistics] Test each toggle case in Students tab 2024-10-21 13:08:02 +03:00
Riku Rauhala 0155624c86 [Course statistics] Refactor tests 2024-10-21 12:19:50 +03:00
Riku Rauhala 1d310b2d9a [Course statistics] Update selected courses title based on substitutions 2024-10-21 12:06:07 +03:00
Riku Rauhala 231cf8e836 [Course statistics] Improve test coverage 2024-10-21 10:08:46 +03:00
Riku Rauhala f55c30b21e [Course statistics] Remove unused test 2024-10-21 10:08:46 +03:00
Riku RauhalaandGitHub e9f0490ed1 Merge pull request #4657 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-10-21 09:04:23 +03:00
renovate[bot]andGitHub f9ab98159d Update all non-major dependencies 2024-10-21 05:57:29 +00:00
Matti Räsänen ec9746097b Code cleaning 2024-10-21 08:46:50 +03:00
Matti Räsänen 067e47a99e [Backend][FacultyStatistics] Changed to use id instead of code in frontend and backend faculties router. Implemented getFacultyCodeById to backend to find code by id in faculties router 2024-10-21 08:25:03 +03:00
Riku Rauhala 2e760c7473 [Class statistics] Remove 'Passed after retry' and 'Failed multiple times' 2024-10-18 15:09:04 +03:00
Riku RauhalaandGitHub b3449fcf2a Merge pull request #4655 from UniversityOfHelsinkiCS/OOD-63-v2
OOD-63: [updater] require logger from correct file
2024-10-18 13:36:29 +03:00
Jan Rundt 009f3c13ae ATK is complex and hard 2024-10-18 13:29:18 +03:00
Jan Rundt 06c31b3922 logger from correct place 2024-10-18 13:24:21 +03:00
Valtteri Kantanen e9657fec00 [Class statistics] Remove Students tab from Courses of class 2024-10-18 13:12:42 +03:00
Riku Rauhala 64f2c8f9f6 [Course statistics] Hide subheaders if there are no substitutions 2024-10-18 11:06:13 +03:00
Riku Rauhala db374b6d84 [Course statistics] Limit pass rate to 100 percent 2024-10-18 10:55:09 +03:00
Riku Rauhala 9056a91857 [Course statistics] Reduce copy-paste in tests 2024-10-18 10:41:24 +03:00
Riku Rauhala a8d94c3707 [Course statistics] Refactor tests to reduce copy-paste 2024-10-18 10:08:35 +03:00
Riku Rauhala 02243fdf4b [Course statistics] Test Students table, grades shown 2024-10-18 09:50:49 +03:00
Valtteri KantanenandGitHub 810bf65030 Merge pull request #4649 from UniversityOfHelsinkiCS/OOD-63
OOD-63: [updater] some added error handling and a null check
2024-10-17 16:38:45 +03:00
Valtteri Kantanen 207711b980 [Updater] Use Array.isArray instead of isArray function in lodash 2024-10-17 16:29:26 +03:00
Valtteri Kantanen dce0748b43 [Updater] Use optional chaining instead of checking the object first 2024-10-17 16:28:25 +03:00
Riku Rauhala bd299a0b84 [Course statistics] Test Students table, grades hidden 2024-10-17 15:56:12 +03:00
Riku Rauhala 454bb610af [Course statistics] Test all data on Attempts table 2024-10-17 15:14:54 +03:00
Riku Rauhala 134752f94c [Course statistics] Improve tests 2024-10-17 15:01:52 +03:00
Riku Rauhala 92c01d1eaa [Course statistics] Rename yearcode 2024-10-17 14:35:32 +03:00
Valtteri Kantanen 4333d6a44f [Filters] Fix age filter test 2024-10-17 14:22:10 +03:00
Riku Rauhala 34440de0d5 [Backend] Remove duplicate code 2024-10-17 13:45:02 +03:00
Riku Rauhala b3e352d51d [Backend] Remove student pass categories 2024-10-17 13:36:31 +03:00
Riku Rauhala 3ca2235ca1 [Course statistics] Remove student numbers from total 2024-10-17 10:36:25 +03:00
Jan Rundt 453586a634 null checking effectiveValidityPeriod 2024-10-16 14:56:37 +03:00
Jan Rundt 4313ba2c94 adding array check for programme module children var 2024-10-16 13:23:37 +03:00
Jan Rundt 295683ced1 some added error handling 2024-10-16 13:07:36 +03:00
Valtteri Kantanen b05b11aa06 Don't fetch enrollments with state other than ENROLLED or REJECTED (#4292)
Rejected enrollments are only used in Programme courses / By semester and Language center view
2024-10-14 17:38:44 +03:00
Valtteri Kantanen ab7e5d01f5 [Class statistics] Credit distribution development: Fix edge case on categories 2024-10-14 14:16:09 +03:00
Riku Rauhala e206e2b956 [Course statistics] Use same colors in both pass rate charts 2024-10-14 13:47:30 +03:00
Riku Rauhala af6cc538da [Course statistics] Update info boxes 2024-10-14 13:05:27 +03:00
Valtteri Kantanen 8e83da9138 [Class statistics] Fix Credits gained for master's programmes 2024-10-14 12:16:24 +03:00
Riku Rauhala 7811bdd509 [Course statistics] Fix tests 2024-10-14 11:46:48 +03:00
Riku Rauhala 63dc848857 [Course statistics] Visually separate main code and substitutions 2024-10-14 10:41:02 +03:00
Valtteri Kantanen 2d6a9afc80 Add no-unnecessary-type-assertion rule for ESLint and fix errors 2024-10-14 10:15:00 +03:00
Riku Rauhala ffb12bc89d [Course statistics] Summary tab: replace connect with hooks 2024-10-14 09:54:35 +03:00
Riku Rauhala c08db04472 [Course statistics] Replace Redux connect with hooks 2024-10-14 09:33:30 +03:00
Riku RauhalaandGitHub 63dec8a96d Merge pull request #4646 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-10-14 09:07:25 +03:00
renovate[bot]andGitHub 92b01943a0 Update all non-major dependencies 2024-10-14 06:00:36 +00:00
Riku RauhalaandGitHub 00196be0e7 Merge pull request #4645 from UniversityOfHelsinkiCS/fix-course-stats
Course statistics: Fix numbers in Students table
2024-10-11 17:13:59 +03:00
Riku Rauhala 106fa5ed0f Merge branch 'master' into fix-course-stats 2024-10-11 17:06:27 +03:00
Riku Rauhala 42074567e8 [Course statistics] Simplify fail rate calculation 2024-10-11 17:02:32 +03:00
Riku Rauhala eed4a316da [Course statistics] Remove withEnrollments 2024-10-11 17:02:32 +03:00
Riku Rauhala feb80d4203 [Course statistics] Update keys for consistency 2024-10-11 17:02:32 +03:00
Riku Rauhala 89c2e95dd0 [Course statistics] Update prop type
This was giving an annoying console warning every time the page is loaded
2024-10-11 17:02:32 +03:00
Riku Rauhala 142896bc0e [Course statistics] Fix Total students and Failed in Students table 2024-10-11 17:02:32 +03:00
Valtteri Kantanen a5f4ab9ad2 Fix tests after update in anon sis-db 2024-10-11 16:34:42 +03:00
Valtteri Kantanen a99fbbb6c4 [Study programme overview] Fix tests after update in anon sis-db 2024-10-11 15:47:44 +03:00
Riku Rauhala 24f5018861 [Course statistics] Remove categories from frontend and fix pass/fail rates 2024-10-11 15:45:13 +03:00
Riku Rauhala 90f6c52469 [Course statistics] Fix total failed 2024-10-11 15:28:05 +03:00
Valtteri Kantanen 66f603e2ca Add minimumCredits to ProgrammeModule model for degree programmes 2024-10-11 15:16:05 +03:00
Riku Rauhala 937bb70b08 [Course statistics] Fix pass rate chart 2024-10-11 15:11:55 +03:00
Riku Rauhala ae509ae8ea [Course statistics] Refactor 2024-10-11 15:06:13 +03:00
Riku Rauhala 8beddf655a [Course statistics] Fix total passed 2024-10-11 15:04:55 +03:00
Riku Rauhala 3486112d1c [Course statistics] Simplify fail rate calculation 2024-10-11 09:50:04 +03:00
Riku Rauhala 7cadd05c5a [Course statistics] Remove withEnrollments 2024-10-11 09:27:24 +03:00
Valtteri Kantanen 3b19485d44 [Class statistics] Don't include secondary attainments in Credits gained tab 2024-10-10 17:34:21 +03:00
Valtteri Kantanen 37c000b8dc [Course statistics] Fix tests 2024-10-10 17:25:54 +03:00
Valtteri Kantanen 8835d38fd3 [Population statistics] Mark improved grades as passed 2024-10-10 16:12:30 +03:00
Valtteri Kantanen 4bc5ed6558 [Updater] Ignore enrollments with state other than ENROLLED or REJECTED 2024-10-10 14:08:42 +03:00
Riku Rauhala b96d409a00 [Course statistics] Update keys for consistency 2024-10-10 14:03:18 +03:00
Riku Rauhala da2e24f4e6 [Course statistics] Update prop type
This was giving an annoying console warning every time the page is loaded
2024-10-10 14:00:01 +03:00
Riku Rauhala 80ca4185ed [Course statistics] Fix Total students and Failed in Students table 2024-10-10 11:43:19 +03:00
Valtteri Kantanen c1982596d5 [Backend] Improve logging 2024-10-09 17:03:55 +03:00
Valtteri Kantanen f6d3728bd1 [Teachers] Calculate leaderboard data if it's not present 2024-10-09 16:04:30 +03:00
Valtteri Kantanen b0590afc29 [Class statistics] Remove unused parameters and props 2024-10-09 16:02:45 +03:00
Valtteri Kantanen 703c1be7c3 [Study programme overview] Improve infobox texts 2024-10-09 16:02:00 +03:00
Valtteri Kantanen 416c160667 [Class statistics] Fix old courses data being used when the study programme changes (#4632) 2024-10-09 16:01:24 +03:00
Valtteri KantanenandGitHub d9e1943c51 Merge pull request #4644 from UniversityOfHelsinkiCS/OOD-59
OOD-59: [updater] [backend] try-catch-clauses for updates and if-clauses for using HY-specific programmeCode-map
2024-10-08 15:07:54 +03:00
Jan Rundt 52a8cd2e75 OOD-59: try-catch-clauses for updates and if-clauses for using HY-specific programmeCode-map 2024-10-08 12:23:31 +03:00
Valtteri Kantanen 4d712a308c [Frontend] Upgrade Sentry from v6 to v8 2024-10-07 13:02:46 +03:00
Valtteri Kantanen b81d179f27 [Study programme overview] Update tests 2024-10-07 12:23:50 +03:00
Valtteri Kantanen 083aa87d0a [Study programme overview] Add study track level breakdown for graduation times (#4468) 2024-10-07 11:58:50 +03:00
Valtteri Kantanen 420db2dd88 [Class statistics] Make progress bar label always have the same width 2024-10-07 11:25:38 +03:00
Valtteri Kantanen 5a03c2b426 [Class statistics] Fix ambiguous chart legend: Previously "150–180", now "150 ≤ credits < 180" 2024-10-07 11:24:01 +03:00
Valtteri Kantanen a227d65aa4 [Class statistics] Fix misleading infobox text 2024-10-07 11:21:52 +03:00
Valtteri Kantanen 5236638322 [Class statistics] Fix filters on Credits gained tab not working properly
Previously the filters used the Credits earned filter, but that's not correct as it uses number of all credits, no matter when they were attained. Previously the Personal study plan filter didn't have an effect on the table either even though the table header suggested it did. Now HOPS credits are used in the table if Personal study plan filter is active.
2024-10-07 11:21:01 +03:00
Riku Rauhala 16fd293290 [Course statistics] Fix missing enrollments 2024-10-07 10:10:53 +03:00
Riku RauhalaandGitHub 2d98a776c4 Merge pull request #4643 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-10-07 09:36:23 +03:00
renovate[bot]andGitHub be784074ee Update all non-major dependencies 2024-10-07 06:27:50 +00:00
Riku Rauhala 7aedb82dcf [Course statistics] Update tests 2024-10-04 17:28:43 +03:00
Riku Rauhala e93031427a [Course statistics] Remove duplicate primary course info 2024-10-04 17:01:42 +03:00
Riku Rauhala 5f98df491d [Backend] Update substitution courses logic 2024-10-04 16:58:29 +03:00
Riku Rauhala 8970d62413 [Course statistics] Update tests 2024-10-04 16:04:10 +03:00
Riku Rauhala ba5224a3b7 [Population statistics] Update tests 2024-10-04 15:43:45 +03:00
Riku Rauhala 934f5afa2a [Course statistics] Update tests 2024-10-04 15:41:18 +03:00
Riku Rauhala 8f331e6078 [Backend] Include main course in alternatives 2024-10-04 15:31:41 +03:00
Riku Rauhala e8b51cc5e3 [Course statistics] Fix broken links 2024-10-04 15:02:15 +03:00
Riku Rauhala 1bcf1baa3e [Students] Update tests 2024-10-04 14:47:26 +03:00
Riku Rauhala 9c04ca7bf1 [Backend] Fix invalid date 2024-10-04 14:28:53 +03:00
Riku Rauhala 517e67aeab [Course statistics] Extract CourseLabel component and Selected courses text 2024-10-04 14:28:53 +03:00
Riku Rauhala 13d8db6ed2 [Course statistics] Include name of alternative courses and improve styling 2024-10-04 14:28:53 +03:00
Valtteri Kantanen 2cf2d240f6 [Students] Fix grade symbol and name going on different rows in the courses table 2024-10-03 11:13:39 +03:00
Riku Rauhala 71c297d345 [Course statistics] Update enrolled, no grade calculation 2024-09-30 15:34:15 +03:00
Riku Rauhala 4b718b3cf6 [Course statistics] Update enrollments column title and helper text 2024-09-30 14:37:56 +03:00
Riku Rauhala 6375c816ac [Backend] Remove enrollment state from course statistics 2024-09-30 14:35:17 +03:00
Riku Rauhala df081ae3a4 [Course statistics] Fix missing enrollments for groups other than All 2024-09-30 14:02:36 +03:00
Riku Rauhala dba194c52e [Course statistics] Refactor 2024-09-30 09:29:51 +03:00
Riku RauhalaandGitHub 493f56c28a Merge pull request #4634 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-09-30 08:42:39 +03:00
renovate[bot]andGitHub 668c37c00c Update all non-major dependencies 2024-09-30 05:35:17 +00:00
Valtteri Kantanen 7897ef596a Remove composite column from credit_teachers and course_providers 2024-09-27 18:05:29 +03:00
Riku Rauhala 09b8f75a59 [Course statistics] Update tests 2024-09-27 17:43:52 +03:00
Valtteri Kantanen ef4f1518cb [Backend] Fix SISStudyRight model definition 2024-09-27 17:08:02 +03:00
Valtteri Kantanen 71a3686846 Fix some model definitions 2024-09-27 17:00:20 +03:00
Valtteri Kantanen c761949d68 [Updater] Improve logging of Sequelize errors 2024-09-27 16:54:46 +03:00
Valtteri Kantanen b9bee01681 [Updater] Add unique constraint to course_providers 2024-09-27 16:54:17 +03:00
Valtteri Kantanen 586985c45f [Updater] Add unique constraint to credit_teachers 2024-09-27 16:53:46 +03:00
Riku Rauhala da4e495a33 [Course statistics] Update total attempts text
Let's use multiline for show grades as well for consistency
2024-09-27 14:55:11 +03:00
Riku Rauhala 88652020ca [Course statistics] Remove enrollments by state 2024-09-27 14:50:23 +03:00
Riku Rauhala 5bd9e7ff9f [Course statistics] Remove enrollment types from Attempts tab 2024-09-27 13:33:17 +03:00
Riku Rauhala c87ea60b37 [Course statistics] Change never passed text to failed in pass rate chart 2024-09-27 13:27:45 +03:00
Valtteri Kantanen 60f5c23de4 [Frontend] Upgrade vulnerable version of rollup 2024-09-27 13:00:23 +03:00
Valtteri Kantanen 57083d03fa Add hasPersonalIdentityCode field to Student model 2024-09-27 12:59:00 +03:00
Riku Rauhala 18f5666daf [Course statistics] Add enrolled, no grade to pass rate chart in Attempts tab 2024-09-27 11:19:54 +03:00
Riku Rauhala 43e746de6f [Course statistics] Refactor countTotalStats 2024-09-27 10:47:39 +03:00
Riku Rauhala 3cc0f89dbd [Course statistics] Convert countTotalStats to TypeScript 2024-09-27 09:35:58 +03:00
Valtteri Kantanen a9013204d6 [Course statistics] Fix tests 2024-09-26 14:40:21 +03:00
Valtteri Kantanen eb1a124292 [Study programmes] Sort study tracks alphabetically 2024-09-26 14:34:06 +03:00
Valtteri Kantanen 4db0ef4897 [Study programmes] Fix combined programme stats not getting saved in Redis 2024-09-26 14:18:33 +03:00
Riku Rauhala 21cbfb18d4 [Course statistics] Simplify pass rate calculation 2024-09-26 14:08:13 +03:00
Riku Rauhala cfe27ed599 [Course statistics] Calculate pass rate with enrollments 2024-09-26 13:57:43 +03:00
Riku Rauhala 7dafd5d9c2 [Course statistics] Update tests 2024-09-23 15:08:22 +03:00
Valtteri Kantanen 76c037dd72 [Study programmes] Try to optimize Programme courses 2024-09-23 14:58:05 +03:00
Riku Rauhala 053a20f285 [Course statistics] Attempts tab: count total enrollments in total attempts
Enrollments are also considered attempts
2024-09-23 14:40:34 +03:00
Riku Rauhala ae33741452 [Course statistics] Improve the pass rate chart
For the relative passed/failed chart, let's display the bars on top of each other
2024-09-23 13:04:14 +03:00
Riku Rauhala 5c55b58278 [Study programme overview] Add retries to a flaky test 2024-09-23 10:29:55 +03:00
Valtteri Kantanen a692a2dc66 [Sortable table] Fix issue with column filtering and sorting not working in certain cases 2024-09-23 10:21:24 +03:00
Riku RauhalaandGitHub 4e97210422 Merge pull request #4628 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-09-23 09:16:30 +03:00
renovate[bot]andGitHub 4816db4791 Update all non-major dependencies 2024-09-23 06:09:59 +00:00
Riku Rauhala 66af7cfe07 [Language center view] Update tests 2024-09-20 19:10:11 +03:00
Riku Rauhala 56f3eb98a4 [Language center view] Update tests 2024-09-20 18:37:08 +03:00
Valtteri Kantanen 3ebff5d4f9 [Class statistics] Fix credit accumulation chart 2024-09-20 15:40:11 +03:00
Valtteri Kantanen f842364c1f Remove .only from a test 2024-09-20 15:39:58 +03:00
Riku Rauhala 3d5bc801ca [Language center] Update tests 2024-09-20 11:44:44 +03:00
Riku Rauhala d04476d874 [Language center] Update tests 2024-09-20 10:46:51 +03:00
Riku Rauhala 34444db544 [Programme courses] Export course name 2024-09-20 09:40:32 +03:00
Valtteri Kantanen 1c6e87ac5f [Updater] Add index for course_id column in enrollment table 2024-09-19 18:24:52 +03:00
Valtteri KantanenandGitHub a7707358d6 Merge pull request #4631 from UniversityOfHelsinkiCS/renovate/npm-vite-vulnerability
Update dependency vite to v5.4.6 [SECURITY]
2024-09-19 14:00:59 +03:00
Riku RauhalaandGitHub 949b85776a Merge pull request #4629 from UniversityOfHelsinkiCS/delete-user-in-fd-environment-merge-with-master
[Frontend][Backend] Delete user in fd environment, can not add if user already exists in db in fd environment
2024-09-19 11:08:22 +03:00
Valtteri Kantanen 41551182a4 [Close to graduation] Update new credit limits to infobox 2024-09-19 08:44:44 +03:00
Valtteri Kantanen 447142c7fc [Close to graduation] Adjust lower limits 2024-09-19 08:39:00 +03:00
renovate[bot]andGitHub 085e102bd6 Update dependency vite to v5.4.6 [SECURITY] 2024-09-17 21:16:31 +00:00
Jan Rundt 4de0da3b7a using delete as method in delete request 2024-09-17 10:26:43 +03:00
Valtteri Kantanen c0fb7208d8 Fix updating teacher leaderboards via frontend 2024-09-16 15:09:28 +03:00
Jan Rundt 47cb76d78b small fixes 2024-09-16 15:01:38 +03:00
Jan Rundt 1d03e59691 Merge branch 'master' into delete-user-in-fd-environment-merge-with-master 2024-09-16 12:55:12 +03:00
Jan Rundt 07d1db6d36 not overwriting existing user 2024-09-16 12:50:21 +03:00
Valtteri Kantanen a914b46c1c Don't try to get Sisu roles in staging 2024-09-16 12:46:18 +03:00
Valtteri Kantanen 52cca4dd01 Upgrade redis to v4 and redis-lock to v1 2024-09-16 12:19:58 +03:00
Valtteri Kantanen 2360d05f25 [Teachers] Sort leaderboard rows by number of credits 2024-09-16 11:56:15 +03:00
Riku Rauhala f5829c0c18 [Course statistics] Simplify pass rate chart 2024-09-16 10:03:51 +03:00
Jan Rundt 587ec153eb delete shown only for fd 2024-09-16 09:14:43 +03:00
Riku RauhalaandGitHub eebabf0ba1 Merge pull request #4627 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-09-16 09:08:59 +03:00
Jan Rundt fc93028e57 deleting a user, backend and frontend 2024-09-16 09:07:45 +03:00
renovate[bot]andGitHub 42bbce2f57 Update all non-major dependencies 2024-09-16 05:57:35 +00:00
Valtteri Kantanen 86430e12e1 [Study programmes] Fix irrelevant programmes data being saved to Redis 2024-09-13 13:05:35 +03:00
Valtteri Kantanen c4daf60e8c [Study programmes] Make types more specific 2024-09-13 13:00:47 +03:00
Valtteri Kantanen 66341a1bc4 [Faculties] Replace getProgrammes with getDegreeProgrammesOfFaculty 2024-09-13 10:57:39 +03:00
Riku Rauhala 2ae0d23271 Fix tests 2024-09-13 10:26:23 +03:00
Riku Rauhala 029491fb84 [Frontend] Fix grammar issues 2024-09-13 10:12:00 +03:00
Riku Rauhala 9a91b5fba7 [Frontend] Type PassRateChart.tsx 2024-09-13 10:03:48 +03:00
Riku Rauhala b16ad3d28b [Frontend] Convert PassRateChart to TypeScript 2024-09-13 09:20:14 +03:00
Riku Rauhala 2c7d9bce74 [Course statistics] Remove details from the Students tab
Fail-% is always visible, passed first/eventually is removed
2024-09-12 21:33:10 +03:00
Riku Rauhala 2f7343bdc0 [Course statistics] Improve table formatting 2024-09-12 21:33:10 +03:00
Riku Rauhala 7e474b7599 [Backend] Remove type PassedCategory 2024-09-12 21:33:10 +03:00
Valtteri Kantanen 03d9ccb8aa [Updater] Handle custom attainments in HY the way they used to be handled 2024-09-12 15:46:42 +03:00
Valtteri Kantanen 7569cd91be [Course statistics] Filter out courses made out of custom attainments (#4624) 2024-09-12 14:00:39 +03:00
Valtteri Kantanen 83095bb9cc [Docs] Add info about access rights (#4498) 2024-09-12 09:42:12 +03:00
Valtteri Kantanen 53a9d538da Upgrade vulnerable packages 2024-09-12 08:43:33 +03:00
Riku RauhalaandGitHub 0f6c14b07c Merge pull request #4626 from UniversityOfHelsinkiCS/type-study-programme
Type remaining study programme code
2024-09-10 10:29:16 +03:00
Riku Rauhala f56d2deb84 [Backend] Fix build errors 2024-09-10 10:14:54 +03:00
Riku Rauhala b094834448 [Backend] Fix graduation times 2024-09-10 10:09:33 +03:00
Riku Rauhala 5662c6f9f6 [Backend] Type studyProgrammeCourses 2024-09-10 09:23:19 +03:00
Riku Rauhala a793b7a35d [Backend] Improve error handling 2024-09-10 08:35:52 +03:00
Riku Rauhala a590b93130 [Backend] Begin typing studyProgrammeCourses 2024-09-10 08:26:22 +03:00
Riku Rauhala 04225056ff [Backend] Improve raw query typing and formatting 2024-09-09 21:58:51 +03:00
Valtteri Kantanen 7967d10333 [Docs] Update documentation after deleting some tables in the db 2024-09-09 16:46:21 +03:00
Valtteri Kantanen 2d82c69ee3 [Updater] Drop old study right related tables 2024-09-09 16:45:24 +03:00
Riku Rauhala 475b78e36e [Backend] Convert studyProgrammeCourses into TypeScript 2024-09-09 16:23:24 +03:00
Riku Rauhala 57247069a2 [Backend] Type studyProgrammeUpdates 2024-09-09 16:19:12 +03:00
Riku Rauhala 67b9dea0fa [Backend] Convert studyProgrammeUpdates into TypeScript 2024-09-09 15:46:47 +03:00
Riku Rauhala 2b462ca3b1 [Backend] Type studyProgrammeBasics 2024-09-09 15:45:53 +03:00
Riku Rauhala a257bbdca3 [Backend] Extract type SemesterEnrollment 2024-09-09 10:54:03 +03:00
Riku Rauhala 6c76adfa30 [Backend] Convert studyProgrammeBasics into TypeScript 2024-09-09 10:34:23 +03:00
Riku Rauhala 766e705a34 [Backend] Type studyProgramme service 2024-09-09 10:32:40 +03:00
Riku Rauhala f08e27eeb2 [Backend] Convert studyProgramme into TypeScript 2024-09-09 10:32:40 +03:00
Valtteri Kantanen cf1b30cc4c [Custom population] Fix tests 2024-09-09 10:05:42 +03:00
Valtteri Kantanen 656f199b1b [Class statistics] Fix courses stats not being fetched 2024-09-09 10:05:27 +03:00
Valtteri Kantanen f7e4575ced [Population statistics] Remove unneeded fields from course stats response 2024-09-09 09:25:57 +03:00
Valtteri Kantanen ad3ad11e7f [Population statistics] Only render tab panel content if the panel is active 2024-09-09 09:22:34 +03:00
Valtteri Kantanen 7b745b0853 [Study guidance groups] Sort student numbers before fetching data to make RTK Query cache work properly 2024-09-09 09:22:00 +03:00
Valtteri Kantanen 8bab98eb32 [Study guidance groups] Fix Courses tab crashing 2024-09-09 09:16:33 +03:00
Valtteri Kantanen 4f7a272f9b [Study guidance groups] Reduce loading time of the page 2024-09-09 09:15:39 +03:00
Riku RauhalaandGitHub 11b68b7e25 Merge pull request #4625 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-09-09 08:30:31 +03:00
renovate[bot]andGitHub 8c08204fe7 Update all non-major dependencies 2024-09-09 05:18:10 +00:00
Riku Rauhala 39e9dc8985 [Backend] Extract types to remove copy-paste 2024-09-05 15:03:13 +03:00
Riku Rauhala 0a63bfd7b1 [Documentation] Add Redis to databases.md 2024-09-05 14:48:08 +03:00
Riku Rauhala 1dbe571bc8 [Documentation] Add titles to database diagrams 2024-09-05 13:24:24 +03:00
Riku Rauhala 4afa5707fb [Documentation] Add sis-db relations 2024-09-05 11:47:49 +03:00
Riku Rauhala fb9e45f63d [Documentation] Add sis-db tables 2024-09-05 08:56:26 +03:00
Riku RauhalaandGitHub ece3a12e71 Merge pull request #4623 from UniversityOfHelsinkiCS/type-bottlenecks
Type bottlenecks
2024-09-04 09:07:12 +03:00
Riku Rauhala f5b4477f2e [Backend] Type bottlenecksOf 2024-09-03 21:44:54 +03:00
Riku Rauhala 7df3b06e7b [Backend] Convert bottlenecksOf into TypeScript 2024-09-03 15:54:05 +03:00
Valtteri Kantanen f7d51b0dd9 [Docs] Fix link to importer-db-staging-sampletaker 2024-09-03 15:08:03 +03:00
Riku Rauhala f7ddd23200 [GitHub Actions] Update workflow files 2024-09-03 13:49:41 +03:00
Valtteri Kantanen 899709bc9e [Backend] Change 'as' to 'AS' in Dockerfile to get rid of a warning in GHA 2024-09-03 13:33:49 +03:00
Valtteri Kantanen 271e0c70f0 [Students] Add more tests 2024-09-03 13:19:57 +03:00
Valtteri Kantanen 0b5eb3cbdf [Bachelor Honours] Fix one main module code 2024-09-03 13:18:59 +03:00
Valtteri Kantanen 121ab7e9de [Docs] Update instructions on creating or updating a test database 2024-09-03 12:35:12 +03:00
Valtteri Kantanen 56a38e342e [Language center view] Fix a test 2024-09-03 12:17:17 +03:00
Valtteri Kantanen de912d2557 Fix tests to align with new test data 2024-09-03 11:54:50 +03:00
Riku Rauhala b194689379 [GitHub Actions] Build frontend before running tests 2024-09-03 11:23:54 +03:00
Riku Rauhala e4f4e47f87 [Frontend] Fix the type error 2024-09-02 15:10:27 +03:00
Riku Rauhala fc6af9432a [Frontend] Cause a type error 2024-09-02 15:10:04 +03:00
Riku Rauhala 545a3f9f8c [Frontend] Run tsc before vite build 2024-09-02 15:00:32 +03:00
Valtteri KantanenandGitHub 90d6b63892 Merge pull request #4622 from UniversityOfHelsinkiCS/OOD-52-changes
[Fd compose] Changed fd compose
2024-09-02 12:29:09 +03:00
Matti Räsänen 12f35ae9e0 [Fd compose] Fixed typo oodikone-backedn context 2024-09-02 12:26:33 +03:00
Matti Räsänen 61628c7601 [Fd compose] Changed fd compose 2024-09-02 12:10:11 +03:00
Riku Rauhala a9a61139db [Study programme] Remove median graduation time details for BaMa 2024-09-02 10:54:04 +03:00
Riku RauhalaandGitHub 674fe0cc40 Merge pull request #4621 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-09-02 10:31:32 +03:00
Valtteri KantanenandGitHub ad4893f05b Merge pull request #4619 from UniversityOfHelsinkiCS/OOD-52
[Backend][Frontend] Replaced curriculumPeriodIdToYearCode with curriculumPeriods
2024-09-02 10:17:34 +03:00
renovate[bot]andGitHub a498d72e2f Update all non-major dependencies 2024-09-02 07:17:04 +00:00
Valtteri Kantanen 9932b77d35 [Faculties] Fix a typo 2024-09-02 10:10:41 +03:00
Valtteri Kantanen 2fe2561d69 Add a better type for CurriculumPeriods 2024-09-02 10:10:13 +03:00
Valtteri Kantanen 254ccefb9c Add instructions for running the tests faster locally 2024-09-02 09:46:39 +03:00
Valtteri Kantanen ccd21629b8 Add tests for University view 2024-09-02 09:40:13 +03:00
Valtteri Kantanen 8488453cbb Fix tests 2024-09-02 09:28:38 +03:00
Valtteri Kantanen 830647d988 Refactor tests to align with new test data and also write ~30 new tests 2024-09-02 09:08:25 +03:00
Valtteri Kantanen 1cd5bb66d9 [Study programme overview] Fix wrong degree type showing on study track graduation times charts 2024-09-02 09:03:40 +03:00
Valtteri Kantanen 4158b75375 Update a vulnerable webpack version 2024-09-02 09:02:48 +03:00
Valtteri Kantanen 84acff6e58 [Frontend] Fix progress bar appearance on wider screens 2024-09-02 09:01:53 +03:00
Valtteri Kantanen d7aed1f9e2 [Class statistics] Fix 'Total' class page not showing any students 2024-09-02 09:00:19 +03:00
Riku Rauhala 371443f737 [Frontend] Add tsc command 2024-08-30 18:41:52 +03:00
Riku Rauhala 3782347124 [Study programme] Type MedianTimeBarChart 2024-08-30 18:24:06 +03:00
Riku Rauhala fa67d645b4 [Frontend] Convert MedianTimeBarChart to TypeScript 2024-08-30 15:00:09 +03:00
Riku Rauhala d31a24d6a8 [Study programmes] Revert changes to median times chart 2024-08-30 12:49:17 +03:00
Riku Rauhala 5d7550435e [Backend] Fix tests 2024-08-30 11:40:46 +03:00
Riku Rauhala d07c10f184 [Backend] Fix TS problems 2024-08-30 11:23:24 +03:00
Riku Rauhala 49054b5eeb [Backend] Remove unused transfers and improve typing 2024-08-30 11:09:57 +03:00
Matti Räsänen dfdb233700 [Backend] Added impossible default to mapCurriculumPeriodIdToYear 2024-08-29 13:16:07 +03:00
Matti Räsänen 4252230c81 [FacultyProgrammeOverview] Added creditCounts keys length check in function calculateStats 2024-08-29 12:31:38 +03:00
Matti Räsänen 7984cefeb0 Code cleaning 2024-08-29 12:25:37 +03:00
Matti Räsänen c9373a6e02 [Backend] Created curriculumPeriods service and refactored getDegreeProgrammesOfOrganization to use CurriculumPeriods instead of curriculumPeriodIdToYearCode way 2024-08-29 12:21:23 +03:00
Riku Rauhala 1d510951e5 [Study programme] Update graduation time labels and tooltips #4580 2024-08-29 11:51:39 +03:00
Riku Rauhala 917475b6ae [Backend] Remove an unused parameter 2024-08-29 10:52:30 +03:00
Riku Rauhala e1f863ff77 [Backend] Use shared mapToProviders 2024-08-29 10:17:25 +03:00
Valtteri KantanenandGitHub d3a3c0ff5c Merge pull request #4618 from UniversityOfHelsinkiCS/OOD-51
[FD compose] Added new docker compose for fd
2024-08-29 08:49:11 +03:00
Matti Räsänen 0a67aaae29 [FD compose] Added new docker compose for fd 2024-08-29 08:31:28 +03:00
Riku Rauhala 3d5c4ccaec [Course statistics] Flip the order of TT and HT grades 2024-08-28 15:26:01 +03:00
Riku Rauhala f29cd0f1e4 [Course statistics] Improve colors in grade distribution chart 2024-08-28 14:40:13 +03:00
Riku Rauhala 487a0c3c81 [Frontend] Remove unused colors 2024-08-28 13:42:22 +03:00
Riku Rauhala 2ae850d44b [Frontend] Convert color to TypeScript 2024-08-28 13:33:21 +03:00
Riku Rauhala bbc9cd2c78 [Frontend] Add .tsx files to ESLint overrides 2024-08-28 12:00:51 +03:00
Riku Rauhala f3560e68da [Course statistics] Type GradeDistributionChart 2024-08-28 11:58:38 +03:00
Riku Rauhala d3343debec [Course statistics] Fix a broken parameter 2024-08-28 11:58:38 +03:00
Riku Rauhala e49644ca50 [Course statistics] Refactor resolveGrades 2024-08-28 11:58:38 +03:00
Valtteri Kantanen 08b3e9074a [Backend] Add type for DegreeProgrammeType 2024-08-28 10:54:12 +03:00
Valtteri Kantanen 36b8aaa681 Remove SemesterEnrollment` model from updater 2024-08-28 10:48:45 +03:00
Valtteri Kantanen 2d34d13bce Remove SemesterEnrollment model from backend 2024-08-28 10:47:05 +03:00
Valtteri Kantanen 005ea1eb68 Fix pre-commit hook: don't try to check deleted files 2024-08-28 10:36:23 +03:00
Valtteri Kantanen c5dd481f4c [Custom population] Fix semester enrollments that are displayed in the General tab 2024-08-28 10:35:39 +03:00
Valtteri Kantanen 429b2ae7f8 [Study guidance groups] Add a direct link to Close to graduation view 2024-08-28 10:34:46 +03:00
Valtteri Kantanen 9266ad5dd0 [Class statistics] Remove extents, semesters and semester enrollments from backend response 2024-08-28 10:34:08 +03:00
Valtteri Kantanen 105b171bb1 [Class statistics] Use enrollments associated with study rights in enrollment status filter if they are available 2024-08-28 10:30:56 +03:00
Valtteri Kantanen 0c5d5a3efa [Class statistics] Use enrollments associated with study rights on Progress tab instead of semester enrollments 2024-08-28 10:29:10 +03:00
Valtteri Kantanen 21d4bfc0e8 [Students] Replace SemesterEnrollment model with enrollments associated with study rights 2024-08-28 10:27:38 +03:00
Riku Rauhala 705cc9c10a [Course statistics] Handle 2nd national language grade distribution 2024-08-27 18:43:40 +03:00
Valtteri KantanenandGitHub 304dd24bdb Merge pull request #4617 from UniversityOfHelsinkiCS/OOD-50-continuation
[Language center view]:putting language center view env variable into prod Dockerfile
2024-08-27 18:15:34 +03:00
Valtteri Kantanen 963bb272b6 Fix languageCenterViewEnabled configuration 2024-08-27 18:01:55 +03:00
Riku RauhalaandGitHub 966cc0aaa8 Merge pull request #4616 from UniversityOfHelsinkiCS/fail-pass-distribution
Course statistics: Handle fail/pass graded courses
2024-08-27 17:57:11 +03:00
Jan Rundt 157cef1643 OOD-50-cont: debugging tests 2024-08-27 17:49:55 +03:00
Valtteri Kantanen 1c1ae58e20 [Updater] Remove studyrightid from studyplan 2024-08-27 17:11:28 +03:00
Valtteri Kantanen 426965473f Delete ElementDetail, Studyright, StudyrightElement and Transfer models from updater 2024-08-27 17:09:10 +03:00
Jan Rundt f850ca8d12 OOD-50-continuation: putting language center view env variable into prod Dockerfile 2024-08-27 16:51:09 +03:00
Riku Rauhala f0d55d5d7f [Course statistics] Handle fail-pass courses 2024-08-27 15:37:14 +03:00
Riku Rauhala 5ee1b6dce0 [Course statistics] Refactor to eliminate copy-paste 2024-08-27 15:37:13 +03:00
Riku Rauhala 4facb54a97 [Course statistics] Type util 2024-08-27 15:37:13 +03:00
Riku Rauhala 1e7f980661 [Course statistics] Convert util to TypeScript 2024-08-27 15:37:13 +03:00
Valtteri Kantanen 2515411c70 Delete ElementDetail, Studyright, StudyrightElement and Transfer models from backend 2024-08-27 14:55:09 +03:00
Valtteri Kantanen e8ce4c703c [Population statistics] Check transfers only if there's a study programme code included 2024-08-27 14:37:23 +03:00
Valtteri Kantanen 9661807e60 [Class statistics] Replace Transfer model with studyRightElements 2024-08-27 14:13:02 +03:00
Valtteri KantanenandGitHub b80ef2316f Merge pull request #4613 from UniversityOfHelsinkiCS/OOD-50
[Language center view]: Make language center view visibility and backend functionality configurable
2024-08-27 12:54:55 +03:00
Valtteri Kantanen 3d008c7fa7 [Backend] Ensure concurrentWorkers is always a number 2024-08-27 12:45:48 +03:00
Valtteri Kantanen 1e7217197c [Language center view] Only use language center router if the view is enabled 2024-08-27 12:44:05 +03:00
Valtteri Kantanen 4f98bd61b8 Merge branch 'master' into OOD-50 2024-08-27 12:28:13 +03:00
Valtteri Kantanen 3e50a40f6a [Course population] Fix page crashing with obfuscated students 2024-08-27 11:46:28 +03:00
Valtteri Kantanen a2c37d38eb [Study programmes] Big refactor: Programme courses 2024-08-27 11:46:05 +03:00
Valtteri Kantanen d35e303657 [Backend] Replace ElementDetails where they were still used 2024-08-26 17:17:53 +03:00
Valtteri Kantanen 620bfcb81d [Class statistics] Fix included students when only fall semester is chosen 2024-08-26 16:50:07 +03:00
Valtteri Kantanen 56feb9518b [Class statistics] Use new study rights to determine the students of one year's class 2024-08-26 16:34:04 +03:00
Riku Rauhala 2d866b5c53 [CourseYearlyStatsCounter] Refactor and improve types 2024-08-26 14:52:25 +03:00
Valtteri Kantanen 7bfd075bf7 [Class statistics] Remove old study rights from backend response 2024-08-26 13:40:33 +03:00
Valtteri KantanenandGitHub 32a76d4ce5 Merge pull request #4597 from UniversityOfHelsinkiCS/renovate/docker-build-push-action-6.x
Update docker/build-push-action action to v6
2024-08-26 10:50:30 +03:00
Valtteri KantanenandGitHub c631a0d615 Merge pull request #4517 from UniversityOfHelsinkiCS/renovate/bullmq-5.x
Update dependency bullmq to v5
2024-08-26 10:44:58 +03:00
Valtteri KantanenandGitHub 3d09f5ce9e Merge pull request #4614 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-08-26 10:39:22 +03:00
Valtteri KantanenandGitHub e16792d0d6 Merge pull request #4518 from UniversityOfHelsinkiCS/renovate/cron-3.x
Update dependency cron to v3
2024-08-26 10:35:34 +03:00
renovate[bot]andGitHub 9aac9d789b Update all non-major dependencies 2024-08-26 07:30:59 +00:00
Valtteri Kantanen f4f2649d74 Upgrade vulnerable micromatch package 2024-08-26 10:25:44 +03:00
Valtteri KantanenandGitHub 892ab49eba Merge pull request #4596 from UniversityOfHelsinkiCS/renovate/lru-cache-11.x
Update dependency lru-cache to v11
2024-08-26 10:21:43 +03:00
Valtteri Kantanen ff2f4badd0 [Course population] Check if enrollment is null 2024-08-26 10:18:19 +03:00
Riku Rauhala 44478234d7 [Courses] Fix incorrectly counted enrollments 2024-08-26 10:11:27 +03:00
Valtteri Kantanen 9f8ea752b7 [Course statistics] Add null checks 2024-08-26 10:07:57 +03:00
Valtteri Kantanen c97ebf51e2 [Custom population] Big refactor: Programme distribution 2024-08-26 09:45:20 +03:00
Valtteri Kantanen 591444c099 [Course population] Big refactor: Credit gains table 2024-08-26 09:44:30 +03:00
Valtteri Kantanen 204703d873 [Class statistics] Big refactor: The rest of general tab 2024-08-26 09:43:03 +03:00
Valtteri Kantanen c4a9f49c47 [Class statistics] Fix page crashing if no correct study right is found 2024-08-23 16:18:08 +03:00
Valtteri Kantanen 54728c373c [Filters] Fix programme filter 2024-08-23 15:53:39 +03:00
Valtteri Kantanen dccef76d5a [Class statistics] Big refactor: Programme filter 2024-08-23 15:33:49 +03:00
Riku Rauhala cd0ef266e8 [Documentation] Update resources.md 2024-08-23 14:41:46 +03:00
Riku Rauhala 5f9a8312a2 [Documentation] Add pgcli in resources.md 2024-08-23 14:32:06 +03:00
Valtteri Kantanen 80e908d9a8 [Class statistics] Big refactor: Semester enrollments 2024-08-23 13:29:17 +03:00
Valtteri Kantanen e6a216f872 [Class statistics] Big refactor: Study track filter 2024-08-23 12:51:36 +03:00
renovate[bot]andGitHub edacce89e2 Update dependency cron to v3 2024-08-23 09:43:46 +00:00
renovate[bot]andGitHub 528def2260 Update dependency bullmq to v5 2024-08-23 09:42:02 +00:00
renovate[bot]andGitHub ad9ececc57 Update dependency lru-cache to v11 2024-08-23 08:55:02 +00:00
Valtteri Kantanen 6f7172c7d3 Use markdown on filter tool tips 2024-08-23 11:41:50 +03:00
Valtteri Kantanen 65a15f5de2 [Class statistics] Big refactor: Study right status filter 2024-08-23 11:41:20 +03:00
Jan Rundt 364ec8b9fd OOD-50: del 2024-08-23 10:45:27 +03:00
Jan Rundt 0ef8efeca3 OOD-50: backend and lint fixes 2024-08-23 10:34:08 +03:00
Riku Rauhala b49b1feff4 [Study programme] Improve avg. graduation times texts 2024-08-23 10:25:38 +03:00
Jan Rundt c44c146692 OOD-50: some formatting 2024-08-22 17:22:59 +03:00
Jan Rundt c526629b11 OOD-50: using env variable to enable/disable language center view 2024-08-22 17:02:01 +03:00
Valtteri Kantanen f085593f55 Remove commented-out code 2024-08-22 16:27:25 +03:00
Riku Rauhala c24766f896 [Backend] Improve types 2024-08-22 16:04:07 +03:00
Valtteri Kantanen 45acf19cc9 Type routes.js 2024-08-22 15:58:03 +03:00
Valtteri Kantanen 9847ba9466 Type student routes 2024-08-22 15:57:43 +03:00
Valtteri Kantanen 12bd60d70f [Class statistics] Big refactor: Credit distribution development 2024-08-22 12:59:43 +03:00
Valtteri Kantanen b03c5a11d6 [Class statistics] Big refactor: Study right type filter 2024-08-22 12:59:13 +03:00
Valtteri Kantanen 120808c00d [Study guidance groups] Fix tests 2024-08-22 12:58:49 +03:00
Valtteri Kantanen 19b80a5cee [Class statistics] Big refactor: General tab 2024-08-22 12:49:56 +03:00
Valtteri Kantanen 143458f867 [Class statistics] Big refactor: Hops filter 2024-08-22 12:44:47 +03:00
Valtteri Kantanen 289e0cd856 [Class statistics] Big refactor: Graduated from programme filter 2024-08-22 12:44:12 +03:00
Valtteri Kantanen f069d65ea0 [Class statistics] Big refactor: Admission type filter 2024-08-22 12:43:43 +03:00
Valtteri Kantanen 16b876fa5a Add @types/lodash to frontend 2024-08-22 12:40:06 +03:00
Valtteri Kantanen 9736a5c12d Fix installation script failing if symlinks already exist 2024-08-22 12:35:28 +03:00
Valtteri Kantanen 01a4dcf5f6 [Filters] Fix age filter behavior to match lodash implementation 2024-08-22 12:00:45 +03:00
Valtteri Kantanen 2918cfc44b [Filters] Fix age filter 2024-08-22 10:56:14 +03:00
Jan Rundt 0e8c5ef7a7 OOD-50: backend language center disabled option 2024-08-22 09:12:57 +03:00
Valtteri Kantanen 066a212261 [Class statistics] Big refactor: Age distribution 2024-08-21 18:14:49 +03:00
Jan Rundt 9c9f0dfadd OOD-50: compose update and no refresh language center in frontend 2024-08-21 17:10:26 +03:00
Valtteri Kantanen b349237343 [Class statistics] Big refactor: Graduations in credit accumulation graph 2024-08-21 16:23:38 +03:00
Valtteri Kantanen 02b3315991 [Study guidance groups] Fix fetching of user's study guidance groups 2024-08-21 14:33:25 +03:00
Riku Rauhala 2d3372d33f [Shared] Add language codes 2024-08-20 18:43:50 +03:00
Valtteri Kantanen 8b8f80b965 Add a name for lint job 2024-08-20 16:27:33 +03:00
Valtteri Kantanen 40bf945553 [University] Fix university view crashing 2024-08-20 16:27:05 +03:00
Riku Rauhala 5c0932aef5 [Frontend] Remove unnecessary folder
InfoBox is the only component inside Info
2024-08-20 14:03:17 +03:00
Valtteri Kantanen 493df5c733 Fix triple-slash directives in test files 2024-08-20 13:11:02 +03:00
Valtteri Kantanen 761d0ae11d Upgrade umzug to v3 and update migration files accordingly 2024-08-20 13:09:12 +03:00
Valtteri Kantanen 597acb83d9 Specify shell as 'bash' in the lint action 2024-08-20 12:09:53 +03:00
Valtteri Kantanen 9fddb500fc [Evaluation overview] Add tests 2024-08-20 12:06:51 +03:00
Valtteri Kantanen 6dce3e500d Convert 'lint' from a workflow to an action 2024-08-20 12:06:51 +03:00
Valtteri Kantanen 8819cc8382 [Evaluation overview] Hide section for study level if there are no stats for it 2024-08-20 12:06:51 +03:00
Riku Rauhala 69e580f3ce [Scripts] Add symlinking to the installation script 2024-08-20 11:56:57 +03:00
Riku Rauhala cacc275235 [Scripts] Improve language 2024-08-20 11:55:45 +03:00
Riku Rauhala 9dfe65d1c8 [GitHub Actions] Change build context for production 2024-08-20 10:06:08 +03:00
Riku RauhalaandGitHub 92c89f4279 Merge pull request #4608 from UniversityOfHelsinkiCS/gha-lint
Create a separate workflow for linting
2024-08-20 09:52:47 +03:00
Riku Rauhala 97b5d5f32f [GitHub Actions] Extract linting as a separate workflow 2024-08-20 09:50:41 +03:00
Riku Rauhala 9c3db7243e [GitHub Actions] Restore staging.yaml 2024-08-20 09:27:56 +03:00
Riku RauhalaandGitHub b53b4548e7 Merge pull request #4607 from UniversityOfHelsinkiCS/oodikone-shared-2
Implement code sharing
2024-08-20 09:26:57 +03:00
Riku Rauhala 0d86be4546 [Backend] Remove hello world 2024-08-20 08:41:34 +03:00
Riku Rauhala 1795c688ea [Shared] Add index.ts back 2024-08-20 08:40:38 +03:00
Riku Rauhala 174858959f [GitHub Actions] Add symlink for linting for PRs 2024-08-20 08:38:07 +03:00
Riku Rauhala 9bd17a3f26 [Shared] Remove helloWorld 2024-08-20 08:36:35 +03:00
Riku Rauhala 29df71ad6b [GitHub Actions] Add symlink for linting 2024-08-20 08:34:13 +03:00
Riku Rauhala 296459d16e [Gitignore] Add symlink target directories 2024-08-16 18:51:15 +03:00
Valtteri Kantanen d412b2589a [Faculties] Fix type error 2024-08-16 18:35:59 +03:00
Valtteri Kantanen 9a00ae5ef9 [Study programmes] Divide progress stats into bachelor+master and bachelor study rights in master's programmes 2024-08-16 18:30:59 +03:00
Valtteri Kantanen d2dfb21f65 [Faculties] Big refactor: Faculty progress stats 2024-08-16 18:30:59 +03:00
Riku Rauhala a18ecc11fe [Backend] Run hello world from shared 2024-08-16 15:02:56 +03:00
Riku Rauhala d463d25c4f [Backend] Mount shared as a volume in dev mode 2024-08-16 12:31:46 +03:00
Riku Rauhala 2fddd0769a [Backend] Fix path in Dockerfile 2024-08-16 11:46:10 +03:00
Riku Rauhala cd0516b7fe [Frontend] Fix path in Dockerfile 2024-08-16 11:45:48 +03:00
Riku Rauhala 878004ae77 [Backend] Update Dockerfile 2024-08-16 11:39:19 +03:00
Riku Rauhala 44aabde06c [Frontend] Update Dockerfile 2024-08-16 11:38:00 +03:00
Riku Rauhala d277f78d3b [Frontend] Mount shared in development 2024-08-16 11:35:08 +03:00
Riku Rauhala 63affe01b0 [Frontend] Add shared as a volume inside frontend src 2024-08-15 16:04:21 +03:00
Riku Rauhala 31e561230b [Frontend] Uninstall oodikone-shared 2024-08-15 15:45:18 +03:00
Riku Rauhala 86ec37dce3 [Shared] Rename oodikone-shared to shared 2024-08-15 15:28:56 +03:00
Riku RauhalaandGitHub 24160474b0 Merge pull request #4605 from UniversityOfHelsinkiCS/OOD-48
[FacultyStatistics] Added optional chaining to function roundValue
2024-08-15 14:17:55 +03:00
Matti Räsänen da0039cb01 [FacultyStatistics] Added optional chaining to function roundValue 2024-08-15 13:48:42 +03:00
Riku Rauhala 10ee08f9fb [Backend] Remove hello world 2024-08-15 13:41:21 +03:00
Riku Rauhala bac8f6bd15 [Shared] Hello world 2024-08-15 13:28:37 +03:00
Valtteri Kantanen b0dc3b7540 [Study programmes] Fix old programme name showing in the programme list (#4502) 2024-08-15 13:27:29 +03:00
Riku Rauhala bf0e7bb050 [Shared] Convert to js 2024-08-15 13:10:24 +03:00
Valtteri Kantanen d4a0e44ebd [Study programmes] Use the same endpoint for getting study programmes everywhere 2024-08-15 11:41:43 +03:00
Valtteri Kantanen 1b9afef816 [Class statistics] Big refactor: Previous bachelor's programme or the following master's programme 2024-08-14 18:21:35 +03:00
Valtteri KantanenandGitHub 804fe9fcdb Merge pull request #4604 from UniversityOfHelsinkiCS/OOD-46
[Backend] Added undefined check for thesisStats in facultyThesisWriters
2024-08-14 16:58:48 +03:00
Valtteri Kantanen 95fbe7d875 [Study programmes] Use curriculum periods from db instead of hardcoding the period format 2024-08-14 16:51:34 +03:00
Valtteri Kantanen 3e9fa504fa Add CurriculumPeriod model 2024-08-14 15:47:03 +03:00
Riku Rauhala f7c9c2e9ce [Backend] Add oodikone-shared 2024-08-14 14:26:48 +03:00
Riku Rauhala fc396aae57 [Backend] Update Dockerfile 2024-08-14 13:57:43 +03:00
Riku Rauhala a03fe5ee7c [GitHub Actions] Define dockerfile in test.yaml 2024-08-14 13:55:23 +03:00
Riku Rauhala ea772525d0 [GitHub Actions] Change backend build context 2024-08-14 13:53:28 +03:00
Riku Rauhala f615214ca8 [Backend] Change build context to services 2024-08-14 13:50:15 +03:00
Riku Rauhala 78071784d2 [Frontend] Add oodikone-shared 2024-08-14 13:24:04 +03:00
Matti Räsänen 8496b1c660 [Backend] Added undefined check for thesisStats to facultyThesisWriters 2024-08-14 11:25:30 +03:00
Riku RauhalaandGitHub 71feb64add Merge pull request #4603 from UniversityOfHelsinkiCS/add-root-org-id-to-fd-compose
[FD compose] Added ROOT_ORG_ID to backend as well
2024-08-14 08:42:53 +03:00
Matti Räsänen aed6e2265b [FD compose] Added ROOT_ORG_ID to backend as well 2024-08-14 08:40:19 +03:00
Valtteri Kantanen c49925bc6c [Study programmes] Credits produced: Fix tests 2024-08-13 20:08:16 +03:00
Valtteri Kantanen 45d4dbb33e [Study programmes] Fix categories for certain attainments 2024-08-13 18:09:02 +03:00
Valtteri Kantanen 24093efca8 Improve and update rapodiff 2024-08-13 18:08:07 +03:00
Valtteri Kantanen 59c54230d6 Remove refreshing study right associations from daily jobs 2024-08-13 14:44:13 +03:00
Valtteri Kantanen 5dd67c79b0 [Backend] Big refactor: study right associations 2024-08-13 14:41:31 +03:00
Valtteri Kantanen 3b94afe6ea Fix auto imports not working in frontend with VS Code 2024-08-13 13:14:19 +03:00
Riku RauhalaandGitHub 6695f7620b Merge pull request #4602 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-08-13 08:17:41 +03:00
renovate[bot]andGitHub dc0629741d Update all non-major dependencies 2024-08-13 05:00:58 +00:00
Valtteri Kantanen 9405bcefac [Study programme overview] Fix tests 2024-08-12 16:02:00 +03:00
Valtteri Kantanen f80a92db18 [Study programme overview] Credits produced: Improve types 2024-08-12 16:01:30 +03:00
Valtteri Kantanen 3648d5d5cf [Faculties] Remove ununsed parameter from function call 2024-08-12 15:39:11 +03:00
Valtteri Kantanen fe9888dca1 [Study programmes] Big refactor: Credits produced by the study programme 2024-08-12 15:36:04 +03:00
Valtteri Kantanen 8bd3274fa8 [Study programme overview] Display a message about progress stats being unavailable for individual study tracks 2024-08-12 14:38:52 +03:00
Valtteri Kantanen b0e696d187 [Updater] Fix course code used by course providers for custom attainments 2024-08-12 13:08:14 +03:00
Valtteri Kantanen 40b9df5dba [Study programmes] Fix credit stats showing all special groups when they should be excluded 2024-08-12 10:24:25 +03:00
Valtteri Kantanen 185e356373 [Faculties] Remove ununsed 'studyProgrammeFilter' query parameter 2024-08-12 10:21:08 +03:00
Riku RauhalaandGitHub 8073049c78 Merge pull request #4601 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update dependency @types/node to v20.14.15
2024-08-12 10:10:02 +03:00
renovate[bot]andGitHub ca49f241e4 Update dependency @types/node to v20.14.15 2024-08-12 07:02:07 +00:00
Riku Rauhala 366958683e [Backend] Refactor courseFinders 2024-08-09 12:54:40 +03:00
Riku RauhalaandGitHub 1c7b04633c Merge pull request #4600 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-08-09 12:40:58 +03:00
renovate[bot]andGitHub bf7c521173 Update all non-major dependencies 2024-08-09 09:34:27 +00:00
Riku Rauhala 1fda343551 [GitHub Actions] Remove ts-frontend branch from staging workflow 2024-08-09 10:02:59 +03:00
Riku Rauhala 6f87bc8989 [Cypress] Add retries to a flaky test 2024-08-09 10:02:23 +03:00
Riku RauhalaandGitHub 882812c8b9 Merge pull request #4599 from UniversityOfHelsinkiCS/ts-frontend
Add TypeScript to frontend
2024-08-09 09:54:52 +03:00
Riku Rauhala b3ea850b6d [Frontend] Fix linter issue 2024-08-09 09:05:57 +03:00
Riku Rauhala f4e32e2186 [GitHub Actions] Run staging.yaml in ts-frontend branch 2024-08-09 08:55:03 +03:00
Riku Rauhala ae4133e97c [Frontend] Remove eslint dependencies 2024-08-08 21:01:06 +03:00
Riku Rauhala fbfcebf74a [Frontend] Resolve ts file imports 2024-08-08 14:16:48 +03:00
Riku Rauhala 5def6898ce [Frontend] Configure TypeScript 2024-08-08 14:01:16 +03:00
Riku Rauhala 95abbebe70 [Frontend] Install ts dependencies 2024-08-08 13:07:42 +03:00
Riku Rauhala ecd1b2d42a [Backend] Remove @type/cron (deprecated, not necessary) 2024-08-08 12:22:51 +03:00
Riku Rauhala a36eb4e010 [Backend] Type events 2024-08-08 11:00:13 +03:00
Riku Rauhala 0c2ea7c42d [Backend] Convert events into TypeScript 2024-08-08 09:39:46 +03:00
Riku Rauhala d4e786094f [Backend] Install types for cron 2024-08-08 09:38:57 +03:00
Riku Rauhala 055bd7e00c [Backend] Type routes/faculties 2024-08-07 16:46:06 +03:00
Riku Rauhala ed621834c2 [Backend] Convert routes/faculties into TypeScript 2024-08-07 16:45:33 +03:00
Riku Rauhala 2cbbf3330d [Backend] Extract type StatsType 2024-08-07 14:19:18 +03:00
Riku Rauhala 16e6a07143 [Backend] Fix linter issues 2024-08-07 11:37:16 +03:00
Riku Rauhala 7b0b200775 [Backend] Extract faculty types 2024-08-07 11:35:25 +03:00
Riku RauhalaandGitHub 4d9c4d66da Merge pull request #4594 from UniversityOfHelsinkiCS/OOD-43-basic-auth-for-importerclient-in-fd-environment
OOD-43: basic auth for importerclient optional
2024-08-07 11:26:23 +03:00
Riku Rauhala 9106817d8f [Backend] Type updater router 2024-08-07 10:37:43 +03:00
Riku Rauhala 3547932508 [Backend] Type sisUpdaterService 2024-08-07 10:36:37 +03:00
Riku Rauhala c1015119ec [Backend] Convert sisUpdaterService to TypeScript 2024-08-07 09:29:04 +03:00
Riku Rauhala 8cdf83772e [tsconfig] Disable error truncation
This allows seeing the details of larger types when debugging
2024-08-06 18:42:37 +03:00
Riku Rauhala d033b40ac4 [Backend] Fix types 2024-08-06 18:41:22 +03:00
Riku Rauhala 3d904185c0 [Backend] Type facultyService 2024-08-06 18:08:45 +03:00
Jan Rundt 401e3e7c27 OOD-43: debugging test fail #6 2024-08-06 12:11:23 +03:00
Jan Rundt b78a29c26f OOD-43: debugging test fail #5 2024-08-06 12:02:57 +03:00
Jan Rundt 2a7307320a OOD-43: debugging test fail #4 2024-08-06 11:14:07 +03:00
Jan Rundt 808923023f OOD-43: debugging test fail #3 2024-08-06 11:08:27 +03:00
Jan Rundt ff6999f0b3 OOD-43: debugging test fail #2 2024-08-06 10:35:25 +03:00
Jan Rundt 8dd55fa03e OOD-43: debugging test fail 2024-08-06 10:26:28 +03:00
Jan Rundt 7e698f62a0 Merge branch 'master' into OOD-43-basic-auth-for-importerclient-in-fd-environment 2024-08-06 10:14:33 +03:00
Riku Rauhala 778cf988e5 [Backend] Improve teacher typing 2024-08-06 09:16:22 +03:00
Riku RauhalaandGitHub 111b11179d Merge pull request #4598 from UniversityOfHelsinkiCS/type-teachers
Type teachers
2024-08-05 18:19:46 +03:00
Riku Rauhala 0bb12b667b [Backend] Add missing types 2024-08-05 18:08:54 +03:00
Riku Rauhala 660f797aca [Backend] Finish typing teachers 2024-08-05 17:53:57 +03:00
Riku Rauhala abc9035e84 Merge branch 'master' into type-teachers 2024-08-05 16:01:19 +03:00
Jan Rundt a3797c5605 OOD-43: better readibility for if clause 2024-08-05 14:26:13 +03:00
Riku Rauhala 9e35cee053 [Backend] Type facultyUpdates 2024-08-05 14:12:15 +03:00
Riku Rauhala 9075e0dd95 [Backend] Type facultyCredits 2024-08-05 12:59:59 +03:00
Riku Rauhala 7aaff17d0d [Backend] Type openUniStats 2024-08-05 11:16:22 +03:00
Riku Rauhala 3321327df2 [Backend] Fix open uni search missing dates 2024-08-05 11:16:22 +03:00
renovate[bot]andGitHub 73b5ca1477 Update docker/build-push-action action to v6 2024-08-05 07:02:33 +00:00
Riku RauhalaandGitHub a9026b1085 Merge pull request #4593 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-08-05 09:52:27 +03:00
renovate[bot]andGitHub 66ddd4c6b3 Update all non-major dependencies 2024-08-05 06:41:25 +00:00
Riku Rauhala 938ccec288 [Backend] Mark combinedProgramme as possibly null 2024-08-02 19:04:08 +03:00
Riku Rauhala 10c8a61c04 [Backend] Type analyticsService 2024-08-02 18:56:14 +03:00
Riku Rauhala 5a9c62c8f4 [Backend] Type studyProgramme route requests 2024-08-02 18:56:14 +03:00
Riku Rauhala 63c7b7fff2 [Backend] Type studyProgrammePin service 2024-08-02 18:56:14 +03:00
Valtteri Kantanen 29d28a484c [University] Fix page crashing 2024-08-02 17:40:16 +03:00
Jan Rundt d165c313d1 OOD-43: dev things away 2024-08-02 16:02:29 +03:00
Jan Rundt cf328a7714 OOD-43: basic auth for importerclient optional 2024-08-02 16:00:04 +03:00
Valtteri Kantanen 557094a53f [Filters] Fix tests 2024-08-02 14:32:27 +03:00
Valtteri Kantanen 4bbc0a40a3 [Class statistics] Fix encrypted student check 2024-08-02 14:32:11 +03:00
Valtteri Kantanen acd308fbae [Filters] Study track filter: Remove the other filtering based on current time 2024-08-02 14:31:36 +03:00
Valtteri Kantanen 36236766e1 [Study programmes] Don't show programmes before/after graduation section if there are none 2024-08-02 13:43:07 +03:00
Valtteri Kantanen c98d575b63 [Backend] Match versions in package.json and package-lock.json 2024-08-02 13:41:38 +03:00
Valtteri Kantanen 112a52b8ec [Backend] Add a common job for refreshing programme and faculty data to make sure that programme data is refreshed before faculties 2024-08-02 13:41:38 +03:00
Riku Rauhala 4c82c79f5b [Class statistics] Acually fix crashing 2024-08-02 13:35:13 +03:00
Riku Rauhala dadf5d05cd [Class statistics] Fix crashing 2024-08-02 13:26:11 +03:00
Riku Rauhala bfddc55bf4 Ignore local VS Code config files 2024-08-02 09:52:33 +03:00
Riku RauhalaandGitHub 6a41185e49 Merge pull request #4591 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-08-01 21:52:39 +03:00
Riku Rauhala 41eb4068b0 Revert [Backend] Type routes 2024-08-01 17:10:26 +03:00
Riku Rauhala 267d61bd76 [Backend] Type routes 2024-08-01 17:04:07 +03:00
Riku Rauhala 37a0584b4f [Backend] Install types for compression and cors 2024-08-01 17:02:34 +03:00
Riku Rauhala aafdcbd8f5 [Backend] Add Request and Response types to remaining routes
Let's add the types for consistency
2024-08-01 15:23:16 +03:00
Riku Rauhala 6644c7c0be [Backend] Change the way Request is extended with user 2024-08-01 14:38:50 +03:00
renovate[bot]andGitHub 92554d2b2a Update all non-major dependencies 2024-08-01 08:52:17 +00:00
Valtteri Kantanen a36ffefdee [Study programme overview] Fix credits produced table years 2024-08-01 10:52:52 +03:00
Riku Rauhala 5144ded031 [ESLint] Ban variable name d 2024-08-01 10:48:40 +03:00
Riku Rauhala b04c4db365 [ESLint] Ban variable name t 2024-08-01 10:48:40 +03:00
Riku Rauhala 4759fbc42b [ESLint] Ban variable name c 2024-08-01 10:48:40 +03:00
Valtteri Kantanen b9e95f7405 [Backend] Update number of attempts from 1 to 3 for BullMQ jobs 2024-08-01 10:28:53 +03:00
Valtteri Kantanen 8932d7938a [Backend] Change the order of daily jobs 2024-08-01 10:25:12 +03:00
Riku Rauhala 4c686f6a58 [Backend] Rename a function 2024-07-31 18:38:25 +03:00
Riku Rauhala 1f4990f160 [Backend] Add missing usage cases of enum CreditTypeCode 2024-07-31 18:31:36 +03:00
Riku RauhalaandGitHub 862931292b Merge pull request #4590 from UniversityOfHelsinkiCS/type-populations
Type populations
2024-07-31 18:17:59 +03:00
Riku Rauhala 383bd22810 [Backend] Type populations 2024-07-31 18:05:08 +03:00
Riku Rauhala 9839ff3c8a [Backend] Extract type UnifyStatus 2024-07-31 18:05:08 +03:00
Riku Rauhala c66fd1d504 [Backend] Extract type Criteria 2024-07-31 18:05:08 +03:00
Riku Rauhala fd8eee5b9a [Backend] Improve populations/shared typing 2024-07-31 18:05:08 +03:00
Riku Rauhala 032b1f3fb9 [Backend] Refactor getStudentsIncludeCoursesBetween 2024-07-31 18:05:08 +03:00
Riku Rauhala ffde4616dc [Backend] Refactor and type getStudentsIncludeCoursesBetween 2024-07-31 18:05:08 +03:00
Riku Rauhala b01b157cad [Backend] Move enum Unification to types 2024-07-31 18:05:08 +03:00
Valtteri Kantanen 676d9dd01b [Faculties] Update headers in 'Students of the faculty' table 2024-07-31 17:04:16 +03:00
Valtteri Kantanen 51d1f6baa5 [Faculties, study programmes] Update tooltip texts and appearance 2024-07-31 17:03:08 +03:00
Valtteri Kantanen e320c2b27c [Faculties] Fix exceptional goal times for certain degrees 2024-07-31 14:26:20 +03:00
Valtteri Kantanen 5b426e3a5a [Faculties] Fix graduation times calculation in case not all programmes have stats for the same years 2024-07-31 14:25:15 +03:00
Valtteri Kantanen d7af692cde [Faculties] Fix tests 2024-07-31 13:17:25 +03:00
Valtteri Kantanen ce85a67a66 [Faculties, evaluation overview] Fix graduation times view crashing when toggling 'Group by' option with one year's stats open 2024-07-31 12:38:12 +03:00
Valtteri Kantanen b5f8e44a50 [Faculties, evaluation overview] Remove licentiate section from graduation times (#4299) 2024-07-31 12:38:12 +03:00
Valtteri Kantanen 82f73ca67b [Backend] Big refactor: Graduation times 2024-07-31 12:38:12 +03:00
Valtteri KantanenandGitHub 2449c81f4b Merge pull request #4588 from UniversityOfHelsinkiCS/OOD-41-all-service-provider-env-variables-etc-lower-case
OOD-41: service provider variables in lower case
2024-07-31 12:10:26 +03:00
Jan Rundt f7b0cc2940 OOD-41: lint fix 2024-07-31 11:45:59 +03:00
Jan Rundt 5e09869c74 OOD-41: lint fix 2024-07-31 10:57:17 +03:00
Jan Rundt 91d659e810 OOD-41: for backwards compatibility 2024-07-31 10:51:54 +03:00
Riku Rauhala e592fa3c5e [Backend] Improve creditsAndEnrollmentsOfCourse 2024-07-30 18:48:56 +03:00
Riku RauhalaandGitHub ea56bd2fed Merge pull request #4589 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-07-30 18:35:33 +03:00
renovate[bot]andGitHub 4588788d75 Update all non-major dependencies 2024-07-30 14:25:57 +00:00
Riku Rauhala 0bcfb406c6 [Backend] Refactor courseStatsCounter 2024-07-30 17:17:11 +03:00
Valtteri Kantanen 1ee0541299 Rename OODIKONE_SENTRY_DSN to OODIKONE_FRONTEND_SENTRY_DSN 2024-07-30 16:55:21 +03:00
Valtteri Kantanen 22ce1cd6f0 Fix backend url prefix 2024-07-30 16:54:49 +03:00
Riku Rauhala 875d72b5ad [Backend] Fix linter issues 2024-07-30 16:50:59 +03:00
Riku RauhalaandGitHub 599e316884 Merge pull request #4587 from UniversityOfHelsinkiCS/type-courses
Type courses
2024-07-30 16:45:36 +03:00
Riku Rauhala f60aef096d [Backend] Type courseYearlyStatsCounter 2024-07-30 16:33:45 +03:00
Valtteri Kantanen f429d94656 Update Sentry project names 2024-07-30 14:03:03 +03:00
Valtteri Kantanen 067e83708d Separate frontend and backend Sentry projects 2024-07-30 13:50:45 +03:00
Riku Rauhala 7f7a572751 [Backend] Revert a variable name causing issues 2024-07-30 13:41:14 +03:00
Jan Rundt e9ff756610 OOD-41: also in compose file 2024-07-30 13:39:11 +03:00
Jan Rundt 3e11475c0d OOD-41: service provider variables in lower case 2024-07-30 13:35:30 +03:00
Valtteri KantanenandGitHub 24579c20c7 Merge pull request #4586 from UniversityOfHelsinkiCS/OOD-13-no-winstonGelf-for-fd
OOD-13: no winston gelf for fd
2024-07-30 12:56:31 +03:00
Jan Rundt 0966a04933 OOD-13: env variable lower case for new env variables 2024-07-30 12:48:02 +03:00
Riku Rauhala a7235940a0 [Backend] Type services/courses 2024-07-30 11:06:46 +03:00
Jan Rundt 40b27cb40a OOD-13: fixed lint errors 2024-07-30 10:39:10 +03:00
Riku Rauhala 6346966604 [Backend] Remove incorrect typing 2024-07-29 21:41:54 +03:00
Riku Rauhala d32f8e40ea [Backend] Simplify queries
Studentnumber and is_open are never null so these parts of the queries are redundant. Also, TypeScript wouldn't shut up about it
2024-07-29 21:39:17 +03:00
Riku Rauhala f9875b84ca [Backend] Type creditsAndEnrollmentsOfCourse 2024-07-29 21:29:31 +03:00
Riku Rauhala 19239ba6e4 [Backend] Move isOpenUniCourseCode to util 2024-07-29 21:28:16 +03:00
Riku Rauhala c5a6d2bd0b [Backend] Type routes/courses 2024-07-29 21:27:27 +03:00
Riku Rauhala 904d127a60 [Backend] Improve courseStatsCounter 2024-07-29 21:22:27 +03:00
Riku Rauhala 144a095baa [Backend] Type courseStatsCounter 2024-07-29 21:18:53 +03:00
Valtteri Kantanen 9aa1bdf383 [Faculties] Big refactor: Students and graduated of the faculty 2024-07-29 18:13:00 +03:00
Jan Rundt b03243104c OOD-13: no winston gelf for fd 2024-07-29 16:25:43 +03:00
Riku Rauhala afa6639987 [Backend] Extract type CourseWithSubsId 2024-07-29 15:41:48 +03:00
Valtteri Kantanen 2f5716c1be [Faculties] Fix tests 2024-07-29 14:48:09 +03:00
Valtteri Kantanen 17aca47378 [Faculties] Big refactor: Thesis writers of the faculty 2024-07-29 13:56:03 +03:00
Riku Rauhala 5f794339e0 [Backend] Use enum ElementDetailType in a query 2024-07-29 13:08:00 +03:00
Riku Rauhala 7b34d09452 [Backend] Use enum PriorityCode in a query 2024-07-29 13:06:07 +03:00
Riku Rauhala 379f918d6e [ESLint] Disable lines-between-class-members rule 2024-07-29 11:15:31 +03:00
Riku RauhalaandGitHub f079565232 Merge pull request #4535 from UniversityOfHelsinkiCS/renovate/react-redux-9.x
Update dependency react-redux to v9
2024-07-29 10:45:46 +03:00
Riku RauhalaandGitHub 4ac083e280 Merge pull request #4524 from UniversityOfHelsinkiCS/renovate/knex-3.x
Update dependency knex to v3
2024-07-29 10:39:59 +03:00
Valtteri Kantanen 482d44b276 [Backend] Combine updating close to graduation data with other daily jobs 2024-07-29 10:18:25 +03:00
Valtteri Kantanen 8dc553fd08 [Class statistics] Fix key error 2024-07-29 10:18:25 +03:00
Valtteri Kantanen 16bff7b28a [Study programmes] Replace a duplicate route 2024-07-29 10:18:25 +03:00
Valtteri Kantanen fcc9881dd7 [Faculties] Clean up facultyUpdates.js 2024-07-29 10:18:25 +03:00
Riku RauhalaandGitHub 8e20da6f81 Merge pull request #4585 from UniversityOfHelsinkiCS/renovate/all-minor-patch
Update all non-major dependencies
2024-07-29 10:13:35 +03:00
renovate[bot]andGitHub b3c51cd8c5 Update all non-major dependencies 2024-07-29 07:03:14 +00:00
Riku Rauhala 58b2b4092b [Eslint] Add id-denylist for ambiguous variable names 2024-07-29 09:47:03 +03:00
Riku Rauhala 58df06b5c8 [Backend] Type tags 2024-07-26 20:46:00 +03:00
Riku Rauhala eb24ec1159 [Backend] Begin to type teachers 2024-07-26 19:17:27 +03:00
renovate[bot]andGitHub dc86a75fc1 Update dependency react-redux to v9 2024-06-10 06:25:21 +00:00
renovate[bot]andGitHub 951bb6b002 Update dependency knex to v3 2024-06-10 06:25:12 +00:00
654 changed files with 27974 additions and 23555 deletions
+4 -109
View File
@@ -1,111 +1,6 @@
// ! If we don't have this file, running ESLint will fail because of the import/no-unused-modules rule
// ! See: https://github.com/import-js/eslint-plugin-import/issues/3079
// ! When this issue is resolved, this file can be removed
{
"env": {
"commonjs": true,
"es2021": true,
"node": true
},
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "commonjs"
},
"extends": [
"eslint:recommended",
"airbnb",
"plugin:cypress/recommended",
"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/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",
"no-continue": "off",
"no-param-reassign": "off",
"no-plusplus": "off",
"no-promise-executor-return": "off",
"no-restricted-syntax": [
"error",
{
"selector": "ForInStatement",
"message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
},
"LabeledStatement",
"WithStatement"
],
"no-return-await": "off",
"no-shadow": "off",
"prettier/prettier": "error",
"quotes": ["error", "single", { "avoidEscape": true }]
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".jsx", ".ts", ".tsx"]
}
}
},
"ignorePatterns": ["build/", "dist/", "instrumented/", "node_modules/", "**/*migrations*/**"]
"ignorePatterns": ["**/node_modules/", "**/dist/", "**/build/"]
}
+21
View File
@@ -0,0 +1,21 @@
name: 'Lint code'
description: 'Runs ESLint and Stylelint for frontend and backend'
runs:
using: composite
steps:
- uses: actions/setup-node@v4
with:
node-version: 20
- name: 'Install dependencies'
shell: bash
run: npm ci && cd services/frontend && npm ci && cd ../backend && npm ci
- name: 'Create symlink for shared'
shell: bash
run: ln -s $PWD/services/shared $PWD/services/backend/src/shared && ln -s $PWD/services/shared $PWD/services/frontend/src/shared
- name: 'Run Stylelint for CSS files'
shell: bash
run: npm run stylelint "services/frontend/src/**/*.css"
- name: 'Run ESLint for JS and TS files'
shell: bash
run: npm run eslint -- services
+5 -2
View File
@@ -11,6 +11,9 @@ inputs:
spec:
required: true
description: 'Path to Cypress file'
test_name:
required: true
description: 'Name of the test'
runs:
using: composite
@@ -35,10 +38,10 @@ runs:
CYPRESS_baseUrl: http://localhost:1337
- name: 'Upload video if tests fail'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: failure()
with:
name: Cypress videos
name: 'Cypress videos - ${{ inputs.test_name }}'
path: ./cypress/videos/*
- name: 'Print logs on failure'
+47 -17
View File
@@ -26,14 +26,14 @@ jobs:
with:
image: oodikone-frontend
tags: production ${{ github.sha }}
context: services/frontend
context: services
containerfiles: |
services/frontend/Dockerfile
build-args: |
BUILT_AT=${{ steps.get_current_time.outputs.NOW }}
SENTRY_RELEASE=${{ steps.extract_version.outputs.VERSION }}
SENTRY_ENVIRONMENT=production
SENTRY_DSN=${{ secrets.OODIKONE_SENTRY_DSN }}
SENTRY_DSN=${{ secrets.OODIKONE_FRONTEND_SENTRY_DSN }}
- name: Push to quay.io
uses: redhat-actions/push-to-registry@v2
@@ -61,7 +61,7 @@ jobs:
with:
image: oodikone-backend
tags: production ${{ github.sha }}
context: services/backend
context: services
containerfiles: |
services/backend/Dockerfile
build-args: |
@@ -77,8 +77,8 @@ jobs:
username: toska+github
password: ${{ secrets.QUAY_IO_TOKEN }}
sentry_release:
name: 'Create Sentry release'
sentry_release_frontend:
name: 'Create Sentry release for frontend'
runs-on: ubuntu-latest
needs:
- release_frontend
@@ -94,12 +94,40 @@ jobs:
- name: Pull frontend image and create a container
run: docker create --name frontend quay.io/toska/oodikone-frontend:production
- 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: Create Sentry release
uses: getsentry/action-release@v1
env:
SENTRY_URL: https://toska.cs.helsinki.fi/
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_TOKEN }}
SENTRY_ORG: toska
SENTRY_PROJECT: oodikone-frontend
with:
environment: production
set_commits: 'skip'
sourcemaps: './build/assets'
url_prefix: '~/assets'
version: ${{ steps.extract_version.outputs.VERSION }}
sentry_release_backend:
name: 'Create Sentry release for backend'
runs-on: ubuntu-latest
needs:
- release_frontend
- release_backend
steps:
- uses: actions/checkout@v4
- name: Extract release version
id: extract_version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Pull backend image and create a container
run: docker create --name backend quay.io/toska/oodikone-backend:production
- name: Copy assets from the backend container
run: docker cp backend:/opt/app-root/src/dist ./dist
@@ -109,27 +137,28 @@ jobs:
SENTRY_URL: https://toska.cs.helsinki.fi/
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_TOKEN }}
SENTRY_ORG: toska
SENTRY_PROJECT: oodikone
SENTRY_PROJECT: oodikone-backend
with:
environment: production
set_commits: 'skip'
sourcemaps: './build/assets ./dist'
url_prefix: '~/assets'
sourcemaps: './dist'
url_prefix: '~/opt/app-root/src/dist/'
version: ${{ steps.extract_version.outputs.VERSION }}
report_success:
name: 'Report deployment success to oodikone notifications'
name: 'Report deployment success to the "oodikone_notifications" channel'
runs-on: ubuntu-latest
needs:
- release_frontend
- release_backend
- sentry_release
- sentry_release_frontend
- sentry_release_backend
if: success()
steps:
- uses: actions/checkout@v4
- name: 'Send notification to slack'
- name: 'Send notification to Slack'
uses: UniversityOfHelsinkiCS/slackbot-action@v1.5
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
@@ -138,18 +167,19 @@ jobs:
deployment-target: production
report_failure:
name: 'Report deployment failure to oodikone notifications'
name: 'Report deployment failure to the "oodikone_notifications" channel'
runs-on: ubuntu-latest
needs:
- release_frontend
- release_backend
- sentry_release
- sentry_release_frontend
- sentry_release_backend
if: failure()
steps:
- uses: actions/checkout@v4
- name: 'Send notification to slack'
- name: 'Send notification to Slack'
uses: UniversityOfHelsinkiCS/slackbot-action@v1.5
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
+7 -9
View File
@@ -18,15 +18,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: 'Install dependencies'
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: Lint backend and frontend
uses: ./.github/actions/lint
test_end_to_end:
runs-on: ubuntu-latest
@@ -62,6 +55,10 @@ jobs:
spec: cypress/e2e/Language_center.js
- name: 'Test study guidance groups'
spec: cypress/e2e/Study_guidance_groups.js
- name: 'Test evaluation overview'
spec: cypress/e2e/Evaluation_overview.js
- name: 'Test university'
spec: cypress/e2e/University.js
name: ${{ matrix.name }}
steps:
- uses: actions/checkout@v4
@@ -71,3 +68,4 @@ jobs:
username: ${{ secrets.TOSKAREGISTRY_USERNAME }}
password: ${{ secrets.TOSKAREGISTRY_PASSWORD }}
spec: ${{ matrix.spec }}
test_name: ${{ matrix.name }}
+9 -9
View File
@@ -5,16 +5,16 @@ on:
branches:
- master
paths:
- '.github/workflows/test.yaml'
- '.github/workflows/staging.yaml'
- '.github/actions/**'
- '.github/workflows/staging.yaml'
- '.github/workflows/test.yaml'
- 'cypress/**'
- 'cypress.config.json'
- 'services/**'
- '!services/backend/src/rapodiff/**'
- 'updater/sis-updater-worker/src/db/migrations/**'
- 'docker-compose.ci.yml'
- 'package*.json'
- 'updater/sis-updater-worker/src/db/migrations/**'
jobs:
test:
@@ -41,7 +41,7 @@ jobs:
with:
image: oodikone-frontend
tags: staging ${{ github.sha }}
context: services/frontend
context: services
containerfiles: |
services/frontend/Dockerfile
build-args: |
@@ -72,7 +72,7 @@ jobs:
with:
image: oodikone-backend
tags: staging ${{ github.sha }}
context: services/backend
context: services
containerfiles: |
services/backend/Dockerfile
build-args: |
@@ -90,7 +90,7 @@ jobs:
password: ${{ secrets.QUAY_IO_TOKEN }}
report_success:
name: 'Report deployment success to oodikone notifications'
name: 'Report deployment success to the "oodikone_notifications" channel'
runs-on: ubuntu-latest
needs:
- release_frontend
@@ -100,7 +100,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: 'Send notification to slack'
- name: 'Send notification to Slack'
uses: UniversityOfHelsinkiCS/slackbot-action@v1.5
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
@@ -109,7 +109,7 @@ jobs:
deployment-target: staging
report_failure:
name: 'Report deployment failure to oodikone notifications'
name: 'Report deployment failure to the "oodikone_notifications" channel'
runs-on: ubuntu-latest
needs:
- release_frontend
@@ -119,7 +119,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: 'Send notification to slack'
- name: 'Send notification to Slack'
uses: UniversityOfHelsinkiCS/slackbot-action@v1.5
with:
webhook-url: ${{ secrets.WEBHOOK_URL }}
+78 -36
View File
@@ -17,15 +17,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: 'Install dependencies'
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 and TS files'
run: npm run eslint -- --ext .js,.jsx,.ts,.tsx services --report-unused-disable-directives
- name: Lint backend and frontend
uses: ./.github/actions/lint
# === Build database images ===
build_backend:
@@ -35,32 +28,77 @@ jobs:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: 'Build image and export it'
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
build-args: |
SENTRY_RELEASE=${{ github.sha }}
SENTRY_ENVIRONMENT=staging
context: services/backend
context: services
file: services/backend/Dockerfile
push: false
tags: oodikone-backend:staging
outputs: type=docker,dest=/tmp/backend.tar
env:
DOCKER_BUILD_SUMMARY: false
- name: 'Upload image for other jobs'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: backend-image
path: /tmp/backend.tar
build_and_push_anon_sis_db:
name: 'Build anon sis-db and push to toska hub if new migrations have been added'
build_frontend:
name: 'Build frontend'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
- name: Get current time
id: get_current_time
run: echo "NOW=$(date --iso-8601=seconds)" >> $GITHUB_OUTPUT
- name: Build frontend image
id: build-image
uses: redhat-actions/buildah-build@v2
with:
image: oodikone-frontend
tags: staging ${{ github.sha }}
context: services
containerfiles: |
services/frontend/Dockerfile
build-args: |
BUILT_AT=${{ steps.get_current_time.outputs.NOW }}
BASE_PATH=/oodikone
STAGING=true
check_migration_files:
name: Check if new migrations have been added
runs-on: ubuntu-latest
outputs:
kone_db_migrations: ${{ steps.check_migrations.outputs.kone_db_migrations }}
user_db_migrations: ${{ steps.check_migrations.outputs.user_db_migrations }}
sis_db_migrations: ${{ steps.check_migrations.outputs.sis_db_migrations }}
steps:
- uses: actions/checkout@v4
- name: Check if new migrations have been added
id: check_migrations
uses: dorny/paths-filter@v3
with:
filters: |
migrations:
kone_db_migrations:
- 'services/backend/src/database/migrations_kone/**'
- '.github/workflows/test.yaml'
user_db_migrations:
- 'services/backend/src/database/migrations_user/**'
- '.github/workflows/test.yaml'
sis_db_migrations:
- 'updater/sis-updater-worker/src/db/migrations/**'
- '.github/workflows/test.yaml'
build_and_push_anon_sis_db:
name: 'Build anon sis-db and push to toska hub if new migrations have been added'
needs: check_migration_files
runs-on: ubuntu-latest
if: needs.check_migration_files.outputs.sis_db_migrations == 'true'
steps:
- uses: actions/checkout@v4
- name: 'Login to toska docker registry'
uses: docker/login-action@v3
with:
@@ -68,7 +106,6 @@ jobs:
username: ${{ secrets.username }}
password: ${{ secrets.password }}
- name: 'Let sis-updater-worker run migrations, then push new version of image to toska hub'
if: steps.check_migrations.outputs.migrations == 'true'
run: |
docker compose --file docker-compose.ci.yml up --detach sis-updater-worker
sleep 30 # wait for sis-updater-worker to start and run migrations
@@ -78,22 +115,18 @@ jobs:
name: 'Build anon kone-db and push to toska hub if new migrations have been added'
needs:
- build_backend
- build_frontend
- check_migration_files
runs-on: ubuntu-latest
if: needs.check_migration_files.outputs.kone_db_migrations == 'true'
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: check_migrations
- name: 'Download backend image'
uses: actions/download-artifact@v4
with:
filters: |
migrations:
- 'services/backend/src/database/migrations_kone/**'
- '.github/workflows/test.yaml'
- name: 'Download images'
uses: actions/download-artifact@v3
with:
path: /tmp/
name: backend-image
- name: 'Load built backend image'
run: docker load --input /tmp/artifact/backend.tar
run: docker load --input backend.tar
- name: 'Login to toska docker registry'
uses: docker/login-action@v3
with:
@@ -101,17 +134,19 @@ jobs:
username: ${{ secrets.username }}
password: ${{ secrets.password }}
- name: 'Let backend run migrations, then push new version of image to toska hub'
if: steps.check_migrations.outputs.migrations == 'true'
run: |
docker compose --file docker-compose.ci.yml up --detach backend
sleep 30 # wait for sis-updater-worker to start and run migrations
sleep 30 # wait for backend to start and run migrations
./anonyymioodi/commit_and_push_to_toska_hub.sh kone-db
build_and_push_anon_user_db:
name: 'Build anon user-db and push to toska hub if new migrations have been added'
needs:
- build_backend
- build_frontend
- check_migration_files
runs-on: ubuntu-latest
if: needs.check_migration_files.outputs.user_db_migrations == 'true'
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
@@ -121,12 +156,12 @@ jobs:
migrations:
- 'services/backend/src/database/migrations_user/**'
- '.github/workflows/test.yaml'
- name: 'Download images'
uses: actions/download-artifact@v3
- name: 'Download backend image'
uses: actions/download-artifact@v4
with:
path: /tmp/
name: backend-image
- name: 'Load built backend image'
run: docker load --input /tmp/artifact/backend.tar
run: docker load --input backend.tar
- name: 'Login to toska docker registry'
uses: docker/login-action@v3
with:
@@ -134,10 +169,9 @@ jobs:
username: ${{ secrets.username }}
password: ${{ secrets.password }}
- name: 'Let backend run migrations, then push new version of image to toska hub'
if: steps.check_migrations.outputs.migrations == 'true'
run: |
docker compose --file docker-compose.ci.yml up --detach backend
sleep 30 # wait for sis-updater-worker to start and run migrations
sleep 30 # wait for backend to start and run migrations
./anonyymioodi/commit_and_push_to_toska_hub.sh user-db
# === End-to-end tests ===
@@ -147,6 +181,8 @@ jobs:
- build_and_push_anon_sis_db
- build_and_push_anon_kone_db
- build_and_push_anon_user_db
# Always run the tests, even if some of the needed jobs failed or were skipped
if: always()
strategy:
fail-fast: false
matrix:
@@ -177,6 +213,10 @@ jobs:
spec: cypress/e2e/Language_center.js
- name: 'Test study guidance groups'
spec: cypress/e2e/Study_guidance_groups.js
- name: 'Test evaluation overview'
spec: cypress/e2e/Evaluation_overview.js
- name: 'Test university'
spec: cypress/e2e/University.js
name: ${{ matrix.name }}
steps:
- uses: actions/checkout@v4
@@ -186,6 +226,7 @@ jobs:
username: ${{ secrets.username }}
password: ${{ secrets.password }}
spec: ${{ matrix.spec }}
test_name: ${{ matrix.name }}
# === Other tests ===
test_service_exit_statuses:
@@ -194,6 +235,7 @@ jobs:
- build_and_push_anon_sis_db
- build_and_push_anon_kone_db
- build_and_push_anon_user_db
if: always()
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+24 -11
View File
@@ -27,7 +27,7 @@ jobs:
- name: 'Install dependencies'
run: npm ci && cd updater/sis-updater-scheduler && npm ci && cd ../sis-updater-worker && npm ci
- name: 'Run linter'
run: npm run eslint updater -- --report-unused-disable-directives
run: npm run eslint -- updater
build_worker:
name: 'Build Updater worker'
@@ -36,15 +36,18 @@ jobs:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: 'Build the image'
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: updater/sis-updater-worker/
push: false
outputs: type=docker,dest=/tmp/worker.tar
tags: toska/sis-updater-worker:latest
- name: 'Store built image'
uses: actions/upload-artifact@v3
env:
DOCKER_BUILD_SUMMARY: false
- name: 'Upload image for other jobs'
uses: actions/upload-artifact@v4
with:
name: updater-worker-image
path: /tmp/worker.tar
build_scheduler:
@@ -54,15 +57,18 @@ jobs:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: 'Build the image'
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: updater/sis-updater-scheduler/
push: false
outputs: type=docker,dest=/tmp/scheduler.tar
tags: toska/sis-updater-scheduler:latest
- name: 'Store built image'
uses: actions/upload-artifact@v3
env:
DOCKER_BUILD_SUMMARY: false
- name: 'Upload image for other jobs'
uses: actions/upload-artifact@v4
with:
name: updater-scheduler-image
path: /tmp/scheduler.tar
test_updater:
@@ -81,13 +87,14 @@ jobs:
username: ${{ secrets.TOSKAREGISTRY_USERNAME }}
password: ${{ secrets.TOSKAREGISTRY_PASSWORD }}
- name: 'Download built images'
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
path: /tmp/
pattern: 'updater-{worker,scheduler}-image'
merge-multiple: true
- name: 'Load worker image'
run: docker load --input /tmp/artifact/worker.tar
run: docker load --input worker.tar
- name: 'Load scheduler image'
run: docker load --input /tmp/artifact/scheduler.tar
run: docker load --input scheduler.tar
- name: 'Start worker'
run: docker compose --file docker-compose.ci.yml up --detach --no-recreate sis-updater-worker
- name: 'Run hourly jobs'
@@ -128,6 +135,9 @@ jobs:
context: updater/sis-updater-worker
containerfiles: |
updater/sis-updater-worker/Dockerfile
build-args: |
SENTRY_RELEASE=${{ github.sha }}
SENTRY_ENVIRONMENT=production
- name: Push to quay.io
uses: redhat-actions/push-to-registry@v2
@@ -155,6 +165,9 @@ jobs:
context: updater/sis-updater-scheduler
containerfiles: |
updater/sis-updater-scheduler/Dockerfile
build-args: |
SENTRY_RELEASE=${{ github.sha }}
SENTRY_ENVIRONMENT=production
- name: Push to quay.io
uses: redhat-actions/push-to-registry@v2
+12 -3
View File
@@ -56,8 +56,17 @@ cypress/videos/
coverage/
instrumented/
# pycharm
# Pycharm
.idea
# fd specific
environments/
# VS Code
.vscode/settings.json
.vscode/tasks.json
.vscode/launch.json
.vscode/terminals.json
# Funidata specific
environments/
# Target of shared symlink
services/**/src/shared
+1 -1
View File
@@ -1,6 +1,6 @@
npx lint-staged
if git grep -qE --cached "(it|describe)\.only" $(git diff --cached --name-only) ./cypress; then
if git grep -qE --cached "(it|describe)\.only" $(git diff --cached --name-only --diff-filter=ACMR | xargs) ./cypress; then
echo "\e[0;31mDo not commit .only-tests."
exit 1
else
+37 -2
View File
@@ -191,7 +191,7 @@ Continuous integration (CI) works with Github actions and is defined in workflow
- Updater is deployed to production when pushed if tests pass
- Test databases can be updated, instructions in [anonyymioodi README](anonyymioodi/README.md)
[Rapodiff](./services/backend/src/rapodiff/) can be used to check differences between Rapo API and Oodikone data.
[Rapodiff](./services/backend/src/rapodiff/) can be used to check differences between Rapo and Oodikone data.
## ❓FAQ
@@ -199,7 +199,7 @@ Continuous integration (CI) works with Github actions and is defined in workflow
You should always install the dependencies **inside** the container to have the application **inside** the container access them. Module might be missing for example when someone else installs a new library and you only pull the changes in package.json. Use `docker exec <service> npm ci` to install modules inside the container or `npm run install:docker` to install modules inside all packages (backend, frontend, sis-updater-scheduler and sis-updater-worker).
### Studyguidance groups don't work on my machine
### Study guidance groups don't work on my machine
Make sure you have your VPN on.
@@ -209,6 +209,41 @@ First: Try `npm run cli` option 1: _Set up oodikone from scratch_ and then optio
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.
### How to run tests locally faster
Vite simplifies development but can be **very slow** when running tests. If you're curious about the reasons for this, you can read more in [this GitHub issue](https://github.com/cypress-io/cypress/issues/22968).
To speed up testing, you can use Vite's preview mode. This will make the tests run faster, but keep in mind that you must rebuild the frontend code every time you make changes to it. To use the preview mode, follow these steps:
1. Run `npm run build` in the `services/frontend` directory.
2. Run `npm run preview` in the `services/frontend` directory and keep it running.
3. Update the `baseUrl` in [cypress.config.js](./cypress.config.js) to `http://localhost:4173`.
4. Start Oodikone with `npm run oodikone`.
After these steps, you can run your tests:
- Use `npm run cypress open` to open the Cypress UI and run the tests interactively.
Tests can also be run in headless mode, similar to how they are run in the CI/CD pipeline. To do this, use:
```bash
npm run cypress:run -- --spec <file path>
```
For example:
```bash
npm run cypress:run -- --spec "cypress/e2e/Language_center.js"
```
Running tests in headless mode may also help if your browser keeps crashing when running tests interactively. If you want to run all tests, use:
```bash
npm run cypress:run
```
Remember to revert the `baseUrl` in [cypress.config.js](./cypress.config.js) to its original value when you're done.
## ✌🏼 Maintainers and contribution
[Toska](https://toska.dev) - University of Helsinki
+67 -13
View File
@@ -1,18 +1,72 @@
# Anonyymioodi - test database setup
This folder contains a guide and scripts to modify or create anonymous database images for oodikone, mainly used for testing.
This folder contains a guide and scripts to modify or create anonymous database images for Oodikone, mainly used for testing.
This quick guide should be enough for most purposes: Updating schema, data or postgres version. If not, a more elaborate guide is stored in [old readme](old_readme.md) (though it may have some outdated info).
The quick guide should be enough for most purposes: updating schema, data, or the PostgreSQL version. If you need to generate completely new `sis-importer-db` and `sis-db` images (which shouldn't happen too often), follow the instructions in the section [Generate new sis-importer-db and sis-db completely from scratch](#generate-new-sis-importer-db-and-sis-db-completely-from-scratch).
### Quick guide to update a test database
## Quick guide to updating a test database
- Make sure you have the latest test-db. Just pulling images doesn't seem to replace your already existing volume, so you may need to remove it by hand. This may work: `docker volume rm oodikone_sis-db-data`. The new image including data should be pulled next time you launch oodikone normally.
- Do your dev stuff normally with the test database, for example with `npm run both`. Edit data however you like, run updater via frontend for example, then run tests to see if they pass.
- When you're happy with the database, create a dump:
- `docker exec -i <dbname> pg_dump -Fc -U postgres <dbname> > anonyymioodi/<dbname>.sqz`
- Now you can shut it down `npm run docker:down`
- Navigate to this folder `cd anonyymioodi`
- If you need to **update postgres version**, do it now by changing it in this directory's `docker-compose.yml`.
- Create new container from dump `./create_container_from_dump.sh <dbname>`
- Commit and push it to toska registry `./commit_and_push_to_toska_hub.sh <dbname>`
- `docker compose down`
1. Ensure you have the latest test-db. Just pulling images might not replace your existing volume, so you may need to remove it manually. This command might work: `docker volume rm oodikone_sis-db-data` (replace `oodikone_sis-db-data` with the correct volume name). The new image, including data, should be pulled the next time you launch Oodikone normally.
2. Do your dev work as usual with the test database, for example, using `npm run both`. Edit data as needed, (for example, run the updater via the frontend), and then run tests to see if they pass (follow the instructions in the [README](../README.md) to run the tests more quickly).
3. If you're updating `sis-db`:
- Modify the scheduler limit in [config.js](../updater/sis-updater-scheduler/src/config.js) by setting `module.exports.DEV_SCHEDULE_COUNT = null`.
- Follow the `sis-updater-worker` logs to ensure everything is running smoothly (`docker logs --follow oodikone_sis-updater-worker_1`).
4. When you're satisfied with the database, create a dump:
- Use the command: `docker exec -i <dbname> pg_dump -Fc -U postgres <dbname> > anonyymioodi/<dbname>.sqz`.
5. Shut down the environment with `npm run docker:down`.
6. Navigate to this folder: `cd anonyymioodi`.
7. If you need to update the PostgreSQL version, do it now by changing the version in this directory's `docker-compose.yml`. The version should match the one used on the server to avoid compatibility issues.
8. Create a new container from the dump: `./create_container_from_dump.sh <dbname>`.
9. Commit and push it to the Toska registry: `./commit_and_push_to_toska_hub.sh <dbname>`.
10. Bring down the environment with `docker compose down`.
## Generate new sis-importer-db and sis-db completely from scratch
> [!CAUTION]
> The more the data in `sis-importer-db` and `sis-db` changes, the more tests might break. Because of this, it's highly recommended to make as few changes as possible (for example, avoid changing the degree programs whose students are used in the test data).
>
> If you only need to make small changes, follow the quick guide above.
### Sis-importer-db
1. To generate a sample dump from test Sisu data, follow the instructions at [importer-db-staging-sampletaker](https://github.com/UniversityOfHelsinkiCS/sis-importer/tree/master/importer-db-staging-sampletaker) inside the `sis-importer` repo.
2. Copy `sis-importer-db.sqz` into this folder.
3. Run the following commands (in this folder) to create a new image from the dump and push it to the Toska registry:
```bash
./create_container_from_dump.sh sis-importer-db
./commit_and_push_to_toska_hub.sh sis-importer-db
docker compose down
```
### Sis-db
1. To ensure the newest `sis-importer-db` image is used:
- Delete all `sis-importer-db` images you have locally (list images with `docker images`, delete with `docker rmi <image ID>`).
- Also, delete the old `sis-importer-db` volume (`docker volume rm sis-importer-db-data`).
2. Start Oodikone and Updater with `npm run both` (this will pull the newest `sis-importer-db` image you just pushed in the previous step).
3. Truncate all tables in `sis-db` **_except for `migrations` and `semesters`_** (the easiest way is to use Adminer).
4. Modify the scheduler limit in [config.js](../updater/sis-updater-scheduler/src/config.js) by setting `module.exports.DEV_SCHEDULE_COUNT = null`.
5. Navigate to <http://localhost:3000/updater> and click the three buttons (Update meta, Update students, Update curriculums) one by one. This will populate `sis-db` with data from the new `sis-importer-db`. You might want to do each update a couple of times, as the database tables affect each other, and not all necessary data might be generated on the first try.
6. Follow the `sis-updater-worker` logs to ensure everything is running smoothly (`docker logs --follow oodikone_sis-updater-worker_1`). If you see any errors, try clicking the buttons again.
7. Run all the tests to ensure everything is working as expected (follow the instructions in the [README](../README.md) to run the tests more quickly). If any tests fail, update the test files accordingly.
+4 -10
View File
@@ -9,9 +9,7 @@ services:
PGDATA: /data
POSTGRES_DB: kone-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
profiles:
- oodikone
image: postgres:15.8
sis-db:
<<: *defaults
@@ -20,7 +18,7 @@ services:
PGDATA: /data
POSTGRES_DB: sis-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
image: postgres:15.8
shm_size: 2gb
sis-importer-db:
@@ -30,9 +28,7 @@ services:
PGDATA: /data
POSTGRES_DB: sis-importer-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
profiles:
- updater
image: postgres:15.8
user-db:
<<: *defaults
@@ -41,6 +37,4 @@ services:
PGDATA: /data
POSTGRES_DB: user-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
profiles:
- oodikone
image: postgres:15.8
-133
View File
@@ -1,133 +0,0 @@
-- add these to sis-importer db by running:
-- docker exec -i sis-importer-db psql -U postgres sis-importer-db < enrich_sampledata.sql
-- if you need to run updater for these students only, see studentnumberlist in the end
-- this file
-- Add admission types (valintaperusteet) to studyrights for some of the computer
-- science 2018-2019 population students. See person_ids / student_number below
-- (in case you need to update dumps).
-- TODO: this should be moved into the sampledata creator script in
-- importer repo, but since this file contains studentnumbers and person ids, it's
-- better to keep this here
-- KP (koepisteet)
-- hy-hlo-1412620 | 013683126
-- hy-hlo-1485246 | 014340523
-- hy-hlo-32230770 | 010589388
-- hy-hlo-35626233 | 010775888
-- hy-hlo-40150144 | 011065995
UPDATE studyrights
SET admission_type_urn = 'urn:code:admission-type:kp'
WHERE person_id IN (
'hy-hlo-1412620',
'hy-hlo-1485246',
'hy-hlo-32230770',
'hy-hlo-35626233',
'hy-hlo-40150144'
);
-- TV (todistusvalinta)
-- hy-hlo-45783433 | 011321150
-- hy-hlo-48537946 | 010764653
-- hy-hlo-49801905 | 011194400
-- hy-hlo-53321302 | 011338871
-- hy-hlo-51223300 | 013951984
-- hy-hlo-53310137 | 012520688
-- hy-hlo-53621583 | 011196796
-- hy-hlo-53863064 | 011736884
-- hy-hlo-57711592 | 010262566
-- hy-hlo-57732872 | 013670647
UPDATE studyrights
SET admission_type_urn = 'urn:code:admission-type:tv'
WHERE person_id IN (
'hy-hlo-45783433',
'hy-hlo-48537946',
'hy-hlo-49801905',
'hy-hlo-53321302',
'hy-hlo-51223300',
'hy-hlo-53310137',
'hy-hlo-53621583',
'hy-hlo-53863064',
'hy-hlo-57711592',
'hy-hlo-57732872'
);
-- YP:
-- hy-hlo-67768022 | 011952325
-- hy-hlo-73740025 | 011736606
-- hy-hlo-74780917 | 012775433
-- hy-hlo-79615371 | 014113941
-- hy-hlo-78118455 | 014300615
UPDATE studyrights
SET admission_type_urn = 'urn:code:admission-type:yp'
WHERE person_id IN (
'hy-hlo-67768022',
'hy-hlo-73740025',
'hy-hlo-74780917',
'hy-hlo-79615371',
'hy-hlo-78118455'
);
-- AV:
-- hy-hlo-78777265 | 010779392
-- hy-hlo-81909550 | 011894153
-- hy-hlo-84571890 | 013897158
-- hy-hlo-80330834 | 015085926
-- hy-hlo-84775722 | 011791272
UPDATE studyrights
SET admission_type_urn = 'urn:code:admission-type:av'
WHERE person_id IN (
'hy-hlo-78777265',
'hy-hlo-81909550',
'hy-hlo-84571890',
'hy-hlo-80330834',
'hy-hlo-84775722'
);
-- M:
-- hy-hlo-87439827 | 011928456
-- hy-hlo-96612371 | 014418361
-- hy-hlo-84840261 | 010975895
-- hy-hlo-90330681 | 010522688
-- hy-hlo-96594603 | 013951573
UPDATE studyrights
SET admission_type_urn = 'urn:code:admission-type:m'
WHERE person_id IN (
'hy-hlo-87439827',
'hy-hlo-96612371',
'hy-hlo-84840261',
'hy-hlo-90330681',
'hy-hlo-96594603'
);
-- all studentnumbers for running updater:
013683126
014340523
010589388
010775888
011065995
011321150
010764653
011194400
011338871
013951984
012520688
011196796
011736884
010262566
013670647
011952325
011736606
012775433
014113941
014300615
010779392
011894153
013897158
015085926
011791272
011928456
014418361
010975895
010522688
013951573
-71
View File
@@ -1,71 +0,0 @@
# Old readme of anonyymioodi
This is mostly obsolete, but may help with something sometimes.
## Anonyymioodi - anonymous database setup for oodikone
(name anonyymioodi for :culture: reasons)
This folder contains a guide and scripts to create anonymous database images for oodikone, mainly used for testing.
## How to create / modify database images
Creating / modifying images varies a bit depending on the db. Generally, to modify images, you can either use `psql` directly in docker by running `docker exec -it <dbname> psql -U postgres <dbname>` or start adminer with oodikone's docker-compose.
By default, previous db images from Toska Hub are used as a base. If needed, comment out Toska hub image and uncomment corresponding clean postgres image in docker-compose before running other steps.
See more details on how to create / modify databases below.
### Sis-importer-db
The following steps are for building a clean sis-importer-db image from scratch. If you want to just modify the existing image a bit, follow the steps defined for user-db / kone-db.
- See [sis-importer/importer-db-staging-sampletaker](https://github.com/UniversityOfHelsinkiCS/sis-importer/tree/master/importer-db-staging-sampletaker) and create `sis-importer-db.sqz` dump
- Move `sis-importer-db.sqz` to this folder
- Run `./create_container_from_dump.sh sis-importer-db`
- Enrich db with command in file `enrich_data.sql` either by piping command to container or by running command inside container.
### Sis-db:
The following steps are for building a clean sis-db image from scratch using oodikone's updater. If you want to just modify the existing image a bit, follow the steps defined for user-db / kone-db.
- Temporarily replace sis-db -service in oodikone's docker-compose.yml with sis-db -service from this repo's docker-compose.yml
- Ensure the newest sis-importer-db image is pulled from toska hub in oodikone repo
- Modify sis scheduler limit in config.js by setting: `module.exports.DEV_SCHEDULE_COUNT = null`
- Start updater, see oodikone readme for more info.
- Populate sis-db from sis-importer-db:
```
curl --request GET --url 'http://localhost:8082/v1/meta?token=dev' && \
curl --request GET --url 'http://localhost:8082/v1/programmes?token=dev' && \
curl --request GET --url 'http://localhost:8082/v1/students?token=dev'
```
(or run these from frontend. You might also want to follow sis-updater-workers logs to ensure things are running smoothly.)
- Clean up any extra stuff you don't want to keep in sis-db.
- Bonus: check that cypress tests run okay
### User-db / kone-db
- Temporarily replace the db service in oodikone's docker-compose.yml with the same db service from this folder's docker-compose.yml
- Ensure the newest user-db/kone-db image is pulled from the registry with `docker-compose pull`
- Modify db to wanted state. Tips:
- Run oodikone if you want to upgrade something from frontend (e.g. tags in kone-db, courses in sis-db etc.).
- Easy way to create new users to user-db is to modify dev user details in the frontend API config. A new user is created during login if the given uid is not present in user-db.
## Publishing the image
After completing the steps above and making sure your local version has the correct data, run `./commit_and_push_to_toska_hub.sh <dbname>` to publish the db image to Toska Hub.
## How to update Postgres versions
When Postgres is updated on the server, anon db images need to be updated to match server versions. Do following:
- Ensure you're using toska hub image in docker-compose and that you've pulled the newest image from toska hub: `docker-compose pull <dbname>`.
- Start database with docker-compose
- Create dump from database by running `docker exec -i <dbname> pg_dump -Fc -U postgres <dbname> > <dbname>.sqz`
- Close container with docker-compose down.
- Update the Postgres version in docker-compose.yml in this folder.
- Uncomment row with Postgres image and comment row with Toskahub image.
- Run `./create_container_from_dump.sh <dbname>`, followed by `./commit_and_push_to_toska_hub.sh <dbname>`
+8 -8
View File
@@ -48,12 +48,12 @@ set_custom_select_prompt() {
PS3="Please enter your choice: "
options=(
"Set up oodikone from scratch."
"Reset all real data."
"Reset single database."
"Set up Oodikone from scratch"
"Reset all real data"
"Reset single database"
"Restore data from dumps"
"Docker system prune"
"Quit."
"Quit"
)
}
@@ -66,11 +66,11 @@ set_custom_select_prompt
while true; do
select opt in "${options[@]}"; do
case $opt in
"Set up oodikone from scratch.")
"Set up Oodikone from scratch")
set_up_oodikone_from_scratch;;
"Reset all real data.")
"Reset all real data")
reset_all_real_data;;
"Reset single database.")
"Reset single database")
reset_single_database
set_custom_select_prompt # Set prompt back to initial values
;;
@@ -78,7 +78,7 @@ while true; do
restore_data_from_dumps;;
"Docker system prune")
docker_prune;;
"Quit.")
"Quit")
break 2;;
*) msg "${RED}Invalid option:${NOFORMAT} $REPLY
";;
+5 -5
View File
@@ -1,7 +1,7 @@
/* eslint-disable global-require */
/* eslint-disable import/extensions */
/* eslint-disable @typescript-eslint/no-require-imports */
/* eslint-disable import/no-commonjs */
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable import/no-unused-modules */
const { defineConfig } = require('cypress')
module.exports = defineConfig({
@@ -9,8 +9,8 @@ module.exports = defineConfig({
defaultCommandTimeout: 30000,
requestTimeout: 30000,
videoCompression: false,
viewportWidth: 1800,
viewportHeight: 1200,
viewportWidth: 1920,
viewportHeight: 1080,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
+11 -7
View File
@@ -1,4 +1,4 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const openCompletedCoursesModal = () => {
cy.get('button').contains('Search completed courses of students').click()
@@ -22,8 +22,8 @@ const selectSavedCourselist = name => {
const deleteAllSearches = () => {
cy.contains('Saved courselists')
.get('.dropdown')
.then(d => {
const searchItems = d.find('div[role=option] > span[class=text]')
.then(dropdown => {
const searchItems = dropdown.find('div[role=option] > span[class=text]')
for (let i = 0; i < searchItems.length; i++) {
if (searchItems[i].textContent.includes('TEST-')) {
cy.get('[data-cy="history-search"]').children().eq(0).type(searchItems[i].textContent).type('{enter}')
@@ -85,10 +85,12 @@ describe('When search modal is opened', () => {
})
it('Pushes the query to url', () => {
cy.url().should(
'include',
'/completedcoursessearch?courseList=TKT10001&courseList=TKT10002&courseList=TKT10003&courseList=TKT10004&studentList=010450938&studentList=010589388&studentList=010614509'
)
cy.fixture('completedCoursesData').then(({ studentSet1, coursesSet1 }) => {
cy.url().should(
'include',
`/completedcoursessearch?courseList=${coursesSet1.join('&courseList=')}&studentList=${studentSet1.join('&studentList=')}`
)
})
})
it('Finds correct students and courses', () => {
@@ -96,6 +98,8 @@ describe('When search modal is opened', () => {
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', studentSet1[0])
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', studentSet1[1])
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', studentSet1[2])
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', studentSet1[3])
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', studentSet1[4])
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', coursesSet1[0])
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', coursesSet1[1])
cy.get('[data-cy="completed-courses-table-div"]').should('contain.text', coursesSet1[2])
File diff suppressed because it is too large Load Diff
+8 -4
View File
@@ -1,4 +1,4 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const openCustomPopupForm = () => {
cy.get('[data-cy="custom-pop-search-button"]').click()
@@ -27,8 +27,8 @@ const deleteAllSearches = () => {
.parent()
.parent()
.get('.dropdown')
.then(d => {
const searchItems = d.find('div[role=option] > span[class=text]')
.then(dropdown => {
const searchItems = dropdown.find('div[role=option] > span[class=text]')
for (let i = 0; i < searchItems.length; i++) {
if (searchItems[i].textContent.includes('TEST-')) {
cy.get('[data-cy="history-search"]').children().eq(0).type(`${searchItems[i].textContent}{enter}`)
@@ -64,7 +64,11 @@ const containsAmountOfStudents = (amount = 0) => {
}
const containsSpecificStudents = (studentnumbers = []) => {
cy.contains(`Students (${studentnumbers.length})`).click()
cy.contains(`Students (${studentnumbers.length})`)
.parent()
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.contains(`Students (${studentnumbers.length})`).click()
})
studentnumbers.forEach(s => cy.contains(s))
}
+514
View File
@@ -0,0 +1,514 @@
/// <reference types="cypress" />
const { getEmptyYears } = require('../support/commands')
describe('Evaluation overview', () => {
describe('Programme view', () => {
beforeEach(() => {
cy.init('/evaluationoverview/programme/KH50_001', 'admin')
})
it('Page opens correctly', () => {
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('KH50_001')
cy.contains("This view is an abridged version of Oodikone's Studyprogramme Overview")
})
it('Progress data is shown correctly with graduated included', () => {
cy.get('[data-cy="Graph-StudyTrackProgress"]').within(() => {
const totalStats = ['12.9%', '6.9%', '9.7%', '9.2%', '3.7%', '6.0%', '51.6%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 8, 8, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 26, 9, 9, 4, 3, 0, 1, 0],
['2021 - 2022', 32, 8, 3, 8, 8, 5, 0, 0],
['2020 - 2021', 27, 2, 1, 3, 6, 2, 4, 9],
['2019 - 2020', 33, 1, 0, 2, 1, 0, 3, 26],
['2018 - 2019', 44, 0, 1, 1, 2, 0, 3, 37],
['2017 - 2018', 47, 0, 1, 3, 0, 1, 2, 40],
['Total', 217, 28, 15, 21, 20, 8, 13, 112],
]
cy.checkTableStats(tableContents, 'StudyTrackProgress')
})
it('Progress data is shown correctly with graduated excluded', () => {
cy.get('[data-cy=GraduatedToggle]').click()
cy.get('[data-cy="Graph-StudyTrackProgress"]').within(() => {
const totalStats = ['30.8%', '15.4%', '22.0%', '20.9%', '4.4%', '5.5%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 8, 8, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 24, 9, 8, 4, 3, 0, 0, 0],
['2021 - 2022', 31, 8, 3, 8, 8, 4, 0, 0],
['2020 - 2021', 11, 2, 1, 2, 5, 0, 0, 1],
['2019 - 2020', 7, 1, 0, 2, 1, 0, 0, 3],
['2018 - 2019', 5, 0, 1, 1, 2, 0, 0, 1],
['2017 - 2018', 5, 0, 1, 3, 0, 0, 1, 0],
['Total', 91, 28, 14, 20, 19, 4, 1, 5],
]
cy.checkTableStats(tableContents, 'StudyTrackProgress')
})
it('Graduation times breakdown data is shown correctly', () => {
cy.get('[data-cy=graduation-times-graph-breakdown]').within(() => {
cy.contains('Graduation year')
cy.contains('2021')
cy.get('[aria-label="2021, 8. Overtime."]')
cy.get('[aria-label="2021, 24. Max. year overtime."]')
cy.get('[aria-label="2021, 14. On time."]').trigger('mouseover')
cy.contains('Graduated On time: 14 students')
})
cy.get('[data-cy=YearToggle]').click()
cy.get('[data-cy=graduation-times-graph-breakdown]').within(() => {
cy.contains('Graduation year')
cy.contains('2021 - 2022')
cy.get('[aria-label="2021 - 2022, 13. Overtime."]')
cy.get('[aria-label="2021 - 2022, 21. Max. year overtime."]')
cy.get('[aria-label="2021 - 2022, 21. On time."]').trigger('mouseover')
cy.contains('Graduated On time: 21 students')
})
})
it('Graduation times median time data is shown correctly', () => {
cy.get('[data-cy=GraduationTimeToggle]').click()
cy.get('[data-cy=graduation-times-graph]').within(() => {
cy.contains('Graduation year')
cy.contains('2021')
cy.contains('46 graduated').trigger('mouseover')
cy.contains('46 students graduated in year 2021')
cy.contains('median study time: 41 months')
cy.contains('14 graduated on time')
cy.contains('24 graduated max year overtime')
cy.contains('8 graduated over year late')
})
cy.get('[data-cy=YearToggle]').click()
cy.get('[data-cy=graduation-times-graph]').within(() => {
cy.contains('Graduation year')
cy.contains('2021 - 2022')
cy.contains('55 graduated').trigger('mouseover')
cy.contains('55 students graduated in year 2021 - 2022')
cy.contains('median study time: 40 months')
cy.contains('21 graduated on time')
cy.contains('21 graduated max year overtime')
cy.contains('13 graduated over year late')
})
})
it("'Programmes before or after' data is shown correctly", () => {
cy.get('[data-cy=Graph-ProgrammesBeforeOrAfter]').within(() => {
cy.contains('Matematiikan ja tilastotieteen maisteriohjelma')
cy.contains('Tietojenkäsittelytieteen maisteriohjelma')
cy.contains('Datatieteen maisteriohjelma')
cy.contains('2021')
cy.contains('32')
cy.contains('19')
})
let emptyYears = new Array(getEmptyYears(false).length).fill(0)
let tableContents = [
['MH50_001', 'MAST', 'Matematiikan ja tilastotieteen maisteriohjelma', 0, 0, 1, 9, 32, 19, 15, ...emptyYears],
['MH50_002', 'LSI', 'Life Science Informatics -maisteriohjelma', 0, 0, 0, 1, 2, 1, 1, ...emptyYears],
[
'MH50_003',
'TCM',
'Teoreettisten ja laskennallisten menetelmien maisteriohjelma',
0,
0,
0,
0,
1,
2,
1,
...emptyYears,
],
['MH50_009', 'CSM', 'Tietojenkäsittelytieteen maisteriohjelma', 0, 0, 0, 0, 2, 3, 2, ...emptyYears],
['MH50_010', 'DATA', 'Datatieteen maisteriohjelma', 0, 0, 0, 1, 5, 10, 1, ...emptyYears],
['MH70_009', 'ECON', 'Taloustieteen maisteriohjelma', 0, 0, 0, 0, 4, 7, 4, ...emptyYears],
]
cy.checkTableStats(tableContents, 'undefined')
cy.get('[data-cy=YearToggle]').click()
cy.get('[data-cy=Graph-ProgrammesBeforeOrAfter]').within(() => {
cy.contains('Matematiikan ja tilastotieteen maisteriohjelma')
cy.contains('Tietojenkäsittelytieteen maisteriohjelma')
cy.contains('Datatieteen maisteriohjelma')
cy.contains('2021 - 2022')
cy.contains('28')
cy.contains('16')
})
emptyYears = new Array(getEmptyYears(true).length).fill(0)
tableContents = [
['MH50_001', 'MAST', 'Matematiikan ja tilastotieteen maisteriohjelma', 0, 0, 4, 24, 28, 16, 4, ...emptyYears],
['MH50_002', 'LSI', 'Life Science Informatics -maisteriohjelma', 0, 0, 1, 0, 3, 1, 0, ...emptyYears],
[
'MH50_003',
'TCM',
'Teoreettisten ja laskennallisten menetelmien maisteriohjelma',
0,
0,
0,
1,
1,
2,
0,
...emptyYears,
],
['MH50_009', 'CSM', 'Tietojenkäsittelytieteen maisteriohjelma', 0, 0, 0, 0, 4, 3, 0, ...emptyYears],
['MH50_010', 'DATA', 'Datatieteen maisteriohjelma', 0, 0, 1, 0, 12, 4, 0, ...emptyYears],
['MH70_009', 'ECON', 'Taloustieteen maisteriohjelma', 0, 0, 0, 4, 6, 5, 0, ...emptyYears],
]
cy.checkTableStats(tableContents, 'undefined')
})
})
describe('Faculty view', () => {
beforeEach(() => {
cy.init('/evaluationoverview/faculty/H50', 'admin')
})
it('Page opens correctly', () => {
cy.contains('Matemaattis-luonnontieteellinen tiedekunta')
cy.contains('H50')
cy.contains("This view is an abridged version of Oodikone's Faculty Overview")
})
describe('Progress stats', () => {
describe('Bachelor stats', () => {
it('graph with graduated included is shown correctly', () => {
cy.get('[data-cy="Graph-FacultyBachelorsProgress"]').within(() => {
const totalStats = ['12.2%', '5.3%', '7.5%', '8.2%', '5.3%', '6.3%', '55.2%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('graph with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy="Graph-FacultyBachelorsProgress"]').within(() => {
const totalStats = ['30.5%', '12.5%', '18.0%', '19.5%', '7.0%', '3.1%', '9.4%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('table with graduated included is shown correctly', () => {
cy.get('[data-cy="Table-FacultyBachelorsProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 14, 13, 0, 0, 0, 0, 0, 1],
['2022 - 2023', 32, 11, 10, 6, 4, 0, 1, 0],
['2021 - 2022', 44, 10, 4, 9, 11, 7, 1, 2],
['2020 - 2021', 38, 3, 1, 3, 7, 3, 8, 13],
['2019 - 2020', 50, 2, 0, 2, 1, 4, 5, 36],
['2018 - 2019', 75, 0, 1, 1, 2, 2, 3, 66],
['2017 - 2018', 66, 0, 1, 3, 1, 1, 2, 58],
['Total', 319, 39, 17, 24, 26, 17, 20, 176],
]
cy.checkTableStats(tableContents, 'FacultyBachelorsProgress')
})
it('table with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy="Table-FacultyBachelorsProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 14, 13, 0, 0, 0, 0, 0, 1],
['2022 - 2023', 30, 11, 9, 6, 4, 0, 0, 0],
['2021 - 2022', 41, 10, 4, 9, 11, 6, 0, 1],
['2020 - 2021', 14, 3, 1, 2, 6, 0, 1, 1],
['2019 - 2020', 14, 2, 0, 2, 1, 3, 2, 4],
['2018 - 2019', 6, 0, 1, 1, 2, 0, 0, 2],
['2017 - 2018', 9, 0, 1, 3, 1, 0, 1, 3],
['Total', 128, 39, 16, 23, 25, 9, 4, 12],
]
cy.checkTableStats(tableContents, 'FacultyBachelorsProgress')
})
})
describe('Bachelor + master stats', () => {
it('graph with graduated included is shown correctly', () => {
cy.get('[data-cy=Graph-FacultyBachelorMastersProgress]').within(() => {
const totalStats = ['21.3%', '8.4%', '10.1%', '14.0%', '12.4%', '6.7%', '27.0%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('graph with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy=Graph-FacultyBachelorMastersProgress]').within(() => {
const totalStats = ['28.7%', '11.6%', '14.0%', '18.6%', '15.5%', '3.1%', '8.5%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('table with graduated included is shown correctly', () => {
cy.get('[data-cy="Table-FacultyBachelorMastersProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 0, 0, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 2, 2, 0, 0, 0, 0, 0, 0],
['2021 - 2022', 4, 2, 0, 0, 0, 1, 0, 1],
['2020 - 2021', 23, 13, 2, 4, 0, 4, 0, 0],
['2019 - 2020', 32, 7, 7, 4, 7, 3, 3, 1],
['2018 - 2019', 63, 10, 2, 5, 13, 7, 4, 22],
['2017 - 2018', 54, 4, 4, 5, 5, 7, 5, 24],
['Total', 178, 38, 15, 18, 25, 22, 12, 48],
]
cy.checkTableStats(tableContents, 'FacultyBachelorMastersProgress')
})
it('table with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy="Table-FacultyBachelorMastersProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 0, 0, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 2, 2, 0, 0, 0, 0, 0, 0],
['2021 - 2022', 4, 2, 0, 0, 0, 1, 0, 1],
['2020 - 2021', 21, 13, 2, 4, 0, 2, 0, 0],
['2019 - 2020', 31, 7, 7, 4, 7, 3, 2, 1],
['2018 - 2019', 41, 9, 2, 5, 12, 7, 1, 5],
['2017 - 2018', 30, 4, 4, 5, 5, 7, 1, 4],
['Total', 129, 37, 15, 18, 24, 20, 4, 11],
]
cy.checkTableStats(tableContents, 'FacultyBachelorMastersProgress')
})
})
describe('Master stats', () => {
it('graph with graduated included is shown correctly', () => {
cy.get('[data-cy=Graph-FacultyMastersProgress]').within(() => {
const totalStats = ['16.1%', '12.9%', '6.5%', '12.9%', '9.7%', '16.1%', '25.8%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('graph with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy=Graph-FacultyMastersProgress]').within(() => {
const totalStats = ['23.8%', '19.0%', '9.5%', '9.5%', '9.5%', '14.3%', '14.3%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('table with graduated included is shown correctly', () => {
cy.get('[data-cy="Table-FacultyMastersProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 4, 4, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 5, 0, 1, 1, 2, 0, 0, 1],
['2021 - 2022', 8, 1, 2, 0, 2, 1, 1, 1],
['2020 - 2021', 3, 0, 0, 0, 0, 0, 2, 1],
['2019 - 2020', 7, 0, 0, 1, 0, 1, 1, 4],
['2018 - 2019', 2, 0, 0, 0, 0, 1, 0, 1],
['2017 - 2018', 2, 0, 1, 0, 0, 0, 1, 0],
['Total', 31, 5, 4, 2, 4, 3, 5, 8],
]
cy.checkTableStats(tableContents, 'FacultyMastersProgress')
})
it('table with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy="Table-FacultyMastersProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 4, 4, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 5, 0, 1, 1, 2, 0, 0, 1],
['2021 - 2022', 5, 1, 2, 0, 0, 0, 1, 1],
['2020 - 2021', 0, 0, 0, 0, 0, 0, 0, 0],
['2019 - 2020', 3, 0, 0, 1, 0, 1, 1, 0],
['2018 - 2019', 2, 0, 0, 0, 0, 1, 0, 1],
['2017 - 2018', 2, 0, 1, 0, 0, 0, 1, 0],
['Total', 21, 5, 4, 2, 2, 2, 3, 3],
]
cy.checkTableStats(tableContents, 'FacultyMastersProgress')
})
})
describe('Doctor stats', () => {
it('graph with graduated included is shown correctly', () => {
cy.get('[data-cy=Graph-FacultyDoctoralProgress]').within(() => {
const totalStats = ['6.0%', '18.0%', '16.0%', '14.0%', '46.0%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('graph with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy=Graph-FacultyDoctoralProgress]').within(() => {
const totalStats = ['9.1%', '24.2%', '12.1%', '15.2%', '39.4%']
for (const stat of totalStats) {
cy.contains(stat)
}
})
})
it('table with graduated included is shown correctly', () => {
cy.get('[data-cy="Table-FacultyDoctoralProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 1, 1, 0, 0, 0, 0],
['2022 - 2023', 9, 1, 7, 1, 0, 0],
['2021 - 2022', 3, 1, 1, 0, 0, 1],
['2020 - 2021', 5, 0, 0, 2, 1, 2],
['2019 - 2020', 11, 0, 0, 1, 4, 6],
['2018 - 2019', 14, 0, 1, 4, 2, 7],
['2017 - 2018', 7, 0, 0, 0, 0, 7],
['Total', 50, 3, 9, 8, 7, 23],
]
cy.checkTableStats(tableContents, 'FacultyDoctoralProgress')
})
it('table with graduated excluded is shown correctly', () => {
cy.get('[data-cy="GraduatedToggle"]').click()
cy.get('[data-cy="Table-FacultyDoctoralProgress"]')
const years = getEmptyYears(true)
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0]),
['2023 - 2024', 1, 1, 0, 0, 0, 0],
['2022 - 2023', 9, 1, 7, 1, 0, 0],
['2021 - 2022', 3, 1, 1, 0, 0, 1],
['2020 - 2021', 4, 0, 0, 2, 0, 2],
['2019 - 2020', 9, 0, 0, 1, 4, 4],
['2018 - 2019', 5, 0, 0, 0, 1, 4],
['2017 - 2018', 2, 0, 0, 0, 0, 2],
['Total', 33, 3, 8, 4, 5, 13],
]
cy.checkTableStats(tableContents, 'FacultyDoctoralProgress')
})
})
it('Years in the tables can be clicked to show programme level breakdown', () => {
cy.get('[data-cy="Table-FacultyBachelorsProgress"]').within(() => {
cy.contains('2021 - 2022').click()
cy.contains('31.3%').trigger('mouseover', { force: true })
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('MAT - KH50_001')
cy.contains('0 Credits: 0')
cy.contains('1 ≤ Credits < 45: 10')
cy.contains('45 ≤ Credits < 90: 9')
cy.contains('90 ≤ Credits < 135: 11')
cy.contains('135 ≤ Credits < 180: 2')
cy.contains('180 ≤ Credits: 0')
})
})
})
describe('Graduation times stats', () => {
it('All the correct sections are displayed', () => {
const checkSections = breakdown => {
const sections = ['bachelor', 'bcMsCombo', 'master', 'doctor']
for (const section of sections) {
cy.get(`[data-cy=Section-${section}]`).within(() => {
cy.get('div.graduations-chart-container').within(() => {
cy.get(`div.faculty-${breakdown ? 'breakdown-' : ''}graph`)
cy.get('div.graduations-message').contains("Click a bar to view that year's programme level breakdown")
})
})
}
}
checkSections(true)
cy.get('[data-cy=GraduationTimeToggle]').click()
checkSections(false)
})
it('Graduation times breakdown data is shown correctly', () => {
cy.get('[data-cy=Section-bachelor]').within(() => {
cy.contains('Graduation year')
cy.contains('2022')
cy.get('[aria-label="2022, 29. On time."]').trigger('mouseover')
cy.contains('On time: 29')
cy.get('[aria-label="2022, 29. On time."]').trigger('click')
cy.contains('Year 2022 by graduation year')
cy.get('[aria-label="MAT, 22. On time."]').trigger('mouseover')
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('KH50_001')
cy.contains('On time: 22')
cy.get('[aria-label="MFKK, 7. On time."]').trigger('mouseover')
cy.contains('Matematiikan, fysiikan ja kemian opettajan kandiohjelma')
cy.contains('KH50_004')
cy.contains('On time: 7')
})
})
it('Graduation times median time data is shown correctly', () => {
cy.get('[data-cy=GraduationTimeToggle]').click()
cy.get('[data-cy=Section-bachelor]').within(() => {
cy.contains('Graduation year')
cy.contains('2021')
cy.contains('70 graduated').trigger('mouseover')
cy.contains('70 students graduated in year 2021')
cy.contains('median study time: 39.5 months')
cy.contains('28 graduated on time')
cy.contains('32 graduated max year overtime')
cy.contains('10 graduated over year late')
cy.contains('70 graduated').trigger('click')
cy.contains('Year 2021 by graduation year')
cy.get('[aria-label="MAT, 41."]').trigger('mouseover')
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('KH50_001')
cy.contains('47 students graduated in year 2021')
cy.contains('median study time: 41 months')
cy.contains('14 graduated on time')
cy.contains('25 graduated max year overtime')
cy.contains('8 graduated over year late')
})
})
})
})
})
+59 -51
View File
@@ -1,8 +1,8 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const path = require('path')
const timestamp = new Date().toISOString().split('T')[0] // YYYY-MM-DD
const timestamp = new Date().toLocaleDateString('en-CA', { year: 'numeric', month: '2-digit', day: '2-digit' }) // YYYY-MM-DD, taking the local time zone into account
const downloadsFolder = Cypress.config('downloadsFolder')
describe('Faculty overview', () => {
@@ -23,7 +23,7 @@ describe('Faculty overview', () => {
})
it('Credits produced by faculty are shown', () => {
cy.get('[data-cy="Section-CreditsProducedByTheFaculty"]').should('be.visible')
cy.get('[data-cy="Section-creditsProducedByTheFaculty"]').should('be.visible')
cy.get('[data-cy="Graph-CreditsProducedByTheFaculty"]').should('be.visible')
})
@@ -35,7 +35,7 @@ describe('Faculty overview', () => {
describe('Basic information: basic user', () => {
beforeEach(() => {
cy.init('/faculties')
cy.contains('a', 'Maatalous-metsätieteellinen tiedekunta').click()
cy.contains('a', 'Matemaattis-luonnontieteellinen tiedekunta').click()
})
it('Basic information tab show all graphs and tables', () => {
@@ -75,14 +75,14 @@ describe('Faculty overview', () => {
})
it('Students of the faculty infobox works', () => {
cy.get('[data-cy="StudentsOfTheFaculty-info-content"]').should('not.exist')
cy.get('[data-cy="StudentsOfTheFaculty-open-info"]').click()
cy.get('[data-cy="StudentsOfTheFaculty-info-content"]').should('be.visible')
cy.get('[data-cy="StudentsOfTheFaculty-close-info"]').click()
cy.get('[data-cy="StudentsOfTheFaculty-info-content"]').should('not.exist')
cy.get('[data-cy="studentsOfTheFaculty-info-content"]').should('not.exist')
cy.get('[data-cy="studentsOfTheFaculty-open-info"]').click()
cy.get('[data-cy="studentsOfTheFaculty-info-content"]').should('be.visible')
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', () => {
it('Data can be exported to Excel files', { retries: 3 }, () => {
const sections = [
'StudentsOfTheFaculty',
'GraduatedOfTheFaculty',
@@ -91,9 +91,9 @@ describe('Faculty overview', () => {
]
sections.forEach(section => {
cy.get(`[data-cy="Graph-${section}"]`).within(() => {
cy.get('[aria-label="View chart menu, Chart"]').click()
cy.get('[aria-label="View chart menu, Chart"]').click({ force: true })
cy.contains('li.highcharts-menu-item', 'Download XLS').click()
const downloadedFile = `oodikone_${section}_H80_${timestamp}.xls`
const downloadedFile = `oodikone_${section}_H50_${timestamp}.xls`
cy.readFile(path.join(downloadsFolder, downloadedFile))
})
})
@@ -155,7 +155,7 @@ describe('Faculty overview', () => {
describe('Average graduation times', () => {
beforeEach(() => {
cy.init('/faculties')
cy.contains('a', 'Kasvatustieteellinen tiedekunta').click()
cy.contains('a', 'Matemaattis-luonnontieteellinen tiedekunta').click()
cy.contains('Graduation times').click()
})
@@ -178,42 +178,51 @@ describe('Faculty overview', () => {
cy.get('[data-cy="Section-bachelor"]').within(() => {
cy.get('div[class="faculty-graph"]')
cy.contains('1 graduated').should('have.length', 1)
cy.contains('1 graduated').trigger('mouseover')
cy.contains('1 students graduated in year 2019')
cy.contains('median study time: 40 months')
cy.contains('0 graduated on time')
cy.contains('1 graduated max year overtime')
cy.contains('44 graduated').trigger('mouseover')
cy.contains('44 students graduated in year 2023')
cy.contains('median study time: 37.5 months')
cy.contains('21 graduated on time')
cy.contains('11 graduated max year overtime')
cy.contains('12 graduated over year late')
cy.contains('1 graduated').click()
cy.contains('Year 2019 by graduation year')
cy.contains('44 graduated').click()
cy.contains('Year 2023 by graduation year')
cy.get('div[class="programmes-graph"]').should('be.visible')
cy.get('div[class="programmes-graph"]').within(() => {
cy.contains('EDUK')
cy.get('[aria-label="EDUK, 40."]').trigger('mouseover')
cy.contains('Kasvatustieteiden kandiohjelma')
cy.contains('KH60_001')
cy.contains('MAT')
cy.get('[aria-label="MAT, 36."]').trigger('mouseover')
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('KH50_001')
cy.contains('26 students graduated in year 2023')
cy.contains('median study time: 36 months')
cy.contains('14 graduated on time')
cy.contains('6 graduated max year overtime')
cy.contains('6 graduated over year late')
})
})
cy.get('[data-cy="Section-master"]').within(() => {
cy.get('div[class="faculty-graph"]')
cy.contains('24 graduated').should('have.length', 1)
cy.contains('24 graduated').trigger('mouseover')
cy.contains('24 students graduated in year 2020')
cy.contains('median study time: 22 months')
cy.contains('0 graduated over year late')
cy.contains('20 graduated on time')
cy.contains('4 graduated max year overtime')
cy.contains('4 graduated').trigger('mouseover')
cy.contains('4 students graduated in year 2023')
cy.contains('median study time: 23 months')
cy.contains('3 graduated on time')
cy.contains('0 graduated max year overtime')
cy.contains('1 graduated over year late')
cy.contains('24 graduated').click()
cy.contains('Year 2020 by graduation year')
cy.contains('4 graduated').click()
cy.contains('Year 2023 by graduation year')
cy.get('div[class="programmes-graph"]').should('be.visible')
cy.get('div[class="programmes-graph"]').within(() => {
cy.contains('EDUM')
cy.get('[aria-label="EDUM, 22."]').trigger('mouseover')
cy.contains('Kasvatustieteiden maisteriohjelma')
cy.contains('MH60_001')
cy.contains('MAST')
cy.get('[aria-label="MAST, 23."]').trigger('mouseover')
cy.contains('Matematiikan ja tilastotieteen maisteriohjelma')
cy.contains('MH50_001')
cy.contains('3 students graduated in year 2023')
cy.contains('median study time: 23 months')
cy.contains('2 graduated on time')
cy.contains('0 graduated max year overtime')
cy.contains('1 graduated over year late')
})
})
})
@@ -221,33 +230,32 @@ describe('Faculty overview', () => {
it('Graduation times grouping and time types can be toggled', () => {
cy.get('[data-cy="GraduationTimeToggle"]').click()
cy.get('[data-cy="GroupByToggle"]').click()
cy.get('[data-cy="Section-bachelor"]').should('not.exist')
cy.get('[data-cy="Section-master"]').should('be.visible')
cy.get('[data-cy="Section-master"]').within(() => {
cy.get('[data-cy="Section-bachelor"]').within(() => {
cy.get('div[class="faculty-graph"]')
cy.contains('19 graduated (44.2 % of class)').should('have.length', 1)
cy.contains('19 graduated').trigger('mouseover')
cy.contains('From class of 2018, 19/43 students have graduated')
cy.contains('24 graduated').trigger('mouseover')
cy.contains('From class of 2020 - 2021, 24/37 students have graduated')
})
cy.get('[data-cy="GraduationTimeToggle"]').click()
cy.get('[data-cy="Section-master"]').within(() => {
cy.get('[aria-label="2018, 16. On time."]').click()
cy.get('[data-cy="Section-bachelor"]').within(() => {
cy.get('[aria-label="2018 - 2019, 29. 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.get('[aria-label="EDUM, 3. Max. year overtime."]').trigger('mouseover')
cy.contains('Kasvatustieteiden maisteriohjelma')
cy.contains('MH60_001')
cy.contains('Year 2018 - 2019 by start year')
cy.contains('MAT')
cy.get('[aria-label="MAT, 20. Max. year overtime."]').trigger('mouseover')
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('KH50_001')
cy.contains('Max. year overtime: 20')
})
})
})
it('Data can be exported to an Excel file', () => {
cy.get('[data-cy="DownloadButton-AverageGraduationTimes"]').click()
const downloadedFile = `oodikone_H60_graduation_times_${timestamp}.xlsx`
const downloadedFile = `oodikone_H50_graduation_times_${timestamp}.xlsx`
cy.readFile(path.join(downloadsFolder, downloadedFile))
})
})
+110 -108
View File
@@ -1,6 +1,6 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const MOCKED_DATE = new Date(2022, 6, 12).getTime()
const MOCKED_DATE = new Date('2024-08-30').getTime()
const checkFilteringResult = studentCount => {
cy.contains(`Students (${studentCount})`)
@@ -18,7 +18,6 @@ const clearSingleDropdownSelection = dataCyAttribute => {
}
const testRangeFilter = (parentEl, min, max, expected) => {
cy.clock(MOCKED_DATE, ['Date'])
cy.cs(parentEl)
.cs('range-selector-min')
.find('input')
@@ -29,13 +28,17 @@ const testRangeFilter = (parentEl, min, max, expected) => {
.find('input')
.invoke('val')
.then(initialMax => {
cy.cs(parentEl).cs('range-selector-min').find('input').type(`{selectall}{backspace}${min}`).blur()
cy.cs(parentEl).cs('range-selector-max').find('input').type(`{selectall}{backspace}${max}`).blur()
cy.cs(parentEl).cs('range-selector-min').find('input').clear()
cy.cs(parentEl).cs('range-selector-min').find('input').type(min)
cy.cs(parentEl).cs('range-selector-max').find('input').clear()
cy.cs(parentEl).cs('range-selector-max').find('input').type(max)
checkFilteringResult(expected)
cy.cs(parentEl).cs('range-selector-min').find('input').type(`{selectall}{backspace}${initialMin}`).blur()
cy.cs(parentEl).cs('range-selector-max').find('input').type(`{selectall}{backspace}${initialMax}`).blur()
cy.cs(parentEl).cs('range-selector-min').find('input').clear()
cy.cs(parentEl).cs('range-selector-min').find('input').type(initialMin)
cy.cs(parentEl).cs('range-selector-max').find('input').clear()
cy.cs(parentEl).cs('range-selector-max').find('input').type(initialMax)
})
})
}
@@ -65,45 +68,45 @@ const createRunTestStepWithPreAndPostPartsFunction = amountWithoutFiltering => {
}
}
describe('Population Statistics with CS master', () => {
const pathToCSMast2019 =
'/populations?months=36&semesters=FALL&semesters=SPRING&studyRights={%22programme%22%3A%22MH50_009%22}&tag&year=2019'
const defaultAmountOfStudents = 27
describe("Population statistics with a master's programme", () => {
const pathToMathMSc2020 =
'/populations?months=49&semesters=FALL&semesters=SPRING&studyRights=%7B"programme"%3A"MH50_001"%7D&year=2020'
const defaultAmountOfStudents = 26
const runTestStepWithPreAndPostParts = createRunTestStepWithPreAndPostPartsFunction(defaultAmountOfStudents)
beforeEach(() => {
cy.init(pathToCSMast2019)
cy.init(pathToMathMSc2020)
})
it('Study track filter works', () => {
runTestStepWithPreAndPostParts('StudyTrack', () => {
const card = cy.cs('StudyTrack-filter-card')
const programmeDropdown = card.cs('StudyTrack-filter-dropdown').selectFromDropdown(0)
checkFilteringResult(1)
checkFilteringResult(15)
programmeDropdown.get('i.delete').click()
})
})
describe('Studyright type filter', () => {
describe('Study right type filter', () => {
it('is visible', () => {
cy.contains('Studyright type')
cy.contains('Study right type')
})
it('is set to all by default', () => {
runTestStepWithPreAndPostParts('studyright-type', () => {
const card = cy.cs('studyright-type-filter-card')
runTestStepWithPreAndPostParts('studyRightTypeFilter', () => {
const card = cy.cs('studyRightTypeFilter-filter-card')
card.get('[data-cy="all"] input').should('be.checked')
})
})
it('works', () => {
runTestStepWithPreAndPostParts('studyright-type', () => {
const expectedBaMaStudents = 27
const expectedMasterOnlyStudents = 0
cy.cs('studyright-type-filter-card').cs('bama').click()
runTestStepWithPreAndPostParts('studyRightTypeFilter', () => {
const expectedBaMaStudents = 24
const expectedMasterOnlyStudents = 2
cy.cs('studyRightTypeFilter-filter-card').cs('bama').click()
checkFilteringResult(expectedBaMaStudents)
cy.cs('studyright-type-filter-card').cs('master').click()
cy.cs('studyRightTypeFilter-filter-card').cs('master').click()
checkFilteringResult(expectedMasterOnlyStudents)
cy.cs('studyright-type-filter-card').cs('all').click()
cy.cs('studyRightTypeFilter-filter-card').cs('all').click()
checkFilteringResult(defaultAmountOfStudents)
})
})
@@ -111,16 +114,16 @@ describe('Population Statistics with CS master', () => {
})
describe('Population Statistics', () => {
const pathToCSBach2018 =
'/populations?months=36&semesters=FALL&semesters=SPRING&studyRights={%22programme%22%3A%22KH50_005%22}&tag&year=2018'
const defaultAmountOfStudents = 159
const pathToMathBSc2020 =
'/populations?months=49&semesters=FALL&semesters=SPRING&studyRights=%7B"programme"%3A"KH50_001"%7D&year=2020'
const defaultAmountOfStudents = 27
const runTestStepWithPreAndPostParts = createRunTestStepWithPreAndPostPartsFunction(defaultAmountOfStudents)
beforeEach(() => {
cy.init(pathToCSBach2018)
cy.init(pathToMathBSc2020)
})
it('Transfer filter is on not transferred by default', () => {
it("Transfer filter is set to 'not transferred' by default", () => {
cy.contains('By default only students who have not transferred to this study programme are shown.')
runTestStepWithPreAndPostParts('TransferredToProgramme', () => {
@@ -133,7 +136,7 @@ describe('Population Statistics', () => {
runTestStepWithPreAndPostParts('GraduatedFromProgramme', () => {
const getCard = () => cy.cs('GraduatedFromProgramme-filter-card')
const graduated = 11
const graduated = 16
getCard().cs('option-graduated-true').click()
checkFilteringResult(graduated)
getCard().cs('option-graduated-false').click()
@@ -142,45 +145,45 @@ describe('Population Statistics', () => {
})
})
// it('Transfer filter works', () => {
// runTestStepWithPreAndPostParts('TransferredToProgramme', () => {
// const transferred = 1
// cy.cs('TransferredToProgramme-filter-card').cs('option-have').click()
// checkFilteringResult(transferred)
// cy.cs('TransferredToProgramme-filter-card').cs('option-all').click()
// checkFilteringResult(defaultAmountOfStudents + transferred)
// cy.cs('TransferredToProgramme-filter-card').cs('option-havenot').click()
// })
// })
it('Transfer filter works', () => {
runTestStepWithPreAndPostParts('TransferredToProgramme', () => {
const transferred = 3
cy.cs('TransferredToProgramme-filter-card').cs('option-have').click()
checkFilteringResult(transferred)
cy.cs('TransferredToProgramme-filter-card').cs('option-all').click()
checkFilteringResult(defaultAmountOfStudents + transferred)
cy.cs('TransferredToProgramme-filter-card').cs('option-havenot').click()
})
})
it('Enrollment filter works', () => {
runTestStepWithPreAndPostParts('EnrollmentStatus', () => {
cy.cs('enrollmentStatusFilter-status').selectFromDropdown(0)
cy.cs('enrollmentStatusFilter-semesters').selectFromDropdown(5)
checkFilteringResult(148)
checkFilteringResult(25)
clearSemanticUIMultipleDropDownSelection('enrollmentStatusFilter-semesters')
})
})
it('Credit filter works', { retries: 3 }, () => {
runTestStepWithPreAndPostParts('CreditsEarned', () => {
testRangeFilter('CreditsEarned-filter-card', 50, 150, 118)
testRangeFilter('CreditsEarned-filter-card', 50, 150, 12)
})
})
it('Age filter works', { retries: 3 }, () => {
cy.clock(MOCKED_DATE, ['Date'])
runTestStepWithPreAndPostParts('Age', () => {
testRangeFilter('Age-filter-card', 20, 40, 47)
testRangeFilter('Age-filter-card', 23, 30, 21)
})
})
it('Gender filter works', () => {
runTestStepWithPreAndPostParts('Gender', () => {
cy.cs('genderFilter-dropdown').selectFromDropdown(0)
checkFilteringResult(42)
checkFilteringResult(13)
cy.cs('genderFilter-dropdown').selectFromDropdown(1)
checkFilteringResult(117)
checkFilteringResult(14)
cy.cs('genderFilter-dropdown').selectFromDropdown(2)
checkFilteringResult(0)
cy.cs('genderFilter-dropdown').selectFromDropdown(3)
@@ -194,38 +197,37 @@ describe('Population Statistics', () => {
cy.cs('startYearAtUni-dropdown').selectFromDropdown(0)
checkFilteringResult(1)
clearSemanticUIMultipleDropDownSelection('startYearAtUni-dropdown')
cy.cs('startYearAtUni-dropdown').selectFromDropdown(14)
checkFilteringResult(136)
cy.cs('startYearAtUni-dropdown').selectFromDropdown(6)
checkFilteringResult(21)
clearSemanticUIMultipleDropDownSelection('startYearAtUni-dropdown')
})
})
// Return when we have +2020 population with admission types in data
it.skip('Admission type filter works', () => {
runTestStepWithPreAndPostParts('AdmissionTypeFilter', () => {
it('Admission type filter works', () => {
runTestStepWithPreAndPostParts('AdmissionType', () => {
cy.cs('admissionTypeFilter-dropdown').selectFromDropdown(0)
checkFilteringResult(5)
cy.cs('admissionTypeFilter-dropdown').selectFromDropdown(2)
checkFilteringResult(10)
checkFilteringResult(18)
cy.cs('admissionTypeFilter-dropdown').selectFromDropdown(1)
checkFilteringResult(6)
clearSingleDropdownSelection('admissionTypeFilter-dropdown')
})
})
it('Courses filter works', () => {
runTestStepWithPreAndPostParts('Courses', () => {
const courses = ['TKT20002', 'MAT11002']
cy.cs('courseFilter-course-dropdown').click().contains(`${courses[0]} - Ohjelmistotekniikka`).click()
checkFilteringResult(133)
cy.cs(`courseFilter-${courses[0]}-dropdown`).selectFromDropdown(1)
checkFilteringResult(127)
cy.cs('courseFilter-course-dropdown')
.click()
.contains(`${courses[1]} - Lineaarialgebra ja matriisilaskenta I`)
.click()
checkFilteringResult(100)
cy.cs(`courseFilter-${courses[1]}-dropdown`).selectFromDropdown(2)
checkFilteringResult(1)
courses.forEach(course => cy.cs(`courseFilter-${course}-clear`).click())
const courses = [
{ code: 'MAT21012', name: 'Differentiaaliyhtälöt I' },
{ code: 'MFK-M204', name: 'Matematiikkaa kaikkialla' },
]
cy.cs('courseFilter-course-dropdown').click().contains(`${courses[0].code} - ${courses[0].name}`).click()
checkFilteringResult(10)
cy.cs(`courseFilter-${courses[0].code}-dropdown`).selectFromDropdown(1) // Passed students
checkFilteringResult(8)
cy.cs('courseFilter-course-dropdown').click().contains(`${courses[1].code} - ${courses[1].name}`).click()
checkFilteringResult(6)
cy.cs(`courseFilter-${courses[1].code}-dropdown`).selectFromDropdown(1)
checkFilteringResult(5)
courses.forEach(({ code }) => cy.cs(`courseFilter-${code}-clear`).click())
})
})
@@ -235,7 +237,7 @@ describe('Population Statistics', () => {
runTestStepWithPreAndPostParts('Age', () => {
const getCard = () => cy.cs('GraduatedFromProgramme-filter-card')
getCard().cs('option-graduated-true').click()
testRangeFilter('Age-filter-card', 20, 30, 1)
testRangeFilter('Age-filter-card', 23, 30, 12)
getCard().cs('option-all').click()
})
})
@@ -244,35 +246,35 @@ describe('Population Statistics', () => {
it('"Reset All Filters" button works', { retries: 3 }, () => {
cy.cs('Gender-header').click()
cy.cs('genderFilter-dropdown').selectFromDropdown(0)
checkFilteringResult(42)
checkFilteringResult(13)
cy.cs('StartYearAtUni-header').click()
cy.cs('startYearAtUni-dropdown').selectFromDropdown(7)
checkFilteringResult(33)
cy.cs('startYearAtUni-dropdown').selectFromDropdown(5)
checkFilteringResult(8)
cy.cs('reset-all-filters').click()
checkFilteringResult(defaultAmountOfStudents)
})
it('Studyright type filter is not visible', () => {
cy.contains('Studyright type').should('not.exist')
it('Study right type filter is not visible', () => {
cy.contains('Study right type').should('not.exist')
})
})
describe('Course Statistics', () => {
const pathToDSAndAlgoSpring2019 =
'/coursepopulation?coursecodes=%5B%22TKT20001%22%2C%2258131%22%5D&from=138&separate=true&to=138&years=Kev%C3%A4t%202019&unifyCourses=unifyStats'
const defaultAmountOfStudents = 118
const pathToLimits2021 =
'/coursepopulation?coursecodes=%5B"MAT11003"%2C"57116"%2C"57016"%2C"AYMAT11003"%5D&from=72&separate=false&to=72&unifyCourses=unifyStats&years=2021-2022'
const defaultAmountOfStudents = 48
const runTestStepWithPreAndPostParts = createRunTestStepWithPreAndPostPartsFunction(defaultAmountOfStudents)
beforeEach(() => {
cy.init(pathToDSAndAlgoSpring2019)
cy.init(pathToLimits2021)
})
it('Grade filter works', () => {
runTestStepWithPreAndPostParts('Grade', () => {
cy.cs('gradeFilter-5').click()
checkFilteringResult(20)
checkFilteringResult(23)
cy.cs('gradeFilter-3').click()
checkFilteringResult(38)
checkFilteringResult(29)
cy.cs('gradeFilter-3').click()
cy.cs('gradeFilter-5').click()
})
@@ -288,24 +290,24 @@ describe('Course Statistics', () => {
runTestStepWithPreAndPostParts('Programme', () => {
const card = cy.cs('Programme-filter-card')
const programmeDropdown = card.cs('Programme-filter-dropdown').selectFromDropdown(1)
checkFilteringResult(116)
checkFilteringResult(33)
programmeDropdown.get('i.delete').click()
})
})
it('Age filter works', () => {
it('Age filter works', { retries: 3 }, () => {
cy.clock(MOCKED_DATE, ['Date'])
runTestStepWithPreAndPostParts('Age', () => {
testRangeFilter('Age-filter-card', 20, 40, 33)
testRangeFilter('Age-filter-card', 21, 30, 36)
})
})
it('Gender filter works', () => {
runTestStepWithPreAndPostParts('Gender', () => {
cy.cs('genderFilter-dropdown').selectFromDropdown(0)
checkFilteringResult(27)
checkFilteringResult(9)
cy.cs('genderFilter-dropdown').selectFromDropdown(1)
checkFilteringResult(91)
checkFilteringResult(39)
cy.cs('genderFilter-dropdown').selectFromDropdown(2)
checkFilteringResult(0)
cy.cs('genderFilter-dropdown').selectFromDropdown(3)
@@ -316,11 +318,11 @@ describe('Course Statistics', () => {
it('Starting year filter works', () => {
runTestStepWithPreAndPostParts('StartYearAtUni', () => {
cy.cs('startYearAtUni-dropdown').selectFromDropdown([0])
cy.cs('startYearAtUni-dropdown').selectFromDropdown(0)
checkFilteringResult(1)
clearSemanticUIMultipleDropDownSelection('startYearAtUni-dropdown')
cy.cs('startYearAtUni-dropdown').selectFromDropdown(13)
checkFilteringResult(71)
cy.cs('startYearAtUni-dropdown').selectFromDropdown(10)
checkFilteringResult(24)
clearSemanticUIMultipleDropDownSelection('startYearAtUni-dropdown')
})
})
@@ -329,9 +331,10 @@ describe('Course Statistics', () => {
cy.clock(MOCKED_DATE, ['Date'])
runTestStepWithPreAndPostParts('Grade', () => {
runTestStepWithPreAndPostParts('Age', () => {
cy.cs('gradeFilter-3').click()
testRangeFilter('Age-filter-card', 20, 30, 1)
cy.cs('gradeFilter-3').click()
cy.cs('gradeFilter-5').click()
checkFilteringResult(23)
testRangeFilter('Age-filter-card', 21, 30, 17)
cy.cs('gradeFilter-5').click()
})
})
})
@@ -354,7 +357,7 @@ describe('Custom Population Statistics', () => {
it('Age filter works', () => {
cy.clock(MOCKED_DATE, ['Date'])
runTestStepWithPreAndPostParts('Age', () => {
testRangeFilter('Age-filter-card', 20, 30, 1)
testRangeFilter('Age-filter-card', 24, 28, 5)
})
})
@@ -367,8 +370,8 @@ describe('Custom Population Statistics', () => {
it('Programme filter works', () => {
runTestStepWithPreAndPostParts('Programme', () => {
const card = cy.cs('Programme-filter-card')
const programmeDropdown = card.cs('Programme-filter-dropdown').selectFromDropdown(0)
checkFilteringResult(5)
const programmeDropdown = card.cs('Programme-filter-dropdown').selectFromDropdown(1)
checkFilteringResult(3)
programmeDropdown.get('i.delete').click()
})
})
@@ -376,39 +379,38 @@ describe('Custom Population Statistics', () => {
it('Gender filter works', () => {
runTestStepWithPreAndPostParts('Gender', () => {
cy.cs('genderFilter-dropdown').selectFromDropdown(0)
checkFilteringResult(3)
checkFilteringResult(5)
cy.cs('genderFilter-dropdown').selectFromDropdown(1)
checkFilteringResult(2)
checkFilteringResult(3)
cy.cs('genderFilter-dropdown').selectFromDropdown(2)
checkFilteringResult(0)
clearSingleDropdownSelection('genderFilter-dropdown')
checkFilteringResult(5, true)
})
})
it('Starting year filter works', () => {
runTestStepWithPreAndPostParts('StartYearAtUni', () => {
cy.cs('startYearAtUni-dropdown').selectFromDropdown([0])
checkFilteringResult(1)
cy.cs('startYearAtUni-dropdown').selectFromDropdown(0)
checkFilteringResult(2)
clearSemanticUIMultipleDropDownSelection('startYearAtUni-dropdown')
cy.cs('startYearAtUni-dropdown').click()
})
})
it('Courses filter works', () => {
runTestStepWithPreAndPostParts('Courses', () => {
const courses = ['MAT11001', 'TKT20004']
cy.cs('courseFilter-course-dropdown').click().contains(`${courses[0]} - Johdatus yliopistomatematiikkaan`).click()
const courses = [
{ code: 'MAT11005', name: 'Integraalilaskenta' },
{ code: 'MAT21007', name: 'Mitta ja integraali' },
]
cy.cs('courseFilter-course-dropdown').click().contains(`${courses[0].code} - ${courses[0].name}`).click()
checkFilteringResult(7)
cy.cs(`courseFilter-${courses[0].code}-dropdown`).selectFromDropdown(1)
checkFilteringResult(6)
cy.cs('courseFilter-course-dropdown').click().contains(`${courses[1].code} - ${courses[1].name}`).click()
checkFilteringResult(5)
cy.cs(`courseFilter-${courses[0]}-dropdown`).selectFromDropdown(1)
checkFilteringResult(5)
cy.cs('courseFilter-course-dropdown').click().contains(`${courses[1]} - Tietoliikenteen perusteet`).click()
checkFilteringResult(5)
cy.cs(`courseFilter-${courses[1]}-dropdown`).selectFromDropdown(1)
checkFilteringResult(3)
cy.cs(`courseFilter-${courses[1]}-dropdown`).selectFromDropdown(2)
checkFilteringResult(1)
courses.forEach(course => cy.cs(`courseFilter-${course}-clear`).click())
cy.cs(`courseFilter-${courses[1].code}-dropdown`).selectFromDropdown(3)
checkFilteringResult(2)
courses.forEach(({ code }) => cy.cs(`courseFilter-${code}-clear`).click())
})
})
})
+91 -33
View File
@@ -1,58 +1,116 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const hasLanded = () => {
cy.contains('This view displays amounts')
cy.contains('Show number of')
}
/*
TODO FIX
These use dumb numbers, because actual_studyright update in db is not yet
in anon data, and the faculties do not get resolved.
*/
const chooseSemester = (semester, fromOrTo) => {
cy.get(`[data-cy="semester-${fromOrTo}"]`)
.click()
.within(() => {
cy.get('div.visible.menu.transition').within(() => {
cy.contains('div.item', semester).click()
})
cy.get('div.menu.transition').should('not.have.class', 'visible')
})
}
const checkNumbers = (numbers, numberOfColumns, mode) => {
const offset = mode === 'semesters' ? 2 : 1
cy.get('table > tbody > tr:first').within(() => {
cy.get('td').should('have.length', numberOfColumns)
cy.get('td').eq(0).contains('All courses total')
numbers.forEach((number, index) => {
cy.get('td')
.eq(index + offset)
.contains(number)
})
})
}
const checkStyle = (styles, numberOfColumns) => {
cy.get('table > tbody > tr:first').within(() => {
cy.get('td').should('have.length', numberOfColumns)
styles.forEach((style, index) => {
cy.get('td')
.eq(index + 2)
.should('have.attr', 'style')
.and('include', style)
})
})
}
describe('When language center is opened', () => {
describe('as an admin user', () => {
beforeEach(() => {
cy.init('/languagecenterview', 'admin')
cy.get('[data-cy="completions-button"]').click()
cy.get('[data-cy="semester-from"]').click()
cy.contains('Syksy 2017').click()
cy.get('[data-cy="semester-to"]').click()
hasLanded()
})
it('Initial view is correct', () => {
cy.contains('All courses total')
cy.contains('Academic and Professional')
cy.contains('AYKK-RUKIRJ')
describe('Faculties tab', () => {
beforeEach(() => {
chooseSemester('Syksy 2017', 'from')
chooseSemester('Kevät 2024', 'to')
})
it('Initial view is correct', () => {
cy.contains('All courses total')
cy.contains('Academic and Professional')
cy.contains('AYKK-RUKIRJ')
})
it('Faculties tab shows numbers', () => {
cy.get('table > tbody > tr:first').within(() => {
cy.get('td').should('have.length', 15)
cy.get('td').eq(0).contains('All courses total')
const numbers = [36, 9, 34, 755, 15, 40, 6, 48, 1, 24, 1059, 49, 0, 2076]
numbers.forEach((number, index) => {
cy.get('td')
.eq(index + 1)
.contains(number)
})
})
})
it('Faculties tab "exceeding" button works', () => {
cy.get('[data-cy="difference-button"]').click()
checkNumbers([...new Array(12).fill(0), 1, 227], 15)
})
it('Faculties tab semester selector changes numbers', () => {
chooseSemester('Syksy 2020', 'from')
checkNumbers([28, 6, 28, 495, 4, 10, 2, 33, 0, 9, 535, 34, 0, 1184], 15)
})
})
it('Faculties tab shows numbers', () => {
cy.contains('All courses total')
cy.contains('1363')
})
describe('Semester tab', () => {
beforeEach(() => {
cy.contains('By semesters').click()
chooseSemester('Syksy 2017', 'from')
chooseSemester('Kevät 2024', 'to')
})
it('Faculties tab "exceeding" button works', () => {
cy.get('[data-cy="difference-button"]').click()
cy.contains('0')
cy.contains('Total')
cy.contains('1363').should('not.exist')
})
it('Semester tab shows numbers', () => {
checkNumbers([69, 26, 298, 58, 343, 98, 438, 138, 310, 90, 123, 74, 10, 1, 2076], 17, 'semesters')
})
it('Faculties tab semester selector changes numbers', () => {
cy.get('[data-cy="semester-from"]').click()
cy.contains('Syksy 2020').click()
cy.contains('1363').should('not.exist')
cy.contains('All courses total')
})
it('Coloring mode works on semester tab', () => {
cy.contains('Compare to average of course').click()
const expectedAlphas = [0.04, 0.016, 0.17, 0.03, 0.192, 0.055, 0.247, 0.08, 0.173, 0.05, 0.07, 0.043, 0.004, 0]
it('Semester tab opens and contains coloring filter', () => {
cy.contains('By semesters').click()
cy.contains('1363')
cy.contains('Coloring mode')
checkStyle(
expectedAlphas.map(alpha => `background-color: rgba(0, 170, 0, ${alpha})`),
17
)
cy.get('table > tbody > tr:first').within(() => {
cy.get('td').eq(16).should('have.attr', 'style').and('not.include', 'background-color')
})
})
})
})
describe('with a user with no rights', () => {
it('"Access denied" is shown', () => {
cy.init('/languagecenterview', 'norights')
+3 -3
View File
@@ -1,4 +1,4 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const deleteAllSearches = () => {
cy.contains('Saved populations')
@@ -7,8 +7,8 @@ const deleteAllSearches = () => {
.parent()
.parent()
.get('.dropdown')
.then(d => {
const searchItems = d.find('div[role=option] > span[class=text]')
.then(dropdown => {
const searchItems = dropdown.find('div[role=option] > span[class=text]')
for (let i = 0; i < searchItems.length; i++) {
if (searchItems[i].textContent.includes('TEST-')) {
cy.get('[data-cy="history-search"]').children().eq(0).type(searchItems[i].textContent).type('{enter}')
+274 -137
View File
@@ -1,72 +1,89 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
// Now "Class statistics" in UI
describe('Population Statistics tests', () => {
const pathToCSBach2017 =
'/populations?months=36&semesters=FALL&semesters=SPRING&studyRights={%22programme%22%3A%22KH50_005%22}&tag&year=2017'
const pathToCSMaster2019 =
'/populations?months=27&semesters=FALL&semesters=SPRING&studyRights=%7B%22programme%22%3A%22MH50_009%22%7D&tag&year=2019'
describe('when using basic user', () => {
const selectStudyProgramme = programme => {
cy.contains('Select study programme')
cy.get('[data-cy=select-study-programme]').click().children().contains(programme).click()
}
const selectStudyTrack = studyTrack => {
cy.contains('Select study track')
cy.get('[data-cy=select-study-track]').click().children().contains(studyTrack).click()
}
describe('Population statistics tests', () => {
const getPath = programme => {
return `/populations?months=49&semesters=FALL&semesters=SPRING&studyRights=%7B"programme"%3A"${programme}"%7D&year=2020`
}
const pathToMathBSc2020 = getPath('KH50_001')
const pathToMathMSc2020 = getPath('MH50_001')
describe('When using basic user', () => {
beforeEach(() => {
cy.init('/populations')
})
it('Population statistics search infobox works', () => {
cy.get('[data-cy="PopulationSearch-info-content"]').should('not.exist')
cy.get('[data-cy="PopulationSearch-open-info"]').click()
cy.get('[data-cy="PopulationSearch-info-content"]').should('be.visible')
cy.get('[data-cy="PopulationSearch-info-content"]').should(
'contain',
'lukuvuosi, jolloin opiskelija on ilmoittautunut'
)
cy.get('[data-cy="PopulationSearch-close-info"]').click()
cy.get('[data-cy="PopulationSearch-info-content"]').should('not.exist')
})
describe('Population search', () => {
it('Info box works', () => {
cy.get('[data-cy="PopulationSearch-info-content"]').should('not.exist')
cy.get('[data-cy="PopulationSearch-open-info"]').click()
cy.get('[data-cy="PopulationSearch-info-content"]').should('be.visible')
cy.get('[data-cy="PopulationSearch-info-content"]').should('contain', 'Tässä osiossa voi tarkastella')
cy.get('[data-cy="PopulationSearch-close-info"]').click()
cy.get('[data-cy="PopulationSearch-info-content"]').should('not.exist')
})
it('Population statistics search form is usable', () => {
cy.contains('See class').should('be.disabled')
cy.contains('Search for class')
cy.contains('Class of')
.parent()
.within(() => {
cy.get('.form-control').as('enrollmentSelect')
it('Form is usable', () => {
cy.contains('Search for class')
cy.contains('See class').should('be.disabled')
cy.contains('Class of')
.parent()
.within(() => {
cy.get('.form-control').as('enrollmentSelect')
})
cy.get('@enrollmentSelect')
.its(`${[0]}.value`)
.then(beforeVal => {
cy.get('@enrollmentSelect').click()
cy.get('.yearSelectInput .rdtPrev').click({ force: true })
cy.get('.yearSelectInput table').contains('2018-2019').click({ force: true })
cy.get('@enrollmentSelect').should('not.have.value', beforeVal)
})
cy.contains('No study tracks available for this programme')
selectStudyProgramme('Matematiikan ja tilastotieteen maisteriohjelma')
cy.contains('See class').should('be.enabled')
selectStudyTrack('Matematiikka ja soveltava matematiikka')
cy.contains('See class').should('be.enabled')
})
describe('Correct population is shown for programme', () => {
it('without study tracks', () => {
cy.contains('Select study programme')
selectStudyProgramme('Matemaattisten tieteiden kandiohjelma')
cy.contains('See class').click()
cy.contains('Matemaattisten tieteiden kandiohjelma 2017 - 2018')
cy.contains('class size 47 students')
})
cy.get('@enrollmentSelect')
.its(`${[0]}.value`)
.then(beforeVal => {
cy.get('@enrollmentSelect').click()
cy.get('.yearSelectInput .rdtPrev').click({ force: true })
cy.get('.yearSelectInput table').contains('2018-2019').click({ force: true })
cy.get('@enrollmentSelect').should('not.have.value', beforeVal)
it('with study tracks', () => {
selectStudyProgramme('Matematiikan ja tilastotieteen maisteriohjelma')
selectStudyTrack('Matematiikka ja soveltava matematiikka')
cy.contains('See class').click()
cy.contains('Matematiikan ja tilastotieteen maisteriohjelma 2017 - 2018')
cy.contains('studytrack MAST-MSM')
cy.contains('class size 1 students')
})
cy.contains('Select study programme')
cy.get('[data-cy=select-study-programme]')
.click()
.children()
.contains('Tietojenkäsittelytieteen maisteriohjelma')
.click()
cy.contains('See class').should('be.enabled')
})
it('Searching for population really shows population', () => {
cy.contains('Select study programme')
cy.get('[data-cy=select-study-programme]')
.click()
.children()
.contains('Tietojenkäsittelytieteen kandiohjelma')
.click()
cy.contains('See class').click()
cy.contains('class size')
cy.contains('Tietojenkäsittelytieteen kandiohjelma')
})
})
it('Population statistics is usable on general level', () => {
cy.visit(pathToCSBach2017)
cy.visit(pathToMathBSc2020)
cy.cs('filtered-students')
cy.contains('Tietojenkäsittelytieteen kandiohjelma')
cy.contains('class size 170 students')
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('class size 30 students')
cy.contains('Excludes exchange students')
cy.contains('Excludes students with non-degree study right')
cy.contains('Excludes students who have transferred out of this programme')
@@ -74,66 +91,115 @@ describe('Population Statistics tests', () => {
})
it('Courses of class is displayed and link to individual course stats page works', () => {
cy.visit(pathToCSBach2017)
cy.visit(pathToMathBSc2020)
cy.contains('Courses of class').click()
cy.intercept('/api/v3/courseyearlystats**').as('coursePage')
cy.get('[data-cy=toggle-group-module-TKT1]').click()
cy.contains('td', 'TKT10002').siblings().find('i.level.up').click()
cy.get('[data-cy=toggle-group-module-MAT110]').click()
cy.contains('td', 'MAT11001').siblings().find('i.level.up').click()
cy.wait('@coursePage')
cy.url().should('include', '/coursestatistics')
cy.contains('TKT10002, 581325, AYTKT10002, A581325 Ohjelmoinnin perusteet')
cy.contains('MAT11001 Johdatus yliopistomatematiikkaan')
cy.contains('AYMAT11001 Avoin yo: Johdatus yliopistomatematiikkaan')
cy.contains('57033 Johdatus yliopistomatematiikkaan')
cy.contains('A57033 Avoin yo: Johdatus yliopistomatematiikkaan')
})
it('Courses of class curriculum selection works', () => {
cy.visit(pathToCSBach2017)
cy.visit(pathToMathBSc2020)
cy.contains('Courses of class').click()
cy.get('Opiskelijan digitaidot - Kumpula').should('not.exist')
cy.get('[data-cy=curriculum-picker]').click()
cy.contains('2020 - 2026').click()
cy.contains('Opiskelijan digitaidot - Kumpula')
cy.get('[data-cy=curriculum-picker]').contains('2020–2023')
cy.get('[data-cy=toggle-group-module-MAT-tyo]')
cy.get('[data-cy=curriculum-picker]').click().contains('2023–2026').click()
cy.get('[data-cy=toggle-group-module-MAT-tyo]').should('not.exist')
})
it("Empty 'tags' tab has a link to the page where tags can be created", () => {
cy.visit(pathToCSBach2017)
cy.contains('Students (170)')
it('New fetch of courses data is done when curriculum is changed', () => {
cy.visit(pathToMathBSc2020)
cy.contains('Courses of class').click()
cy.intercept('/api/v2/populationstatistics/courses').as('courseData')
cy.get('[data-cy=curriculum-picker]').click().contains('2020–2023').click()
cy.wait('@courseData').then(({ response }) => {
expect(response.body).to.have.property('allStudents')
expect(response.body).to.have.property('coursestatistics')
expect(response.body.allStudents).to.equal(27)
expect(response.body.coursestatistics.some(stat => stat.course.code === 'DIGI-100')).to.equal(true)
})
cy.get('[data-cy=curriculum-picker]').click().contains('2023–2026').click()
cy.wait('@courseData').then(({ response }) => {
expect(response.body).to.have.property('allStudents')
expect(response.body).to.have.property('coursestatistics')
expect(response.body.allStudents).to.equal(27)
expect(response.body.coursestatistics.some(stat => stat.course.code === 'DIGI-100')).to.equal(false)
})
})
it('New fetch of courses data is done when filtered students change', () => {
cy.visit(pathToMathBSc2020)
cy.contains('Courses of class').click()
cy.intercept('/api/v2/populationstatistics/courses').as('courseData')
cy.wait('@courseData').then(({ response }) => {
expect(response.body).to.have.property('allStudents')
expect(response.body).to.have.property('coursestatistics')
expect(response.body.allStudents).to.equal(27)
})
cy.get('[data-cy=GraduatedFromProgramme-filter-card]').within(() => {
cy.get('[data-cy=GraduatedFromProgramme-header]').click()
cy.get('[data-cy=option-graduated-true]').click()
cy.wait('@courseData').then(({ response }) => {
expect(response.body).to.have.property('allStudents')
expect(response.body).to.have.property('coursestatistics')
expect(response.body.allStudents).to.equal(16)
})
})
})
it("Empty 'tags' tab has a link to the page where tags can be created", { retries: 2 }, () => {
cy.visit(pathToMathBSc2020)
cy.contains('Students (27)')
.parent()
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.contains('Students (170)').click()
if (!$parentDiv.hasClass('active')) cy.contains('Students (27)').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')
cy.contains('Create a new tag')
cy.url().should('include', '/study-programme/KH50_001?p_m_tab=0&p_tab=4')
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('Create tags for study programme')
cy.contains('button', 'Create a new tag').should('be.disabled')
})
// This test sometimes fails on headless mode. It seems that the click on the
// 'Fetch class with new settings' button doesn't always trigger history.push()
// so the page doesn't reload. This is why waiting also doesn't help.
it('Advanced settings work', { retries: 2 }, () => {
cy.visit(pathToCSBach2017)
cy.visit(pathToMathMSc2020)
cy.get('[data-cy=advanced-toggle]').click()
// only spring
cy.cs('toggle-fall').click()
cy.contains('Fetch class').click()
cy.contains('Students (0)')
cy.contains('Credit accumulation (for 17 students)')
cy.url().should('not.include', 'semesters=FALL')
// only fall
cy.get('[data-cy=advanced-toggle]').click()
cy.cs('toggle-fall').click()
cy.cs('toggle-spring').click()
cy.contains('Fetch class').click()
cy.contains('Credit accumulation (for 170 students)')
cy.contains('Credit accumulation (for 9 students)')
cy.url().should('not.include', 'semesters=SPRING')
// spring + fall
cy.get('[data-cy=advanced-toggle]').click()
cy.cs('toggle-spring').click()
cy.contains('Fetch class').click()
cy.contains('Credit accumulation (for 170 students)')
cy.contains('Credit accumulation (for 26 students)')
cy.url().should('include', 'semesters=FALL&semesters=SPRING')
})
it('Credit Statistics, Credits gained tab works', () => {
cy.selectStudyProgramme('Tietojenkäsittelytieteen kandiohjelma')
it("'Credits gained' tab of 'Credit statistics' shows correct statistics for all students of the class and also students grouped by admission type", () => {
cy.clock(new Date('2024-08-30').getTime(), ['Date'])
cy.visit(pathToMathBSc2020)
const totalStudents = 27
cy.contains('Credit statistics')
.parent()
.then($parentDiv => {
@@ -141,79 +207,122 @@ 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 limits = [45, 90, 135, 180, null]
const ranges = limits.map((limit, i) => (i === 0 ? [1, limit] : [limits[i - 1], limit]))
const limits = [1, 45, 90, 135, 180, null]
const ranges = limits.map((limit, i) => (i === 0 ? [null, 0] : [limits[i - 1], limit])).reverse()
cy.get('.credits-gained-table').should('contain', '(n = 170)')
for (const [start, end] of ranges) {
let value = 'credits'
if (start !== null) {
value = `${start} ≤ ${value}`
}
if (end !== null) {
value = `${value} < ${end}`
}
cy.get('.credits-gained-table').should('contain', value)
const getTableData = (selector, numbersOfStudents) => {
const studentsInCategory = numbersOfStudents.reduce((acc, val) => acc + val, 0)
return ranges.map((range, index) => ({
selector: `[data-cy='credits-gained-table-${selector}']`,
start: range[0],
end: range[1],
students: numbersOfStudents[index],
percentage: `${((numbersOfStudents[index] / studentsInCategory) * 100).toFixed(1)}%`,
}))
}
cy.get("[data-cy='credits-gained-table-body'] td:nth-child(3)").then($els => {
const sum = [...$els].map($el => parseInt($el.innerText, 10)).reduce((a, b) => a + b, 0)
expect(sum).to.equal(170)
cy.get("[data-cy='credits-gained-table-All students of the class'] table thead tr").within(() => {
cy.get('th').eq(1).contains('Credits gained between 01.08.2020 and 30.08.2024 (49 months)')
cy.get('th').eq(2).contains('Number of students')
cy.get('th').eq(2).contains(`(n = ${totalStudents})`)
cy.get('th').eq(3).contains('Percentage of population')
})
cy.get("[data-cy='credits-gained-table-Ei valintatapaa']").should('not.exist')
})
it('Credit Statistics, Credits gained tab shows stats by admissions', () => {
cy.visit(pathToCSMaster2019)
cy.cs('filtered-students')
cy.contains('Credit statistics')
cy.get("[data-cy='Credit statistics']")
.parent()
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.get("[data-cy='Credit statistics']").click()
for (const category of [
getTableData('All students of the class', [9, 5, 7, 4, 2, 0]),
getTableData('Avoin väylä', [0, 1, 3, 2, 0, 0]),
getTableData('Muu', [0, 1, 1, 1, 0, 0]),
getTableData('Todistusvalinta', [9, 3, 3, 1, 2, 0]),
]) {
category.forEach(({ selector, start, end, students, percentage }, index) => {
let value
if (start === null) {
value = '0'
} else if (end === null) {
value = `${start} ≤ credits`
} else {
value = `${start} ≤ credits < ${end}`
}
cy.get(`${selector} [data-cy='credits-gained-table-body']`).within(() => {
cy.get('tr')
.eq(index)
.within(() => {
cy.get('td').eq(1).contains(value)
cy.get('td').eq(2).contains(students)
cy.get('td').eq(3).contains(percentage)
})
})
})
cy.contains('Credits gained').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')
cy.get("[data-cy='credits-gained-table-Ei valintatapaa']").should('exist')
}
cy.get(
"[data-cy='credits-gained-table-All students of the class'] [data-cy='credits-gained-table-body'] td:nth-child(3)"
).then(tds => {
const sum = [...tds].reduce((acc, td) => acc + parseInt(td.innerText, 10), 0)
expect(sum).to.equal(totalStudents)
})
})
it('Credit Statistics, Statistics pane works', () => {
cy.selectStudyProgramme('Tietojenkäsittelytieteen kandiohjelma')
it("'Statistics' tab of 'Credit statistics' shows correct statistics for all students of the class and also students grouped by admission type", () => {
cy.visit(pathToMathBSc2020)
cy.contains('Credit statistics')
.parent()
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.contains('Credit statistics').click()
})
cy.get("[data-cy='credit-stats-tab'] > .menu > :nth-child(2)").click()
const rows = ['Total credits', 'Average', 'Median', 'Standard deviation', 'Minimum', 'Maximum']
const categories = [
{
selector: 'All students of the population',
data: ['3773.50', '139.76', '141.00', '60.07', '19', '268'],
size: 27,
},
{
selector: 'Muu',
data: ['359.00', '119.67', '111.00', '43.71', '71', '177'],
size: 3,
},
{
selector: 'Todistusvalinta',
data: ['2742.50', '152.36', '178.00', '65.75', '19', '268'],
size: 18,
},
{
selector: 'Avoin väylä',
data: ['672.00', '112.00', '104.00', '29.40', '82', '169'],
size: 6,
},
]
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')
for (const { selector, data, size } of categories) {
cy.get(`[data-cy='statistics-table-${selector}']`).within(() => {
cy.contains('h3', selector)
cy.contains("[data-cy='credit-stats-population-size']", `n = ${size}`)
cy.get('table tbody').within(() => {
rows.forEach((text, index) => {
cy.get('tr')
.eq(index)
.within(() => {
cy.get('td').eq(0).contains(text)
cy.get('td').eq(1).contains(data[index])
})
})
})
})
}
})
})
describe('when using admin', () => {
beforeEach(() => {
cy.init(pathToCSBach2017, 'admin')
})
describe('When using admin', () => {
it('Student list checking works as intended', () => {
const existing = '010113437'
const nonExisting = '66666666'
cy.visit(pathToCSBach2017)
cy.contains('Students (170)')
cy.init(pathToMathBSc2020, 'admin')
const existing = '433237'
const nonExisting = '550004'
cy.contains('Students (27)')
.parent()
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.contains('Students (170)').click()
if (!$parentDiv.hasClass('active')) cy.contains('Students (27)').click()
})
cy.contains(existing)
cy.contains(nonExisting).should('not.exist')
@@ -227,16 +336,18 @@ describe('Population Statistics tests', () => {
cy.contains('Student numbers in list but not in Sisu').click()
cy.contains('#notfound', nonExisting)
cy.contains('Student numbers in Sisu but not in list').click()
cy.contains('#notsearched', '010614509')
cy.contains('#notsearched', '457144')
})
cy.contains('button', 'Close').click()
cy.contains('Student numbers in list and in Sisu').should('not.exist')
})
})
describe('when using IAM user', () => {
describe('When using IAM user', () => {
beforeEach(() => {
cy.init(pathToCSBach2017, 'onlyiamrights')
cy.contains('Tietojenkäsittelytieteen kandiohjelma')
cy.contains('class size 170 students')
cy.init(pathToMathBSc2020, 'onlyiamrights')
cy.contains('Matemaattisten tieteiden kandiohjelma 2020 - 2021')
cy.contains('class size 30 students')
})
it('Population statistics is visible', () => {
@@ -245,13 +356,39 @@ describe('Population Statistics tests', () => {
cy.contains('Excludes students with non-degree study right')
cy.contains('Excludes students who have transferred out of this programme')
})
})
cy.contains('Credit accumulation (for 170 students)')
cy.contains('Credit statistics')
it('Only correct panes are visible', () => {
const correctPanes = [
'Credit accumulation (for 27 students)',
'Credit statistics',
'Age distribution',
'Courses of class',
]
cy.get('.accordion.ui.fluid.styled').within(() => {
cy.get('.title').should('have.length', correctPanes.length)
correctPanes.forEach((pane, index) => {
cy.get('.title').eq(index).contains(pane)
})
})
})
it('Ages cannot be ungrouped', () => {
cy.contains('Age distribution')
.parent()
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.contains('Age distribution').click()
})
cy.contains('ui.checked.toggle.checkbox', 'Group ages').should('not.exist')
})
it('Age filter is not visible', () => {
cy.get("[data-cy='filtered-students']")
cy.get("[data-cy='Age-filter-card']").should('not.exist')
})
it('Students tab is not available', () => {
cy.contains('Students (170)').should('not.exist')
cy.contains('Students (27)').should('not.exist')
})
})
})
+271 -113
View File
@@ -1,119 +1,277 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const student = {
firstnames: 'Varpu Roope',
lastname: 'Mårtensson',
studentnumber: '550003',
sis_person_id: 'hy-hlo-115926826',
email: 'sisutestidata134902@testisisudata.fi',
}
const typeStudentNumberAndClick = studentNumber => {
cy.get('.prompt').type(studentNumber)
cy.contains('td a', studentNumber).click()
}
describe('Students tests', () => {
beforeEach(() => {
cy.init()
cy.contains('Students').click()
cy.contains('Show student names')
})
const student = {
firstnames: 'Matti Jaakko',
lastname: 'Nieminen',
studentnumber: '010654019',
sis_person_id: 'hy-hlo-84830887',
}
it('Students search form is usable', () => {
cy.contains('Show student names')
cy.url().should('include', '/students')
cy.get('.prompt').type(student.lastname)
cy.contains('Student number')
cy.contains('Started')
cy.contains('Credits')
cy.contains(student.firstnames).should('not.exist')
cy.cs('toggleStudentNames').click()
cy.contains(student.firstnames)
cy.cs('toggleStudentNames').click()
cy.contains(student.firstnames).should('not.exist')
})
it('can search with studentnumber too', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains(student.studentnumber)
})
it('Can get student specific page by clicking student', () => {
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(student.lastname).should('not.exist')
cy.contains(student.firstnames).should('not.exist')
cy.cs('toggleStudentNames').click()
cy.contains(student.lastname)
cy.contains(student.firstnames)
cy.cs('toggleStudentNames').click()
cy.contains(student.lastname).should('not.exist')
cy.contains(student.firstnames).should('not.exist')
})
it('Can get back to search menu', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.go('back')
cy.contains('Student number').should('not.exist')
cy.contains('Credits').should('not.exist')
})
it('Can jump to course', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.contains('Introduction to Machine Learning (DATA11002)')
.parent()
.siblings()
.last()
.within(() => {
cy.get('.level').click()
})
cy.url().should('include', '/coursestatistics')
cy.contains('Introduction to Machine Learning')
})
it('Has correct Sisu link', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.get('[data-cy=sisulink] > a')
.should('have.attr', 'href')
.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')
cy.get('.prompt').clear().type('01114')
cy.contains('Student number').should('not.exist')
})
it('Can jump to population page', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.contains('.ui.table', 'Completed').within(() => {
cy.get('i.level.up.alternate.icon').click()
describe('when using basic user', () => {
beforeEach(() => {
cy.init()
cy.contains('Students').click()
cy.url().should('include', '/students')
cy.contains('Show student names')
})
cy.contains('Tietojenkäsittelytieteen kandiohjelma')
cy.contains('class size')
})
})
// Use admin to see all students
describe('Testing with admin rights', () => {
it('Does not crash if student has no studyright or courses', () => {
const studentNumber = '011825096'
cy.init(`/students/${studentNumber}`, 'admin') // use admin to see all students
cy.contains('Credits: 0')
it('Students search form is usable', () => {
cy.get('.prompt').type(student.lastname)
cy.contains('Student number')
cy.contains('Started')
cy.contains('Credits')
cy.contains(student.firstnames).should('not.exist')
cy.cs('toggleStudentNames').click()
cy.contains(student.firstnames)
cy.cs('toggleStudentNames').click()
cy.contains(student.firstnames).should('not.exist')
})
it('Search term must be at least 4 characters long', () => {
cy.get('.prompt').type(student.lastname.slice(0, 3))
cy.contains('No search results or search term is not accurate enough')
cy.get('.prompt').type(student.lastname.slice(3))
cy.get('table tbody tr').should('have.length', 1)
})
it('can search with studentnumber too', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains(student.studentnumber)
})
it('Can get student specific page by clicking student', () => {
cy.get('.prompt').type(student.studentnumber)
cy.contains('td a', student.studentnumber).click()
cy.contains('Matemaattisten tieteiden kandiohjelma (01.08.2020–31.07.2027)')
cy.contains(student.lastname).should('not.exist')
cy.contains(student.firstnames).should('not.exist')
cy.contains(student.email).should('not.exist')
cy.cs('toggleStudentNames').click()
cy.contains(student.lastname)
cy.contains(student.firstnames)
cy.contains(student.email)
cy.cs('toggleStudentNames').click()
cy.contains(student.lastname).should('not.exist')
cy.contains(student.firstnames).should('not.exist')
cy.contains(student.email).should('not.exist')
})
it("'Update student' button is not shown", () => {
typeStudentNumberAndClick(student.studentnumber)
cy.get('div.ui.fluid.card').within(() => {
cy.contains('button', 'Update student').should('not.exist')
})
})
it('Can get back to search menu', () => {
typeStudentNumberAndClick(student.studentnumber)
cy.go('back')
cy.contains('Student number').should('not.exist')
cy.contains('Credits').should('not.exist')
})
it('Can jump to course', () => {
typeStudentNumberAndClick(student.studentnumber)
cy.contains('Tilastollinen päättely I (MAT12004)')
.parent()
.siblings()
.last()
.within(() => {
cy.get('.level').click()
})
cy.url().should('include', '/coursestatistics')
cy.contains('MAT12004 Tilastollinen päättely I')
cy.contains('AYMAT12004 Avoin yo: Tilastollinen päättely I')
cy.contains('57046 Johdatus tilastolliseen päättelyyn')
})
it('Has correct Sisu link', () => {
typeStudentNumberAndClick(student.studentnumber)
cy.get('[data-cy=sisulink] > a')
.should('have.attr', 'href')
.and('include', `https://sisu.helsinki.fi/tutor/role/staff/student/${student.sis_person_id}/basic/basic-info`)
})
it('Semester enrollments can be toggled', () => {
typeStudentNumberAndClick(student.studentnumber)
const programmes = [
'Oikeustieteen tohtoriohjelma',
'Matemaattisten tieteiden kandiohjelma',
'Oikeustieteen maisterin koulutusohjelmaOikeusnotaarin koulutusohjelma',
]
cy.get('div.ui.fluid.card').within(() => {
cy.contains('Enrollments').click()
cy.get('table tbody tr')
.should('have.length', 3)
.each(($tr, index) => {
cy.wrap($tr).within(() => {
cy.get('td').eq(0).should('have.text', programmes[index])
})
})
})
})
it('Searching with bad inputs doesnt yield results', () => {
cy.get('.prompt').type('SWAG LITTINEN')
cy.contains('Student number').should('not.exist')
cy.get('.prompt').clear().type('01114')
cy.contains('Student number').should('not.exist')
})
it('Can jump to population page', () => {
typeStudentNumberAndClick(student.studentnumber)
cy.contains('.ui.table', 'Completed').within(() => {
cy.get('i.level.up.alternate.icon').click()
})
cy.contains('Matemaattisten tieteiden kandiohjelma 2020 - 2021')
cy.contains('class size 30 students')
})
it('Grade graph works in all three different modes', () => {
typeStudentNumberAndClick(student.studentnumber)
cy.contains('a.item', 'Grade graph').click()
cy.contains('text.highcharts-title', 'Grade plot')
cy.contains('a.item', 'Show group mean').click()
cy.get('.labeled.input').within(() => {
cy.contains('.label', 'Group size')
cy.get('input').should('have.value', '5')
})
cy.contains('.highcharts-container text', "Nov '22")
cy.contains('.highcharts-container text', "Jul '22").should('not.exist')
cy.contains('text.highcharts-title', 'Grade plot')
cy.get('.labeled.input').within(() => {
cy.contains('.label', 'Group size')
cy.get('input').clear().type('10')
cy.get('input').should('have.value', '10')
})
cy.contains('.highcharts-container text', "Jul '22")
cy.contains('.highcharts-container text', "Nov '22").should('not.exist')
cy.contains('a.item', 'Show semester mean').click()
cy.contains('.highcharts-container text', "Jan '22")
})
describe('Bachelor Honours section', () => {
it("Shows 'Qualified for Honours' tag and main modules info when the student is qualified", () => {
cy.visit('/students/495976')
cy.contains('.divider h4', 'Bachelor Honours')
cy.contains('.green.tag.label', 'Qualified for Honours')
cy.contains('Main courses and other modules').click()
cy.get('[data-cy=main-modules] tbody tr')
.should('have.length', 3)
.each(($tr, index) => {
const info = [
['07.05.2020', 'Matemaattisten tieteiden kandiohjelma (KH50_001)', 'Hyv.'],
['08.05.2018', 'Matematiikka, perusopinnot (MAT110)', '4'],
['25.02.2020', 'Matematiikka, aineopinnot (MAT210)', '4'],
]
cy.wrap($tr).within(() => {
info[index].forEach((text, i) => {
cy.get('td').eq(i).contains(text)
})
})
})
})
it("Shows 'Did not graduate in time' when the student has graduated but not in time", () => {
cy.visit('/students/540355')
cy.contains('.divider h4', 'Bachelor Honours')
cy.contains('.red.tag.label', 'Not qualified for Honours')
cy.contains('.red.tag.label', 'Did not graduate in time')
})
it("Shows 'Module grades too low' when the student has graduated in time but has too low grades", () => {
cy.visit('/students/547934')
cy.contains('.divider h4', 'Bachelor Honours')
cy.contains('.red.tag.label', 'Not qualified for Honours')
cy.contains('.red.tag.label', 'Module grades too low')
})
it("Shows 'Might need further inspection' when the student has graduated in time but has more than four main modules", () => {
cy.visit('/students/478837')
cy.contains('.divider h4', 'Bachelor Honours')
cy.contains('.blue.tag.label', 'Might need further inspection')
})
it("Shows 'Has not graduated' when the student has not graduated", () => {
cy.visit(`students/${student.studentnumber}`)
cy.contains('.divider h4', 'Bachelor Honours')
cy.contains('.red.tag.label', 'Not qualified for Honours')
cy.contains('.red.tag.label', 'Has not graduated')
})
})
})
// Use admin to see all students
describe('when using admin user', () => {
beforeEach(() => {
cy.init('/students', 'admin')
})
it('Does not crash if student has no studyright or courses', () => {
typeStudentNumberAndClick('450730')
cy.contains('Credits: 0')
})
it("'Update student' button is shown", () => {
typeStudentNumberAndClick(student.studentnumber)
cy.get('div.ui.fluid.card').within(() => {
cy.contains('button', 'Update student')
})
})
it('Bachelor Honours section is not shown for students outside of Faculty of Science', () => {
cy.visit('/students/453146')
cy.contains('.divider h4', 'Bachelor Honours').should('not.exist')
})
it('When a study plan is selected, courses included in the study plan are highlighted with a blue background', () => {
cy.visit('/students/550789')
cy.contains('table tbody tr', 'Kulttuurien tutkimuksen kandiohjelma (01.08.2020–30.06.2023)').within(() => {
cy.get('td').eq(0).click()
})
cy.contains('table tbody tr', 'Kandidaatintutkielma (KUKA-LIS222)')
.should('have.attr', 'style')
.and('equal', 'background-color: rgb(232, 244, 255);')
})
it('When a study plan is selected, the time frame of the credit graph is updated', () => {
cy.clock(new Date('2024-08-30').getTime(), ['Date'])
cy.visit('/students/550789')
cy.contains('.highcharts-container text', '1 Aug 2020')
cy.contains('.highcharts-container text', '30 Aug 2024')
cy.contains('table tbody tr', 'Kulttuurien tutkimuksen kandiohjelma (01.08.2020–30.06.2023)').within(() => {
cy.get('td').eq(0).click()
})
cy.contains('.highcharts-container text', '1 Aug 2020')
cy.contains('.highcharts-container text', '19 Aug 2023')
})
it("If there's a study plan corresponding to the degree programme, completed credits are displayed", () => {
cy.visit('/students/550789')
cy.contains('table tbody tr', 'Kulttuurien tutkimuksen kandiohjelma (01.08.2020–30.06.2023)').within(() => {
cy.get('td')
.eq(-1)
.contains(/^185 cr$/)
})
})
it("If there's a study plan corresponding to the degree programme and the student hasn't graduated, percentage of completion is also displayed", () => {
cy.visit('/students/458723')
cy.contains('table tbody tr', 'Oikeusnotaarin koulutusohjelma (01.08.2020–31.07.2027)').within(() => {
cy.get('td').eq(-1).contains('69% (125 cr)')
})
})
})
})
+10 -9
View File
@@ -1,4 +1,4 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
describe('Study guidance group tests', () => {
describe('Study guidance group overview page', () => {
@@ -30,7 +30,7 @@ describe('Study guidance group tests', () => {
cy.contains('Study guidance groups')
const studyGuidanceGroupsTableContent = [
['TKT kandit 2018', 3, 'Tietojenkäsittelytieteen kandiohjelma', '2018 - 2019'],
['MAT kandit 2020', 3, 'Matemaattisten tieteiden kandiohjelma', '2020 - 2021'],
['Oma ohjausryhmä', 2, 'Add study programme', 'Add year'],
]
@@ -97,9 +97,9 @@ describe('Study guidance group tests', () => {
})
it('shows the correct labels', () => {
cy.get('.ui.medium.header').contains('TKT kandit 2018')
cy.get('.ui.blue.tag.label:first').contains('Tietojenkäsittelytieteen kandiohjelma')
cy.get('.ui.blue.tag.label:last').contains('2018 - 2019')
cy.get('.ui.medium.header').contains('MAT kandit 2020')
cy.get('.ui.blue.tag.label:first').contains('Matemaattisten tieteiden kandiohjelma')
cy.get('.ui.blue.tag.label:last').contains('2020 - 2021')
})
it('shows the correct panes', () => {
@@ -114,7 +114,7 @@ describe('Study guidance group tests', () => {
cy.get('[data-cy="CreditDate-filter-card"][data-open="false"]')
cy.get('.ui.primary.button').contains('Show starting from associated year').click()
cy.get('[data-cy="CreditDate-filter-card"][data-open="true"]')
cy.get('.ui.mini.icon.button.credit-date-filter-input').contains('01.08.2018')
cy.get('.ui.mini.icon.button.credit-date-filter-input').contains('01.08.2020')
cy.contains('Reset All Filters')
cy.get('.ui.primary.button').contains('Show all credits').click()
cy.get('[data-cy="CreditDate-filter-card"][data-open="false"]')
@@ -132,23 +132,24 @@ describe('Study guidance group tests', () => {
const firstRowHeadings = [
'Student number',
'Credits',
'Start of studyright',
'Start of study right',
'Started in programme',
'Semesters present',
'Graduation date',
'Start year at uni',
'Other programmes',
'Transferred from',
'Admission type',
'Gender',
'Citizenship',
'Curriculum period',
'Latest attainment date',
'Tags',
]
const secondRowHeadings = ['All', 'HOPS', 'Since 1.8.2018']
const secondRowHeadings = ['All', 'HOPS', 'Since 1.8.2020']
cy.cs('Students (3)').click()
cy.get('[data-cy="student-table-tabs"] table thead tr').then($tr => {
const firstRowTexts = Array.from($tr[0].children).map(elem => elem.innerText.replace('\n', ' '))
const firstRowTexts = Array.from($tr[0].children).map(elem => elem.innerText.replaceAll('\n', ' '))
expect(firstRowTexts).to.deep.equal(firstRowHeadings)
const secondRowTexts = Array.from($tr[1].children)
.filter(elem => elem.style.display !== 'none')
+548 -209
View File
@@ -1,4 +1,6 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const { getEmptyYears } = require('../support/commands')
const deleteTag = name => {
cy.contains('td', name).siblings().contains('Delete').click()
@@ -7,36 +9,21 @@ const deleteTag = name => {
cy.contains('td', name).should('not.exist')
}
const getEmptyYears = isAcademicYear => {
const today = new Date()
const latestYear = isAcademicYear && today.getMonth() < 7 ? today.getFullYear() - 1 : today.getFullYear()
const years = []
for (let year = latestYear; year > 2021; year--) {
if (isAcademicYear) {
years.push(`${year} - ${year + 1}`)
} else {
years.push(year)
}
}
return years
}
describe('Studyprogramme overview', () => {
describe('Study programme overview', () => {
/* Basic information overview -tests */
describe('Basic information -view works for basic user', () => {
describe('Basic information view works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
cy.contains('a', 'Tietojenkäsittelytieteen kandiohjelma').click({ force: true })
cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click({ force: true })
})
// If the backend breaks for one of the sections, the section header is not rendered and this will fail
it('Basic information -tab loads', () => {
cy.get('[data-cy=Section-StudentsOfTheStudyprogramme]')
cy.get('[data-cy=Section-CreditsProducedByTheStudyprogramme]')
cy.get('[data-cy=Section-GraduatedAndThesisWritersOfTheProgramme]')
cy.get('[data-cy=Section-ProgrammesBeforeOrAfter]')
cy.get('[data-cy=Section-AverageGraduationTimes]')
it('Basic information tab loads', () => {
cy.get('[data-cy=Section-studentsOfTheStudyProgramme]')
cy.get('[data-cy=Section-creditsProducedByTheStudyProgramme]')
cy.get('[data-cy=Section-graduatedAndThesisWritersOfTheProgramme]')
cy.get('[data-cy=Section-programmesBeforeOrAfter]')
cy.get('[data-cy=Section-averageGraduationTimes]')
})
it('Basic information contains correct students', () => {
@@ -44,28 +31,49 @@ describe('Studyprogramme overview', () => {
const tableContents = [
// [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],
[2023, 8, 8, 26, 0, 0],
[2022, 25, 26, 48, 1, 4],
[2021, 29, 32, 47, 0, 2],
[2020, 26, 27, 12, 1, 3],
[2019, 28, 34, 1, 0, 1],
[2018, 40, 45, 0, 0, 0],
[2017, 41, 47, 0, 0, 0],
]
cy.checkTableStats(tableContents, 'StudentsOfTheStudyprogramme')
cy.checkTableStats(tableContents, 'StudentsOfTheStudyProgramme')
})
it('Basic information contains correct credits', () => {
const years = getEmptyYears()
const tableContents = [
...years.map(year => [year, 0, 0]),
[2021, 0, 0, 0, 0, 0],
[2020, 4511, 4511, 0, 0, 406],
[2019, 7117, 7097, 0, 0, 434],
[2018, 5742, 5737, 0, 0, 150],
[2017, 1887, 1882, 0, 0, 186],
...years.map(year => [year, 0, 0, 0, 0, 0]),
[2023, 1519, 1519, 0, 0, 222],
[2022, 3235, 3205, 0, 30, 209],
[2021, 5133, 5108, 0, 25, 428],
[2020, 5801, 5796, 0, 5, 94],
[2019, 5305, 5305, 0, 0, 162],
[2018, 3442, 3432, 0, 10, 21],
[2017, 1211, 1211, 0, 0, 189],
]
cy.checkTableStats(tableContents, 'CreditsProducedByTheStudyprogramme')
cy.checkTableStats(tableContents, 'CreditsProducedByTheStudyProgramme')
})
it("Toggling 'Show special categories' on displays additional information", () => {
cy.contains('Show special categories').click()
const years = getEmptyYears()
const tableContents = [
...years.map(year => [year, 0, 0, 0, 0, 0, 0, 0]),
[2023, 1519, 1519, 0, 0, 0, 0, 222],
[2022, 3235, 3205, 0, 30, 0, 0, 209],
[2021, 5133, 5108, 0, 25, 0, 0, 428],
[2020, 5801, 5796, 0, 5, 0, 0, 94],
[2019, 5305, 5305, 0, 0, 0, 0, 162],
[2018, 3442, 3432, 0, 10, 0, 0, 21],
[2017, 1211, 1211, 0, 0, 0, 0, 189],
]
cy.checkTableStats(tableContents, 'CreditsProducedByTheStudyProgramme')
})
it('Basic information contains correct thesis writers and graduates', () => {
@@ -73,54 +81,48 @@ describe('Studyprogramme overview', () => {
const tableContents = [
// [Year, Graduated, Wrote thesis]
...years.map(year => [year, 0, 0]),
[2021, 0, 0],
[2020, 35, 35],
[2019, 17, 25],
[2018, 1, 5],
[2017, 0, 0],
[2023, 26, 0],
[2022, 48, 0],
[2021, 47, 66],
[2020, 12, 18],
[2019, 1, 15],
[2018, 0, 0],
[2017, 0, 1],
]
cy.checkTableStats(tableContents, 'GraduatedAndThesisWritersOfTheProgramme')
})
it('Special studyrights can be excluded and basic data changes accordingly', () => {
it('Special study rights can be excluded and basic data changes accordingly', () => {
cy.get('[data-cy=StudentToggle]').click()
const years = getEmptyYears()
const studentTableContents = [
// [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],
[2023, 8, 8, 24],
[2022, 25, 26, 44],
[2021, 29, 32, 46],
[2020, 26, 27, 11],
[2019, 28, 34, 1],
[2018, 40, 45, 0],
[2017, 41, 47, 0],
]
cy.checkTableStats(studentTableContents, 'StudentsOfTheStudyprogramme')
const creditTableContents = [
...years.map(year => [year, 0, 0]),
[2021, 0, 0, 0, 0, 0],
[2020, 4511, 4511, 0, 0, 406],
[2019, 7117, 7097, 0, 0, 434],
[2018, 5742, 5737, 0, 0, 150],
[2017, 1887, 1882, 0, 0, 186],
]
cy.checkTableStats(creditTableContents, 'CreditsProducedByTheStudyprogramme')
cy.checkTableStats(studentTableContents, 'StudentsOfTheStudyProgramme')
const graduatedTableContents = [
// [Year, Graduated, Wrote thesis]
...years.map(year => [year, 0, 0]),
[2021, 0, 0],
[2020, 35, 35],
[2019, 17, 25],
[2018, 1, 5],
[2017, 0, 0],
[2023, 24, 0],
[2022, 44, 0],
[2021, 46, 60],
[2020, 11, 17],
[2019, 1, 15],
[2018, 0, 0],
[2017, 0, 1],
]
cy.checkTableStats(graduatedTableContents, 'GraduatedAndThesisWritersOfTheProgramme')
cy.get('[data-cy=StudentToggle]').click()
})
it('Year can be changed to academic year, and data changes accordingly', () => {
@@ -130,125 +132,214 @@ describe('Studyprogramme overview', () => {
const studentTableContents = [
// [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],
['2023 - 2024', 8, 8, 4, 0, 0],
['2022 - 2023', 25, 26, 36, 0, 0],
['2021 - 2022', 29, 32, 57, 1, 6],
['2020 - 2021', 26, 27, 30, 0, 3],
['2019 - 2020', 28, 34, 7, 1, 1],
['2018 - 2019', 40, 45, 0, 0, 0],
['2017 - 2018', 41, 47, 0, 0, 0],
]
cy.checkTableStats(studentTableContents, 'StudentsOfTheStudyprogramme')
cy.checkTableStats(studentTableContents, 'StudentsOfTheStudyProgramme')
const creditTableContents = [
...years.map(year => [year, 0, 0]),
['2021 - 2022', 0, 0, 0, 0, 0],
['2020 - 2021', 352, 352, 0, 0, 296],
['2019 - 2020', 6966, 6966, 0, 0, 430],
['2018 - 2019', 7797, 7777, 0, 0, 259],
['2017 - 2018', 4142, 4132, 0, 0, 191],
...years.map(year => [year, 0, 0, 0, 0, 0]),
['2023 - 2024', 160, 160, 0, 0, 67],
['2022 - 2023', 2725, 2720, 0, 5, 337],
['2021 - 2022', 4092, 4042, 0, 50, 198],
['2020 - 2021', 5420, 5415, 0, 5, 321],
['2019 - 2020', 6043, 6043, 0, 0, 101],
['2018 - 2019', 4856, 4851, 0, 5, 107],
['2017 - 2018', 2350, 2345, 0, 5, 26],
]
cy.checkTableStats(creditTableContents, 'CreditsProducedByTheStudyprogramme')
cy.checkTableStats(creditTableContents, 'CreditsProducedByTheStudyProgramme')
cy.get('[data-cy=YearToggle]').click()
})
it('Basic information graphs render', () => {
cy.get('[data-cy=Graph-StudentsOfTheStudyprogramme')
cy.get('[data-cy=Graph-StudentsOfTheStudyProgramme')
.should('contain', 'Started studying')
.should('contain', 'Accepted')
.should('contain', 'Graduated')
.should('contain', 'Transferred away')
.should('contain', 'Transferred to')
cy.get('[data-cy=Graph-CreditsProducedByTheStudyprogramme')
cy.get('[data-cy=Graph-CreditsProducedByTheStudyProgramme')
.should('contain', 'Degree students')
.should('contain', 'Transferred')
.should('contain', 7097)
.should('contain', 434)
.should('contain', 5796)
.should('contain', 428)
cy.get('[data-cy=Graph-GraduatedAndThesisWritersOfTheProgramme')
.should('contain', 'Graduated students')
.should('contain', 'Wrote thesis')
.should('contain', 1)
.should('contain', 5)
.should('contain', 17)
.should('contain', 25)
.should('contain', 35)
.should('contain', 47)
.should('contain', 66)
cy.get('[data-cy=graduation-times-graph-breakdownBachelor]')
cy.get('[data-cy=GraduationTimeToggle]').click()
cy.get('[data-cy=graduation-times-graphBachelor]').within(() => {
cy.contains('Graduation year')
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.contains('2022')
cy.contains('48 graduated').trigger('mouseover')
cy.contains('48 students graduated in year 2022')
cy.contains('median study time: 44.5 months')
cy.contains('22 graduated on time')
cy.contains('12 graduated max year overtime')
cy.contains('14 graduated over year late')
})
cy.get('[data-cy=Graph-ProgrammesBeforeOrAfter')
.should('contain', 'Tietojenkäsittelytieteen maisteriohjelma')
.should('contain', 'Datatieteen maisteriohjelma')
.should('contain', 'Matematiikan ja tilastotieteen maisteriohjelma')
.should('contain', 11)
.should('contain', 12)
.should('contain', 24)
.should('contain', 32)
.should('contain', 21)
.should('contain', 17)
})
})
describe('Graduation times of master programmes', () => {
it('are split into two graphs', () => {
cy.init('/study-programme', 'admin')
cy.contains('a', 'Kasvatustieteiden maisteriohjelma').click({ force: true })
cy.init('/study-programme')
cy.contains('a', 'Matematiikan ja tilastotieteen maisteriohjelma').click({ force: true })
cy.get('[data-cy=graduation-times-graph-breakdownMaster]')
cy.get('[data-cy=graduation-times-graph-breakdownBachelor]')
cy.get('[data-cy=GraduationTimeToggle]').click()
cy.get('[data-cy=graduation-times-graphMaster]').within(() => {
cy.contains('Master study right')
cy.contains('Graduation year')
cy.contains('2020')
cy.contains('24 graduated').trigger('mouseover')
cy.contains('study time: 22.5 months')
cy.contains('19 graduated on time')
cy.contains('5 graduated max year overtime')
cy.contains('2021')
cy.contains('2 graduated').trigger('mouseover')
cy.contains('2 students graduated in year 2021')
cy.contains('median study time: 25 months')
cy.contains('1 graduated on time')
cy.contains('1 graduated max year overtime')
cy.contains('0 graduated over year late')
})
cy.get('[data-cy=graduation-times-graphBachelor]').within(() => {
cy.contains('Bachelor + master study right')
cy.contains('Graduation year')
cy.contains('2020')
cy.contains('0 graduated').trigger('mouseover')
cy.contains('study time: 0 months')
cy.contains('0 graduated on time')
cy.contains('2023')
cy.contains('11 graduated').trigger('mouseover')
cy.contains('11 students graduated in year 2023')
cy.contains('median study time: 69 months')
cy.contains('3 graduated on time')
cy.contains('7 graduated max year overtime')
cy.contains('1 graduated over year late')
})
})
})
/* Studytrack overview -tests */
describe('Studytrack overview works for basic user', () => {
/* Study track overview tests */
describe('Study track overview works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
cy.contains('a', 'Tietojenkäsittelytieteen kandiohjelma').click()
cy.get('.attached').contains('Studytracks and class statistics').click()
cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click()
cy.get('.attached').contains('Study tracks and class statistics').click()
})
// If the backend breaks for one of the sections, the section header is not rendered and this will fail
it('Studytracks and class statistics -tab loads', () => {
cy.get('[data-cy=Section-StudytrackOverview]')
cy.get('[data-cy=Section-StudytrackProgress]')
cy.get('[data-cy=Section-AverageGraduationTimesStudytracks]')
it('Study tracks and class statistics -tab loads', () => {
cy.get('[data-cy=Section-studyTrackOverview]')
cy.get('[data-cy=Section-studyTrackProgress]')
cy.get('[data-cy=Section-averageGraduationTimesStudyTracks]')
})
it('Students of the studyprogramme are shown correctly', () => {
it('Students of the study programme are shown correctly', () => {
const tableContents = [
// [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, 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],
['2023 - 2024', 8, 8, 0, 0, 8, 0, 5, 3, 0, 8, 0],
['2022 - 2023', 26, 25, 0, 0, 24, 2, 19, 7, 0, 25, 1],
['2021 - 2022', 38, 29, 0, 0, 33, 5, 29, 9, 0, 35, 3],
['2020 - 2021', 30, 26, 0, 0, 11, 19, 15, 15, 0, 29, 1],
['2019 - 2020', 35, 28, 0, 0, 8, 27, 22, 13, 0, 34, 1],
['2018 - 2019', 45, 40, 0, 0, 6, 39, 26, 19, 0, 44, 1],
['2017 - 2018', 47, 41, 0, 0, 5, 42, 31, 16, 0, 47, 0],
['Total', 229, 197, 0, 0, 95, 134, 147, 82, 0, 222, 7],
]
cy.checkTableStats(tableContents, 'StudytrackOverview')
cy.checkTableStats(tableContents, 'StudyTrackOverview')
})
it('Years in the students table can be expanded and study track data will be shown', () => {
cy.get('[data-cy=Table-StudyTrackOverview]').within(() => {
cy.get('tbody tr.header-row')
.eq(3)
.within(() => {
cy.get('td i.angle.right.icon').click()
})
const dataForStudyTracks = [
['Ekonometria, MAT-EKO', 2, 2, 0, 0, 0, 2, 1, 1, 0, 2, 0],
['Matematiikka, MAT-MAT', 13, 10, 0, 0, 1, 12, 7, 6, 0, 12, 1],
['Tietojenkäsittelyteoria, MAT-TIE', 2, 1, 0, 0, 1, 1, 2, 0, 0, 2, 0],
['Tilastotiede, MAT-TIL', 4, 4, 0, 0, 0, 4, 1, 3, 0, 4, 0],
]
dataForStudyTracks.forEach((data, index) => {
cy.get('tbody tr.regular-row')
.eq(index)
.within(() => {
cy.get('td').each((cell, i) => {
cy.wrap(cell).contains(data[i])
})
})
})
cy.get('tbody tr.header-row')
.eq(3)
.within(() => {
cy.get('td i.angle.down.icon').click()
})
cy.get('tbody tr.regular-row').should('not.exist')
})
})
it('Links to class statistics page work', () => {
cy.get('[data-cy=Table-StudyTrackOverview]').within(() => {
cy.get('tbody tr.header-row')
.eq(1)
.within(() => {
cy.get('td i.level.up.alternate.icon').click()
})
})
cy.contains('Matemaattisten tieteiden kandiohjelma 2022 - 2023')
cy.contains('class size 26 students')
})
it('Links to class statistics page with all years combined work', { retries: 2 }, () => {
cy.get('[data-cy=Table-StudyTrackOverview]').within(() => {
cy.get('td.total-row-cell a').click()
})
cy.contains('Matemaattisten tieteiden kandiohjelma')
cy.contains('class size 227 students')
})
it('Links to class statistics page with study track info included work', () => {
cy.get('[data-cy=Table-StudyTrackOverview]').within(() => {
cy.get('tbody tr.header-row')
.eq(3)
.within(() => {
cy.get('td i.angle.right.icon').click()
})
})
cy.contains('td', 'Matematiikka, MAT-MAT').within(() => {
cy.get('a').click()
})
cy.contains('Matemaattisten tieteiden kandiohjelma 2020 - 2021')
cy.contains('div.sub.header', 'studytrack MAT-MAT')
cy.contains('class size 30 students')
cy.contains('10 students out of 30 shown')
})
it('Student progress data is shown correctly', () => {
@@ -256,33 +347,124 @@ describe('Studyprogramme overview', () => {
const tableContents = [
// [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, 28, 47, 35, 26, 12, 4],
['2017 - 2018', 171, 20, 21, 26, 22, 22, 33, 27],
['Total', 431, 68, 83, 94, 62, 48, 45, 31],
['2023 - 2024', 8, 8, 0, 0, 0, 0, 0, 0],
['2022 - 2023', 26, 9, 9, 4, 3, 0, 1, 0],
['2021 - 2022', 38, 8, 6, 11, 8, 5, 0, 0],
['2020 - 2021', 30, 2, 1, 3, 7, 4, 4, 9],
['2019 - 2020', 35, 1, 0, 2, 1, 0, 4, 27],
['2018 - 2019', 45, 0, 1, 1, 2, 0, 3, 38],
['2017 - 2018', 47, 0, 1, 3, 0, 1, 2, 40],
['Total', 229, 28, 18, 24, 21, 10, 14, 114],
]
cy.checkTableStats(tableContents, 'StudytrackProgress')
cy.checkTableStats(tableContents, 'StudyTrackProgress')
})
it('Studytrack overview graphs render', () => {
cy.get('[data-cy=Graph-StudytrackProgress]')
it('Study track overview graphs render', () => {
cy.get('[data-cy=Graph-StudyTrackProgress]')
.should('contain', 'Less than 30 credits')
.should('contain', '30–60 credits')
.should('contain', 'At least 180 credits')
.should('contain', '100.0%') // The percentage for less than 15 credits in 2017-2018, to check that the graph renders
.should('contain', '49.8%') // The percentage for total, at least 180 credits, to check that the graph renders
cy.get('[data-cy=Graph-StudyTrackProgress]').contains('49.8%').trigger('mouseover', { force: true })
cy.contains('At least 180 credits: 114')
cy.get("[data-cy='Section-KH50_001']").within(() => {
cy.contains('Start year')
cy.contains('2020 - 2021')
cy.get('[aria-label="2020 - 2021, 15. On time."]').trigger('mouseover')
cy.contains('On time: 15')
cy.contains("Click a bar to view that year's study track level breakdown")
cy.get('[aria-label="2019 - 2020, 22. On time."]').click()
cy.contains("Click a bar to view that year's study track level breakdown").should('not.exist')
cy.contains('Year 2019 - 2020 by start year')
cy.get('[aria-label="MAT-MAT, 14. On time."]').trigger('mouseover')
cy.contains('Matematiikka')
cy.contains('MAT-MAT')
cy.contains('On time: 14')
})
cy.get('[data-cy=graduation-times-graph-breakdownBachelor]')
cy.get('[data-cy=GraduationTimeToggle]').click()
cy.get('[data-cy=graduation-times-graphBachelor]').within(() => {
cy.get("[data-cy='Section-KH50_001']").within(() => {
cy.contains('Start year')
cy.contains('2017 - 2018')
cy.contains('41 graduated').trigger('mouseover')
cy.contains('study time: 31 months')
cy.contains('36 graduated on time')
cy.contains('2020 - 2021')
cy.contains('19 graduated').trigger('mouseover')
cy.contains('From class of 2020 - 2021, 19/30 students have graduated')
cy.contains('median study time: 33 months')
cy.contains('15 graduated on time')
cy.contains('3 graduated max year overtime')
cy.contains('1 graduated over year late')
})
})
describe('Study track can be changed', () => {
beforeEach(() => {
cy.get('.studytrack-selector').contains('All students of the programme, KH50_001').click()
cy.get('.studytrack-selector .visible.menu').contains('Matematiikka, MAT-MAT').click()
})
it('Students of the study track are shown correctly', () => {
cy.get("[data-cy='Section-studyTrackOverview']").contains(
'Students of the study track MAT-MAT by starting year'
)
const tableContents = [
// [Year, All, Started studying, Present, Absent, Inactive, Graduated, Men, Women, Other/Unknown, Finland, Other]
['2022 - 2023', 3, 3, 0, 0, 1, 2, 2, 1, 0, 3, 0],
['2021 - 2022', 5, 1, 0, 0, 1, 4, 3, 2, 0, 5, 0],
['2020 - 2021', 13, 10, 0, 0, 1, 12, 7, 6, 0, 12, 1],
['2019 - 2020', 17, 14, 0, 0, 0, 17, 10, 7, 0, 16, 1],
['2018 - 2019', 23, 21, 0, 0, 2, 21, 11, 12, 0, 23, 0],
['2017 - 2018', 28, 24, 0, 0, 1, 27, 15, 13, 0, 28, 0],
['Total', 89, 73, 0, 0, 6, 83, 48, 41, 0, 87, 2],
]
cy.checkTableStats(tableContents, 'StudyTrackOverview')
})
it('Links to class statistics page with study track info included work', () => {
cy.get('[data-cy=Table-StudyTrackOverview]').within(() => {
cy.get('tbody tr.regular-row')
.eq(2)
.within(() => {
cy.get('a').click()
})
})
cy.contains('Matemaattisten tieteiden kandiohjelma 2020 - 2021')
cy.contains('div.sub.header', 'studytrack MAT-MAT')
cy.contains('class size 30 students')
cy.contains('10 students out of 30 shown')
})
it('Info message about missing progress stats is displayed', () => {
cy.contains('.divider', 'Progress of students of the study track MAT-MAT by starting year')
cy.contains(
'.message',
'Currently progress data is only available for all students of the study programme. Please select ”All students of the programme” to view the progress data.'
)
})
it('Average graduation times are displayed correctly', () => {
cy.get("[data-cy='Section-averageGraduationTimesStudyTracks']")
cy.get("[data-cy='graduation-times-graph-breakdownBachelor']").within(() => {
cy.contains('Start year')
cy.contains('2020 - 2021')
cy.get('[aria-label="2020 - 2021, 9. On time."]').trigger('mouseover')
cy.contains('Graduated On time: 9 students')
})
cy.get('[data-cy=GraduationTimeToggle]').click()
cy.get("[data-cy='graduation-times-graphBachelor']").within(() => {
cy.contains('Start year')
cy.contains('2020 - 2021')
cy.contains('12 graduated').trigger('mouseover')
cy.contains('From class of 2020 - 2021, 12/13 students have graduated')
cy.contains('median study time: 34 months')
cy.contains('9 graduated on time')
cy.contains('2 graduated max year overtime')
cy.contains('1 graduated over year late')
})
})
})
})
@@ -291,7 +473,7 @@ describe('Studyprogramme overview', () => {
describe('Programme courses works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
cy.contains('a', 'Tietojenkäsittelytieteen kandiohjelma').click()
cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click()
cy.get('.attached').contains('Programme courses').click()
})
@@ -301,83 +483,223 @@ describe('Studyprogramme overview', () => {
})
it('time range selection works', () => {
cy.get('[data-cy=CoursesSortableTable] tbody').within(() => {
cy.get('tr')
.eq(0)
.within(() => {
cy.get('td').eq(0).contains('MAT11008')
cy.get('td').eq(1).contains('Advanced calculus')
cy.get('td').eq(2).contains('75')
})
})
cy.get('[data-cy=fromYear]').click().contains('2018').click()
cy.get('[data-cy=toYear').click().contains('2019').click()
cy.get('[data-cy=CoursesSortableTable]').within(() => {
cy.get('tr').eq(1).contains('684')
cy.get('[data-cy=CoursesSortableTable] tbody').within(() => {
cy.get('tr')
.eq(0)
.within(() => {
cy.get('td').eq(0).contains('MAT11008')
cy.get('td').eq(1).contains('Advanced calculus')
cy.get('td').eq(2).contains('15')
})
})
})
it('calendar year -> academic year toggle works', () => {
cy.get('[data-cy=fromYear]').click().contains('2018').click()
cy.get('[data-cy=toYear').click().contains('2019').click()
cy.get('[data-cy=CoursesSortableTable]').within(() => {
cy.get('tr').eq(1).contains('684')
})
cy.get('[data-cy=calendarAcademicYearToggle]').first().click()
cy.get('[data-cy=fromYear]').click().contains('2018-2019').click()
cy.get('[data-cy=toYear').click().contains('2019-2020').click()
cy.get('[data-cy=CoursesSortableTable]').within(() => {
cy.get('tr').eq(1).contains('772')
it("'Calendar year/Academic year' toggle works", () => {
cy.get('[data-cy=calendarAcademicYearToggle]').click()
cy.get('[data-cy=fromYear]').contains('2017-2018')
cy.get('[data-cy=toYear').contains('2023-2024')
cy.get('[data-cy=CoursesSortableTable] tbody').within(() => {
cy.get('tr')
.eq(0)
.within(() => {
cy.get('td').eq(0).contains('MAT11008')
cy.get('td').eq(1).contains('Advanced calculus')
cy.get('td').eq(2).contains('75')
})
})
})
it('contains correct courses in alphabetical order', () => {
cy.get('[data-cy=CoursesSortableTable]').within(() => {
cy.get('tr').eq(1).contains('Aineopintojen harjoitustyö: Tietokantasovellus')
cy.get('tr').eq(-1).contains('Äidinkielinen viestintä')
cy.get('tr').eq(1).contains('Advanced calculus')
cy.get('tr').eq(-1).contains('Äidinkielen opinnot')
})
})
it('different sorting options work', () => {
cy.get('[data-cy=CoursesSortableTable]').within(() => {
// Test sorting by different columns
// Course code
cy.get('th').eq(0).click()
cy.get('tr').eq(2).contains('Tietojenkäsittelytieteen kisälliopetus')
cy.get('tr')
.eq(1)
.within(() => {
cy.get('td').eq(0).contains('odgi-zef0')
cy.get('td').eq(1).contains('Työ- ja organisaatiopsykologian perusopinnot')
})
cy.get('th').eq(0).click()
cy.get('tr')
.eq(1)
.within(() => {
cy.get('td').eq(0).contains('MAT-yht')
cy.get('td').eq(1).contains('Muut opinnot')
})
// Course name
cy.get('th').eq(1).click()
cy.get('tr').eq(1).contains('Äidinkielinen viestintä')
cy.get('tr')
.eq(1)
.within(() => {
cy.get('td').eq(0).contains('MAT20003')
cy.get('td').eq(1).contains('Äidinkielen opinnot')
})
cy.get('th').eq(1).click()
cy.get('tr')
.eq(1)
.within(() => {
cy.get('td').eq(0).contains('MAT11008')
cy.get('td').eq(1).contains('Advanced calculus')
})
// Total credits
cy.get('th').eq(2).click()
cy.get('tr').eq(1).contains('Tietorakenteet ja algoritmit')
cy.get('tr')
.eq(1)
.within(() => {
cy.get('td').eq(0).contains('MAT110')
cy.get('td').eq(1).contains('Matematiikka, perusopinnot')
})
cy.get('th').eq(2).click()
cy.get('tr')
.eq(1)
.within(() => {
cy.get('td').eq(0).contains('MAT20001')
cy.get('td').eq(1).contains('Kypsyysnäyte')
})
})
})
it('show credits -> show students toggle works', () => {
cy.get('[data-cy=CoursesSortableTable]').within(() => {
cy.get('tr').eq(1).contains('1068')
it("'Show credits/Show students' toggle works", () => {
cy.get('[data-cy=CoursesSortableTable] thead tr').within(() => {
cy.get('th').should('have.length', 8)
const headers = [
'Code',
'Name',
'Total credits',
'Major credits',
'Non-major credits',
'Non-degree credits',
'Transferred credits',
'Type',
]
headers.forEach((header, index) => {
cy.get('th').eq(index).contains(header)
})
})
cy.get('[data-cy=creditsStudentsToggle]').first().click()
cy.get('[data-cy=CoursesSortableTable] tbody').within(() => {
cy.get('tr')
.eq(0)
.within(() => {
cy.get('td').eq(0).contains('MAT11008')
cy.get('td').eq(1).contains('Advanced calculus')
cy.get('td').eq(2).contains('75')
})
})
cy.get('[data-cy=CoursesSortableTable]').within(() => {
cy.get('tr').eq(2).contains('267')
cy.get('[data-cy=creditsStudentsToggle]').click()
cy.get('[data-cy=CoursesSortableTable] thead tr')
.eq(0)
.within(() => {
cy.get('th').should('have.length', 7)
const headers = [
'Code',
'Name',
'Total',
'Breakdown of total',
'Breakdown of passed students',
'Not included in total nor passed',
'Type',
]
headers.forEach((header, index) => {
cy.get('th').eq(index).contains(header)
})
})
cy.get('[data-cy=CoursesSortableTable] thead tr')
.eq(1)
.within(() => {
cy.get('th').should('have.length', 6)
const headers = [
'Passed',
'Not completed',
'Major students',
'Non-major students',
'Non-degree students',
'Transferred students',
]
headers.forEach((header, index) => {
cy.get('th').eq(index).contains(header)
})
})
cy.get('[data-cy=CoursesSortableTable] tbody').within(() => {
cy.get('tr')
.eq(0)
.within(() => {
cy.get('td').eq(0).contains('MAT11008')
cy.get('td').eq(1).contains('Advanced calculus')
cy.get('td').eq(2).contains('23')
})
})
})
})
describe('Degree courses works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click()
cy.get('.attached').contains('Degree courses').click()
})
it('content loads', () => {
cy.contains('h3', 'Select curriculum to edit:')
cy.get('[data-cy=curriculum-picker]').contains('2023–2026')
cy.contains('Change visibility of degree courses and select criteria for academic years')
cy.contains('form', 'First year (12 months) last set: 0')
cy.get('table').within(() => {
cy.contains('Muut opinnot')
cy.contains('Matematiikka, perusopinnot')
cy.contains('div.green.label', 'visible')
cy.contains('button', 'Set hidden')
})
})
})
/* Tag-tests */
describe('Basic information -view works for basic user', () => {
describe('Tags view works for basic user', () => {
beforeEach(() => {
cy.init('/study-programme')
cy.contains('a', 'Tietojenkäsittelytieteen kandiohjelma').click()
cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click({ force: true })
cy.get('.attached').contains('Tags').click()
})
it('can create and delete tags for population', () => {
const name = `tag-${new Date().getTime()}`
cy.get('.tagNameSelectInput > .ui > input').type(name)
cy.get('.yearSelectInput').type('2018')
cy.get('.yearSelectInput').type('2022')
cy.contains('Create a new tag').click()
cy.contains(name)
cy.contains('2018')
cy.contains('2022')
deleteTag(name)
})
it('can create personal tags', () => {
const name = `tag-${new Date().getTime()}`
cy.get('.tagNameSelectInput > .ui > input').type(name)
cy.get('.yearSelectInput').type('2018')
cy.get('.yearSelectInput').type('2022')
cy.get('.ui > label').click()
cy.contains('Create a new tag').click()
@@ -385,53 +707,66 @@ describe('Studyprogramme overview', () => {
deleteTag(name)
})
it('can add tags to students', () => {
describe('Adding tags to students and removing them works', () => {
const name = `tag-${new Date().getTime()}`
const studentInput = '477806,478275; 478953 479239\n 480080'
const studentNumbers = studentInput.match(/[^\s,;]+/g)
const student = '010113437'
cy.get('.tagNameSelectInput > .ui > input').type(name)
cy.get('.yearSelectInput').type('2018')
cy.contains('Create a new tag').click()
cy.contains(name)
it('can add tags to students', () => {
cy.get('.tagNameSelectInput > .ui > input').type(name)
cy.get('.yearSelectInput').type('2022')
cy.contains('Create a new tag').click()
cy.contains(name)
cy.contains('Add a tag to students').click()
cy.get('.form > .field > .dropdown').click().get('.ui > input.search').type(name).click()
cy.contains('Add a tag to students').click()
cy.get('.form > .field > .dropdown').click().get('.ui > input.search').type(name).click()
cy.get('.form > .field > .dropdown > .visible').contains(name).click()
cy.get('.form > .field > .dropdown > .visible').contains(name).click()
cy.get('textarea').type('010113437')
cy.contains('Add tags').click()
cy.get('textarea').type(studentInput)
cy.contains('Add tags').click()
cy.contains('Successfully added tags to students.')
cy.contains('Successfully added tags to students.')
cy.contains('Students').click()
cy.get('.prompt').type(student)
cy.contains('a', student).click()
cy.contains(name)
cy.contains('td', name).get('i.level.up.alternate.icon').click()
cy.go('back')
cy.go('back')
deleteTag(name)
cy.contains('Matemaattisten tieteiden kandiohjelma 2022 - 2023')
cy.contains(`Tagged with: ${name}`)
cy.contains('Students (5)')
.parent()
.then($parentDiv => {
if (!$parentDiv.hasClass('active')) cy.contains('Students (5)').click()
})
cy.contains('Students').click()
cy.get('.prompt').type(student)
cy.contains('a', student).click()
cy.contains(name).should('not.exist')
for (const studentNumber of studentNumbers) {
cy.contains(studentNumber)
}
cy.go('back')
deleteTag(name)
})
it('deleting a tag from tag view also removes it from students', () => {
cy.contains(name).should('not.exist')
for (const studentNumber of studentNumbers) {
cy.contains('Students').click()
cy.get('.prompt').type(studentNumber)
cy.contains('a', studentNumber).click()
cy.contains(name).should('not.exist')
}
})
})
})
describe('IAM user', () => {
beforeEach(() => {
cy.init('/study-programme', 'onlyiamrights')
cy.contains('a', 'Tietojenkäsittelytieteen kandiohjelma').dblclick()
cy.contains('a', 'Matemaattisten tieteiden kandiohjelma').click()
})
it('can access programme and correct tabs are visible', () => {
cy.reload()
cy.contains('Basic information')
cy.contains('Studytracks and class statistics')
cy.contains('Study tracks and class statistics')
cy.contains('Update statistics').should('not.exist')
cy.contains('Degree courses').should('not.exist')
@@ -440,19 +775,23 @@ describe('Studyprogramme overview', () => {
it('can access basic information', () => {
cy.contains('Basic information').click()
cy.get('[data-cy=Section-StudentsOfTheStudyprogramme]')
cy.get('[data-cy=Section-CreditsProducedByTheStudyprogramme]')
cy.get('[data-cy=Section-GraduatedAndThesisWritersOfTheProgramme]')
cy.get('[data-cy=Section-ProgrammesBeforeOrAfter]')
cy.get('[data-cy=Section-AverageGraduationTimes]')
cy.get('[data-cy=Section-studentsOfTheStudyProgramme]')
cy.get('[data-cy=Section-creditsProducedByTheStudyProgramme]')
cy.get('[data-cy=Section-graduatedAndThesisWritersOfTheProgramme]')
cy.get('[data-cy=Section-programmesBeforeOrAfter]')
cy.get('[data-cy=Section-averageGraduationTimes]')
})
it('can access studytracks', () => {
cy.get('.attached').contains('Studytracks and class statistics').click()
it('can access study tracks', () => {
cy.get('.attached').contains('Study tracks and class statistics').click()
cy.get('[data-cy=Section-StudytrackOverview]')
cy.get('[data-cy=Section-StudytrackProgress]')
cy.get('[data-cy=Section-AverageGraduationTimesStudytracks]')
cy.get('[data-cy=Section-studyTrackOverview]')
cy.get('[data-cy=Section-studyTrackProgress]')
cy.get('[data-cy=Section-averageGraduationTimesStudyTracks]')
})
it("doesn't see other tabs", () => {
cy.get('div.ui.tabular.menu a').should('have.length', 2)
})
})
})
+35 -21
View File
@@ -1,50 +1,64 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
describe('Teachers page tests', () => {
beforeEach(() => {
// login as admin = has teacher rights
cy.init('/teachers', 'admin')
cy.url().should('include', '/teachers')
})
const teacher1 = 'Landgraf Leo'
const teacher2 = 'Vinogradov Jussi Petteri'
const teacher1 = 'Luokkanen Liisa Viljami'
const teacher2 = 'Perälä Juhani Susanna'
it('Check Statistics', () => {
cy.get(':nth-child(1) > .ui > .search').click()
cy.contains('Syksy 2020').click()
cy.cs('course-providers').click()
cy.contains('Tietojenkäsittelytieteen kandiohjelma').click()
cy.contains('Matemaattisten tieteiden kandiohjelma').click()
cy.cs('course-providers').children('.icon').click()
cy.get('.form > .fluid').click()
cy.contains('button', 'Search').click()
cy.contains('Teacher')
cy.contains(teacher1)
cy.contains(teacher2)
const headers = ['Name', 'Credits', 'Credits transferred', 'Passed']
cy.get('table thead tr th').should('have.length', 4)
headers.forEach((header, index) => {
cy.get('table thead tr th').eq(index).should('contain', header)
})
cy.contains('td', teacher1).siblings().eq(0).contains('235')
cy.contains('td', teacher1).siblings().eq(2).contains('97.40%')
cy.contains('td', teacher2).siblings().eq(0).contains('395')
cy.contains('td', teacher2).siblings().eq(2).contains('98.78%')
})
it('Teacher search works', () => {
cy.url().should('include', '/teachers')
cy.get('.borderless > :nth-child(3)').click()
cy.get('.prompt').type(teacher1.split()[0])
cy.contains('td', teacher1)
cy.get('.prompt').type(teacher1.split(' ')[0])
cy.get('table tbody tr').should('have.length', 4)
cy.get('table tbody tr').eq(2).contains('td', teacher1)
})
it('Can check teacher page', () => {
cy.visit('/teachers/hy-hlo-51367956')
cy.contains('Landgraf Leo')
cy.get('.borderless > :nth-child(3)').click()
cy.get('.prompt').type(teacher2)
// Prevent opening in new tab
cy.contains('a', teacher2).invoke('removeAttr', 'target').click()
cy.url().should('include', '/teachers/hy-hlo-49026530')
cy.contains(teacher2)
cy.contains('Syksy 2023').click()
cy.contains('Kevät 2019').click()
cy.get('.ok-sortable-table tbody tr')
.first()
.eq(0)
.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%')
const rowContent = ['MAT12004', 'Tilastollinen päättely I', '120', '0', '92.31%']
rowContent.forEach((content, index) => {
cy.get('td').eq(index).contains(content)
})
})
})
it('Check leaderboad works', () => {
cy.get('.borderless > :nth-child(2)').click({ force: true })
cy.get(':nth-child(1) > .ui > .search').click({ force: true })
cy.contains('2020-2021').click({ force: true })
cy.get('.borderless > :nth-child(2)').click()
cy.get(':nth-child(1) > .ui > .search').click()
cy.contains('2020-2021').click()
})
})
+96
View File
@@ -0,0 +1,96 @@
/// <reference types="cypress" />
const progressLevels = ['Bachelors', 'BachelorMasters', 'Masters', 'Doctoral']
const checkProgressBarCharts = () => {
progressLevels.forEach(level => cy.get(`[data-cy=Faculty${level}ProgressBarChart]`))
}
const checkProgressTables = () => {
progressLevels.forEach(level => cy.get(`[data-cy=Faculty${level}ProgressTable]`))
}
const graduationTimesLevels = ['bachelor', 'master', 'doctor'] // ? Bachelor + master seems to be missing in test data
const checkAverageGraduationTimesBreakdownBarCharts = () => {
graduationTimesLevels.forEach(level => {
cy.get(`[data-cy=${level}BreakdownBarChart]`)
})
}
const checkAverageGraduationTimesMedianBarCharts = () => {
graduationTimesLevels.forEach(level => {
cy.get(`[data-cy=${level}MedianBarChart]`)
})
}
describe('University view', () => {
beforeEach(() => {
cy.init('/university')
cy.contains('University')
cy.contains('Programme MH90_001 (Veterinary medicine bachelor + licentiate) is currently excluded.')
})
describe('Faculty progress tab', () => {
it('contains all the correct progress bar charts', () => {
checkProgressBarCharts()
})
it('contains all the correct progress tables', () => {
checkProgressTables()
})
it("'Graduated included / Graduated excluded' toggle works", () => {
cy.get('[data-cy=GraduatedToggle]').click()
checkProgressBarCharts()
checkProgressTables()
})
it("'All study rights / Special study rights excluded' toggle works", () => {
cy.get('[data-cy=StudyRightToggle]').click()
checkProgressBarCharts()
checkProgressTables()
})
it('years in the tables can be clicked to show faculty level breakdown', () => {
cy.get('[data-cy=StudyRightToggle]').click()
cy.get('[data-cy=FacultyBachelorsProgressTableShowButton3]').click()
cy.contains('29.5%').trigger('mouseover', { force: true })
cy.contains('Matemaattis-luonnontieteellinen tiedekunta')
cy.contains('H50')
cy.contains('0 Credits: 0')
cy.contains('1 ≤ Credits < 45: 13')
cy.contains('45 ≤ Credits < 90: 10')
cy.contains('90 ≤ Credits < 135: 16')
cy.contains('135 ≤ Credits < 180: 3')
cy.contains('180 ≤ Credits: 2')
})
it('info boxes contain correct information', () => {
cy.get('[data-cy=FacultyProgressInfoBoxButton]').click()
cy.get('[data-cy=FacultyProgressInfoBoxContent]').contains('Kuvaa tiedekuntaan kuuluvien')
cy.get('[data-cy=FacultyBachelorMastersProgressInfoBoxButton]').click()
cy.get('[data-cy=FacultyBachelorMastersProgressInfoBoxContent]').contains('The starting year is the')
})
})
describe('Faculty graduations tab', () => {
beforeEach(() => {
cy.get('[data-cy=FacultyGraduationsTab]').click()
})
it('has all the correct median time bar charts', () => {
checkAverageGraduationTimesBreakdownBarCharts()
})
it("'Breakdown/Median times' toggle works", () => {
cy.get('[data-cy=GraduationTimeToggle]').click()
checkAverageGraduationTimesMedianBarCharts()
})
it('info boxes contain correct information', () => {
cy.get('[data-cy=AverageGraduationTimesInfoBoxButton]').click()
cy.get('[data-cy=AverageGraduationTimesInfoBoxContent]').contains('Opiskelijoiden keskimääräiset valmistumisajat')
})
})
})
+78 -37
View File
@@ -1,27 +1,62 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
const visibleLinks = {
norights: ['University', 'Faculties', 'Special populations', 'Feedback'],
onlycoursestatistics: ['University', 'Courses', 'Special populations', 'Feedback'],
}
visibleLinks.basic = [...visibleLinks.onlycoursestatistics, 'Faculties', 'Programmes', 'Students']
visibleLinks.admin = [...visibleLinks.basic, 'Teachers', 'Admin']
const hasVisibleItems = visibleItems => {
cy.get('[data-cy=nav-bar]').children().should('have.length', visibleItems)
}
const containsLinks = links => {
cy.get('[data-cy=nav-bar]').within(() => {
for (const link of links) {
cy.contains(link)
}
})
}
const userButtonWorks = (username, mocking = false) => {
cy.get('[data-cy=nav-bar-user-button]').click()
cy.contains(mocking ? `Mocking as ${username}` : `Logged in as ${username}`)
cy.contains('Language')
cy.contains('suomi')
cy.contains('English')
cy.contains('svenska')
cy.contains(mocking ? 'Stop mocking' : 'Log out')
}
describe('Users tests', () => {
describe('Using user with just grp-oodikone-user, no other rights', () => {
it('shows only frontpage, university, feedback', () => {
cy.init('', 'norights') // login with norights user
cy.get('[data-cy="navBar"]').contains('University').should('exist')
cy.get('[data-cy="navBar"]').contains('Give feedback').should('exist')
cy.get('[data-cy="navBar"]').contains('Programmes').should('not.exist')
cy.get('[data-cy="navBar"]').contains('Students').should('not.exist')
cy.get('[data-cy="navBar"]').contains('Courses').should('not.exist')
cy.get('[data-cy="navBar"]').contains('Teachers').should('not.exist')
cy.get('[data-cy="navBar"]').contains('Users').should('not.exist')
cy.get('[data-cy="navBar"]').contains('Faculty').should('not.exist')
cy.get('[data-cy="navBar"]').contains('Updater').should('not.exist')
describe('Using as user with just grp-oodikone-user, no other rights', () => {
it('shows correct tabs', () => {
cy.init('', 'norights')
hasVisibleItems(7)
containsLinks(visibleLinks.norights)
userButtonWorks('norights')
})
})
// TODO: Add user right checks for onlycoursestats user (should be like above + should
// show course stats
//
// TODO: Add user right checks for basic user (should show also study programme,
// student stats, course stats
describe('Using as coursestatistics user', () => {
it('shows correct tabs', () => {
cy.init('', 'onlycoursestatistics')
hasVisibleItems(7)
containsLinks(visibleLinks.onlycoursestatistics)
userButtonWorks('onlycoursestatistics')
})
})
describe('Using as basic user', () => {
it('shows correct tabs', () => {
cy.init('')
hasVisibleItems(10)
containsLinks(visibleLinks.basic)
userButtonWorks('basic')
})
})
describe('Using as admin', () => {
beforeEach(() => {
@@ -29,26 +64,32 @@ describe('Users tests', () => {
})
it('should see more stuff than others', () => {
cy.get('[data-cy="navBar"]').contains('Programmes').should('exist')
cy.get('[data-cy="navBar"]').contains('Students').should('exist')
cy.get('[data-cy="navBar"]').contains('Courses').should('exist')
// TODO: admin should probably see this as well, since can access this page: cy.get('[data-cy="navBar"]').contains('Teachers').should('exist')
cy.get('[data-cy="navBar"]').contains('Users').should('exist')
cy.get('[data-cy="navBar"]').contains('University').should('exist')
hasVisibleItems(13)
containsLinks(visibleLinks.admin)
userButtonWorks('admin')
})
it("mocking normal user shows only the mocked user's programmes", () => {
cy.contains('mocking').should('not.exist')
cy.cs('user-edit-button-basic').click()
cy.get('i.spy').click()
cy.contains('mocking as basic')
cy.contains('Programmes').click().siblings().contains('Class statistics').click()
cy.contains('label', 'Study programme')
cy.contains('label', 'Study programme')
.siblings()
.within(() => {
cy.get("div[role='option']").should('have.length', 2).contains('Tietojenkäsittelytieteen kandiohjelma')
})
describe('can mock as other users', () => {
beforeEach(() => {
cy.cs('user-edit-button-basic').click()
cy.get('i.spy').click()
})
it('user button shows mocked user', () => {
userButtonWorks('basic', true)
})
it("only the mocked user's programmes are visible", () => {
cy.get('[data-cy=nav-bar-button-studyProgramme]').click()
cy.get('[data-cy=nav-bar-button-class]').click()
cy.contains('label', 'Study programme')
.siblings()
.within(() => {
cy.get("div[role='option']").should('have.length', 2)
cy.get("div[role='option']").eq(0).contains('Matemaattisten tieteiden kandiohjelma')
cy.get("div[role='option']").eq(1).contains('Matematiikan ja tilastotieteen maisteriohjelma')
})
})
})
})
})
+1 -1
View File
@@ -1,4 +1,4 @@
{
"studentSet1": ["010450938", "010589388", "010614509"],
"studentSet1": ["433237", "457144", "458090", "474270", "479440"],
"coursesSet1": ["TKT10001", "TKT10002", "TKT10003", "TKT10004"]
}
+2 -2
View File
@@ -1,4 +1,4 @@
{
"studentNumbersForCSStudentsSet1": ["010450938", "010589388", "010614509"],
"studentNumbersForCSStudentsSet2": ["010262566", "010307146"]
"studentNumbersForCSStudentsSet1": ["433237", "457144", "458090", "465136"],
"studentNumbersForCSStudentsSet2": ["474270", "479440", "470391", "474789"]
}
+7 -7
View File
@@ -2,16 +2,16 @@
{
"id": "sgg-cypress-1",
"name": {
"fi": "TKT kandit 2018"
"fi": "MAT kandit 2020"
},
"members": [
{ "personStudentNumber": "010450938" },
{ "personStudentNumber": "010589388" },
{ "personStudentNumber": "010262566" }
{ "personStudentNumber": "433237" },
{ "personStudentNumber": "457144" },
{ "personStudentNumber": "458090" }
],
"tags": {
"studyProgramme": "KH50_005",
"year": "2018"
"studyProgramme": "KH50_001",
"year": "2020"
}
},
{
@@ -19,7 +19,7 @@
"name": {
"fi": "Oma ohjausryhmä"
},
"members": [{ "personStudentNumber": "010614509" }, { "personStudentNumber": "010307146" }],
"members": [{ "personStudentNumber": "474270" }, { "personStudentNumber": "479440" }],
"tags": {
"studyProgramme": null,
"year": null
+1 -1
View File
@@ -15,7 +15,7 @@
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
+25 -16
View File
@@ -1,4 +1,4 @@
/// <reference types="Cypress" />
/// <reference types="cypress" />
//
// ! IMPORTANT: here we need to set keys to be all lowercase, since
// ! we're replacing headers after they've left browser / frontend.
@@ -43,7 +43,7 @@ const onlyIamRightsUserHeaders = {
uid: 'onlyiamrights',
displayname: 'Only IAM rights user',
'shib-session-id': 'mock-cypress-session',
hygroupcn: 'hy-employees;hy-mltdk-tkt-jory',
hygroupcn: 'hy-employees;hy-mltdk-mat-jory',
mail: 'grp-toska+mockonlyiamrightsuser@helsinki.fi',
hypersonsisuid: 'hy-hlo-1111111',
}
@@ -87,7 +87,7 @@ Cypress.Commands.add('init', (path = '', userId = 'basic') => {
if (!headersToUse) throw Error(`${userId} is not valid user id!`)
req.headers = headersToUse
})
const baseUrl = Cypress.config().baseUrl
const { baseUrl } = Cypress.config()
cy.visit(baseUrl.concat(path))
})
@@ -99,9 +99,8 @@ Cypress.Commands.add('cs', { prevSubject: 'optional' }, (subject, name) => {
if (subject === undefined) {
return cy.get(selector)
} else {
return cy.wrap(subject).find(selector)
}
return cy.wrap(subject).find(selector)
})
/**
@@ -128,20 +127,11 @@ Cypress.Commands.add('selectFromDropdown', { prevSubject: true }, (s, index) =>
}
})
/**
* Move to page with studyProgramme population
*/
Cypress.Commands.add('selectStudyProgramme', name => {
cy.cs('navbar-studyProgramme').click()
cy.cs('navbar-class').click()
cy.cs('select-study-programme').click().children().contains(name).click()
cy.contains('See class').click()
})
Cypress.Commands.add('checkTableStats', (correctStats, tableName) => {
cy.get(`[data-cy=Table-${tableName}] tbody`).within(() => {
correctStats.forEach((values, trIndex) => {
cy.get('tr')
.filter(':visible')
.eq(trIndex)
.within(() => {
values.forEach((value, tdIndex) => {
@@ -149,6 +139,25 @@ Cypress.Commands.add('checkTableStats', (correctStats, tableName) => {
})
})
})
cy.get('tr').should('have.length', correctStats.length)
cy.get('tr').filter(':visible').should('have.length', correctStats.length)
})
})
const getEmptyYears = isAcademicYear => {
const today = new Date()
const latestYear = isAcademicYear && today.getMonth() < 7 ? today.getFullYear() - 1 : today.getFullYear()
const years = []
for (let year = latestYear; year >= 2024; year--) {
if (isAcademicYear) {
years.push(`${year} - ${year + 1}`)
} else {
years.push(year)
}
}
return years
}
module.exports = {
getEmptyYears,
}
+3 -7
View File
@@ -17,12 +17,8 @@
require('@cypress/code-coverage/support')
require('./commands')
// This error is benign and seems to be only present in Cypress
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/
Cypress.on('uncaught:exception', err => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Cypress.on('uncaught:exception', error => {
/* returning false here prevents Cypress from failing the test */
if (resizeObserverLoopErrRe.test(err.message)) {
return false
}
return true
})
+6 -4
View File
@@ -23,13 +23,15 @@ services:
JAMI_URL: 'http://jami:3003/'
CRYPT_KEY: 'e16730fce263d5270ba8dcf84d068a32556542455e71331880d6c1c5e4db9b4e'
build:
context: services/backend
context: services
dockerfile: backend/Dockerfile
frontend:
<<: *defaults
container_name: frontend
build:
context: services/frontend
context: services
dockerfile: frontend/Dockerfile
kone-db:
<<: *defaults
@@ -44,7 +46,7 @@ services:
<<: *defaults
command: >
sh -c 'redis-server --appendonly yes && redis-cli flushall'
image: redis:6.2.14
image: redis:6.2.16
ports:
- 6379:6379
@@ -81,7 +83,7 @@ services:
jami-db:
<<: *defaults
image: postgres:15.7
image: postgres:15.8
environment:
- PGDATA=/data
- POSTGRES_PASSWORD=postgres
+46
View File
@@ -0,0 +1,46 @@
services:
oodikone-backend:
build:
context: ${OODIKONE_REPO:-.}/services
dockerfile: backend/Dockerfile
args:
SENTRY_ENVIRONMENT: ''
SENTRY_RELEASE: ''
SENTRY_DSN: ''
SERVICE_PROVIDER: fd
NODE_ENV: production
image: ${DOCKER_OODIKONE_NAMESPACE:-637423527834.dkr.ecr.eu-north-1.amazonaws.com/oodikone}/oodikone-backend:${DOCKER_IMAGE_TAG:-dev}
oodikone-frontend:
build:
context: ${OODIKONE_REPO:-.}/services
dockerfile: frontend/Dockerfile
args:
SENTRY_ENVIRONMENT: ''
SENTRY_RELEASE: ''
SENTRY_DSN: ''
SERVICE_PROVIDER: fd
LANGUAGE_CENTER_VIEW_ENABLED: 'false' # currently no intention to be enabled
NODE_ENV: production
image: ${DOCKER_OODIKONE_NAMESPACE:-637423527834.dkr.ecr.eu-north-1.amazonaws.com/oodikone}/oodikone-frontend:${DOCKER_IMAGE_TAG:-dev}
updater-scheduler:
build:
context: ${OODIKONE_REPO:-.}/updater/sis-updater-scheduler
args:
SENTRY_ENVIRONMENT: ''
SENTRY_RELEASE: ''
SENTRY_DSN: ''
NODE_ENV: production
image: ${DOCKER_OODIKONE_NAMESPACE:-637423527834.dkr.ecr.eu-north-1.amazonaws.com/oodikone}/updater-scheduler:${DOCKER_IMAGE_TAG:-dev}
updater-worker:
build:
context: ${OODIKONE_REPO:-.}/updater/sis-updater-worker
args:
SENTRY_ENVIRONMENT: ''
SENTRY_RELEASE: ''
SENTRY_DSN: ''
SERVICE_PROVIDER: fd
NODE_ENV: production
image: ${DOCKER_OODIKONE_NAMESPACE:-637423527834.dkr.ecr.eu-north-1.amazonaws.com/oodikone}/updater-worker:${DOCKER_IMAGE_TAG:-dev}
+16 -6
View File
@@ -38,15 +38,21 @@ services:
SIS_UPDATER_URL: http://sis-updater-scheduler:8082
IMPORTER_DB_API_URL: 'https://localhost:3005'
JAMI_URL: 'http://jami:3003/'
SERVICE_PROVIDER: 'Fd'
SERVICE_PROVIDER: fd
SIS_HOST: ${SIS_HOST}
SIS_GRAPHQL_APPLICATION_ACCOUNT: ${SIS_GRAPHQL_APPLICATION_ACCOUNT}
SIS_GRAPHQL_APPLICATION_KEY: ${SIS_GRAPHQL_APPLICATION_KEY}
IMPORTER_DB_API_USER: dev_user
IMPORTER_DB_API_PASSWORD: dev_password
ROOT_ORG_ID: ${ROOT_ORG_ID}
LANGUAGE_CENTER_VIEW_ENABLED: 'false'
ports:
- 8080:8080
volumes:
- ./environments/ha:/opt/app-root/src/src/environment
- ./services/backend/src:/opt/app-root/src/src
- ./services/shared/:/opt/app-root/src/src/shared
frontend:
<<: *defaults
build:
@@ -56,9 +62,11 @@ services:
ports:
- 3000:3000
environment:
REACT_APP_SERVICE_PROVIDER: 'Fd'
REACT_APP_SERVICE_PROVIDER: fd
REACT_APP_LANGUAGE_CENTER_VIEW_ENABLED: 'false'
volumes:
- ./services/frontend/src/:/opt/app-root/src/src
- ./services/shared/:/opt/app-root/src/src/shared
kone-db:
<<: *defaults
@@ -67,7 +75,7 @@ services:
PGDATA: /data
POSTGRES_DB: kone-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
image: postgres:15.8
ports:
- 12349:5432
volumes:
@@ -77,7 +85,7 @@ services:
<<: *defaults
command: 'redis-server --appendonly yes'
container_name: redis
image: redis:6.2.14
image: redis:6.2.16
ports:
- '6379:6379'
volumes:
@@ -90,7 +98,7 @@ services:
PGDATA: /data
POSTGRES_DB: sis-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
image: postgres:15.8
volumes:
- sis-db-data:/data
shm_size: 2gb
@@ -124,6 +132,7 @@ services:
SIS_IMPORTER_USER: dev
SIS_NATS_TOKEN: dev
SIS_NATS_URI: sis-updater-nats://sis-updater-nats:4222
SERVICE_PROVIDER: fd
ports:
- 8082:8082
volumes:
@@ -155,6 +164,7 @@ services:
SIS_NATS_TOKEN: dev
SIS_NATS_URI: sis-updater-nats://sis-updater-nats:4222
ROOT_ORG_ID: ${ROOT_ORG_ID}
SERVICE_PROVIDER: fd
volumes:
- ./updater/sis-updater-worker/:/opt/app-root/src
@@ -165,7 +175,7 @@ services:
PGDATA: /data
POSTGRES_DB: user-db
POSTGRES_HOST_AUTH_METHOD: trust
image: postgres:15.7
image: postgres:15.8
volumes:
- user-db-data:/data
ports:
+4 -2
View File
@@ -46,6 +46,7 @@ services:
volumes:
- backend-nodemod:/opt/app-root/src/node_modules
- ./services/backend/:/opt/app-root/src
- ./services/shared/:/opt/app-root/src/src/shared
frontend:
<<: *defaults
@@ -60,6 +61,7 @@ services:
volumes:
- frontend-nodemod:/opt/app-root/src/node_modules
- ./services/frontend/:/opt/app-root/src
- ./services/shared/:/opt/app-root/src/src/shared
kone-db:
<<: *defaults
@@ -74,7 +76,7 @@ services:
<<: *defaults
command: 'redis-server --appendonly yes'
container_name: redis
image: redis:6.2.14
image: redis:6.2.16
ports:
- '6379:6379'
volumes:
@@ -187,7 +189,7 @@ services:
jami-db:
<<: *defaults
image: postgres:15.7
image: postgres:15.8
environment:
- PGDATA=/data
- POSTGRES_PASSWORD=postgres
+82
View File
@@ -0,0 +1,82 @@
# Access rights in Oodikone
## Roles
Roles are defined [here](../services/backend/src/config/roles.ts). Criteria for each role can be found [here](../services/backend/src/services/userService.ts). Currently these roles exist:
- admin
- courseStatistics
- facultyStatistics
- fullSisuAccess
- katselmusViewer
- openUniSearch
- studyGuidanceGroups
- teachers
The roles `openUniSearch` and `studyGuidanceGroups` simply enable certain views (_Open uni student population_ and _Guidance groups_ respectively).
### courseStatistics
- Can see statistics of every course but year statistics of ≤ 5 students are hidden
### facultyStatistics
- Can see statistics of every course and year
### katselemusViewer
- Enables the _Evaluation overview_ view
### teachers
- By default, the role only allows seeing the statistics of course providers (study programmes) they have full rights to (see [Programmes](#programmes))
- This role always needs to be given and removed manually
- If the user is a member of the `hy-dekaanit` or `hy-varadekaanit-opetus` IAM group (or is an admin), they can see leaderboards, information about individual teachers, and statistics of all course providers. They don't need to be given the `teachers` role manually.
### Other roles
| | Everybody\* | Admin | fullSisuAccess |
| -------------------- | :---------: | :---: | :------------: |
| Front page | ✅ | ✅ | ✅ |
| Feedback | ✅ | ✅ | ✅ |
| University | ✅ | ✅ | ✅ |
| Language center view | | ✅ | |
| Faculties | | ✅ | ✅ |
| Updater | | ✅ | |
| Users | | ✅ | |
| Programmes | | ✅ | ✅ |
| Students | | ✅ | ✅ |
| Teachers | | ✅ | |
| Guidance groups | | | |
| Custom population | | ✅ | ✅ |
| Completed courses | ✅ | ✅ | ✅ |
| Close to graduation | | ✅ | ✅ |
| Open uni population | | ✅ | |
| Evaluation overview | | ✅ | |
| Courses | | ✅ | ✅ |
\* = anyone who can log in to Oodikone, check the [IAM groups](#iam-groups) section for more information
## Programmes
There are two types of programme rights: limited and full. Limited rights mean that the user cannot see individual students of those programmes, while a user with full rights can.
Manually given programme rights are **always** full rights. Some of the programme rights are based on user's IAM groups: some only qualify for limited rights, some for full rights.
## IAM groups
A user must be a member of either the `grp-oodikone-basic-users` or `grp-oodikone-users` IAM group to be able to log in to Oodikone. The following IAM groups are used to give roles:
| IAM group | Role given based on the group |
| ------------------------ | ----------------------------- |
| grp-oodikone-basic-users | courseStatistics |
| grp-oodikone-users | facultyStatistics |
| grp-toska | admin |
| grp-katselmus-\* | katselmusViewer |
| hy-ypa-opa-dojo | openUniSearch |
`grp-oodikone-basic-users`: basically the whole staff (hy-\*-allstaff)
`grp-oodikone-users`: more limited (e.g. _koulutussuunnittelijat_, _ospa_, _opintoasiainpäälliköt_, _(vara)dekaanit_, _johtoryhmä_)
`grp-kielikeskus-esihenkilot`: gives access to the language center view
Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+46 -5
View File
@@ -1,20 +1,52 @@
# Databases
Oodikone uses three separate databases, each for different purposes. This document contains a simplified overview of each database and the relations between models.
Oodikone uses three main PostgreSQL databases, each for a different purpose. This document contains a simplified overview of each database and the relations between models.
Use Adminer or refer to the actual database for a detailed view of each model and their attributes.
## sis-db
## Storage
### sis-db
The `sis-db` database contains information originating from Sisu.
_To be documented._
```mermaid
---
title: sis-db
---
erDiagram
enrollment }o--|| course : ""
enrollment }o--|| semesters : ""
enrollment }o--|| student : ""
credit_teachers }o--|| teacher : ""
course_types
curriculum_periods
credit_teachers }o--|| credit : ""
credit }o--|| course : ""
credit }o--|| semesters : ""
credit }o--|| credit_types : ""
credit }o--|| student : ""
studyplan }o--|| student : ""
studyplan }o--|| sis_study_rights : ""
sis_study_rights }o--|| studyright_extents : ""
sis_study_rights }o--|| organization : ""
sis_study_rights }o--|| student : ""
sis_study_right_elements }o--|| sis_study_rights : ""
course_providers }o--|| organization : ""
course_providers }o--|| course : ""
programme_module_children }o--|| programme_modules : "references parent"
programme_module_children }o--|| programme_modules : "references child"
programme_modules }o--o| organization : "belong to"
```
## kone-db
### 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
---
title: kone-db
---
erDiagram
custom_population_searches
excluded_courses
@@ -27,11 +59,20 @@ erDiagram
tag_student
```
## user-db
### user-db
The `user-db` database contains information about users of Oodikone.
```mermaid
---
title: user-db
---
erDiagram
users
```
## Caching
### Redis
Redis is used in the backend to cache calculation results.
+10 -31
View File
@@ -4,7 +4,7 @@ Data used in Oodikone contains some numeric types (or _magic numbers_) that are
## Extent code
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`.
Appears in model SISStudyRight (db: `sis_study_rights`) as _extentCode_ (db: `extent_code`). Values are defined in the database table `studyright_extents`.
| value | meaning |
| ----: | :-------------------------------------------------- |
@@ -26,33 +26,6 @@ Appears in model Studyright (db: `studyright`) as _extentcode_ (db: `extentcode`
| 34 | Exchange studies (postgraduate studies) |
| 99 | Non-Degree studies |
## Priority code
> [!WARNING]
> Obsolete, to be deprecated
Appears in model Studyright (db: `studyright`) as _prioritycode_ (db: `prioritycode`).
| 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 | |
## Type
> [!WARNING]
> Obsolete, to be deprecated
Appears in model ElementDetails (db: `element_details`) as _type_ (db: `type`).
| value | meaning |
| ----: | :----------------------------------------------- |
| 20 | study programme |
| 30 | module, studytrack or kokonaisuus e.g. neurology |
## Phase
Appears in model SISStudyRightElement (db: `sis_study_right_elements`) as _phase_ (db: `phase`).
@@ -72,7 +45,15 @@ The level of the programme (bachelor's, master's etc.) of the study right can be
## Enrollment type
Appears in model SemesterEnrollment (db: `semester_enrollments`) as _enrollmenttype_ (db: `enrollmenttype`).
This information is used in the model SISStudyRight (db: `sis_study_rights`), in the field _semesterEnrollments_ (db: `semester_enrollments`).
The `semester_enrollments` column is an array of JSON objects. Each object contains:
- `type` (the type of enrollment),
- `semester` (the code for a semester),
- and sometimes `statutoryAbsence` (this field is included only when the enrollment type is 2).
The `type` field represents one of the following enrollment types. The `semester` field refers to a semester code, defined as `semestercode` in the model Semester.
| value | meaning | notes |
| ----: | :------- | :------------------------------- |
@@ -84,8 +65,6 @@ Appears in model SemesterEnrollment (db: `semester_enrollments`) as _enrollmentt
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 |
+10 -1
View File
@@ -1,6 +1,6 @@
# Resources
A collection of useful resources and documentation.
A collection of useful tools, documentation and other resources.
## AI tools
@@ -19,6 +19,9 @@ A collection of useful resources and documentation.
## Database
- [pgcli](https://www.pgcli.com)
- A useful commandline tool to interact with the database
- Install: `$ pip install pgcli`
- [Postgres documentation](https://www.postgresql.org/docs/15/index.html)
- [Sequelize documentation](https://sequelize.org/docs/v6/)
@@ -39,12 +42,18 @@ A collection of useful resources and documentation.
## User interface
- [Material UI documentation](https://mui.com/material-ui/getting-started/)
- [Material Icons](https://mui.com/material-ui/material-icons/)
- [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/)
+177
View File
@@ -0,0 +1,177 @@
import pluginJs from '@eslint/js'
import globals from 'globals'
import pluginCypress from 'eslint-plugin-cypress/flat'
import pluginImport from 'eslint-plugin-import'
import pluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
import pluginReact from 'eslint-plugin-react'
import pluginReactHooks from 'eslint-plugin-react-hooks'
import tsEslint from 'typescript-eslint'
/** @type {import('eslint').Linter.Config[]} */
export default [
{
files: ['**/*.{js,ts,jsx,tsx}'],
},
{
ignores: ['eslint.config.mjs', '**/build/', '**/dist/', '**/node_modules/'],
},
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
{
linterOptions: {
reportUnusedDisableDirectives: 'error',
},
},
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parserOptions: {
project: [
'services/frontend/tsconfig.json',
'services/frontend/tsconfig.node.json',
'services/backend/tsconfig.json',
'services/shared/tsconfig.json',
],
},
},
},
pluginJs.configs.recommended,
...tsEslint.configs.recommendedTypeChecked,
...tsEslint.configs.stylisticTypeChecked,
pluginCypress.configs.recommended,
pluginReact.configs.flat.recommended,
pluginReact.configs.flat['jsx-runtime'],
pluginImport.flatConfigs.recommended,
pluginImport.flatConfigs.typescript,
pluginPrettierRecommended, // This must be the last plugin so it can override other configs
{
plugins: {
'react-hooks': pluginReactHooks,
},
},
{
rules: {
camelcase: 'off', // TODO: Enable this rule (at least for frontend)
'class-methods-use-this': 'error',
'consistent-return': 'error',
'id-denylist': ['error', 'c', 'd', 'e', 'err', 't'],
'import/no-commonjs': 'error',
'import/no-default-export': 'error',
'import/no-extraneous-dependencies': ['error', { devDependencies: false }],
'import/no-unused-modules': ['error', { missingExports: true, unusedExports: true }],
'import/order': [
'error',
{
alphabetize: { order: 'asc', caseInsensitive: true },
groups: [['builtin', 'external'], ['internal'], ['parent'], ['sibling', 'index']],
},
],
'no-alert': 'error',
'no-async-promise-executor': 'error',
'no-await-in-loop': 'error',
'no-console': 'error',
'no-implied-eval': 'error',
'no-param-reassign': ['error', { props: false }],
'no-promise-executor-return': 'error',
'no-return-assign': 'error',
'no-return-await': 'error',
'object-shorthand': ['error', 'always'],
'prefer-const': 'error',
'prefer-destructuring': ['error', { VariableDeclarator: { object: true } }],
quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: false }],
'react/display-name': 'off', // TODO: Delete this override
'react/function-component-definition': [
'error',
{ namedComponents: 'arrow-function', unnamedComponents: 'arrow-function' },
],
'react/jsx-filename-extension': ['error', { allow: 'as-needed', extensions: ['.jsx', '.tsx'] }],
'react/jsx-sort-props': ['error', { reservedFirst: false }],
'react/no-array-index-key': 'error',
'react/no-this-in-sfc': 'error',
'react/no-unescaped-entities': 'off',
'react/no-unknown-property': 'off', // TODO: Delete this override
'react/prefer-stateless-function': 'error',
'react/prop-types': 'off',
...pluginReactHooks.configs.recommended.rules,
'react-hooks/exhaustive-deps': 'off', // TODO: Delete this override
'@typescript-eslint/array-type': 'off',
'@typescript-eslint/consistent-indexed-object-style': 'off',
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }],
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-misused-promises': 'off', // Most of these errors come from Express route handlers that are handled correctly by express-async-errors
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', caughtErrors: 'none', ignoreRestSiblings: true, varsIgnorePattern: '^_' },
],
'@typescript-eslint/restrict-template-expressions': 'off',
},
},
{
files: ['**/*.{js,jsx}'],
rules: {
...tsEslint.configs.disableTypeChecked.rules,
'dot-notation': 'error',
},
},
{
files: ['services/backend/src/rapodiff/**/*'],
rules: {
'no-console': 'off',
},
},
{
files: ['cypress/**/*.js', 'services/backend/**/*.js', 'updater/**/*.js'],
languageOptions: {
sourceType: 'commonjs',
},
rules: {
'@typescript-eslint/no-require-imports': 'off',
'import/no-commonjs': 'off',
'import/no-unused-modules': 'off',
},
},
{
files: ['services/backend/**/*.{js,ts}', 'updater/**/*.js'],
rules: {
// TODO: Most of these overrides should probably be removed
'consistent-return': 'off',
'import/no-default-export': 'off',
'import/no-unused-modules': 'off',
'no-await-in-loop': 'off',
'no-param-reassign': 'off',
'no-promise-executor-return': 'off',
'no-return-await': 'off',
},
},
{
settings: {
'import/internal-regex': '^@/shared/',
'import/resolver': {
typescript: {
project: [
'services/frontend/tsconfig.json',
'services/frontend/tsconfig.node.json',
'services/backend/tsconfig.json',
'services/shared/tsconfig.json',
],
},
},
react: {
version: '18.3',
},
},
},
]
+1067 -1060
View File
File diff suppressed because it is too large Load Diff
+18 -20
View File
@@ -7,7 +7,7 @@
"cypress:run": "cypress run --browser chrome",
"instrument": "./scripts/instrument.sh",
"eslint": "eslint",
"lint:all": "eslint --ext .js,.jsx,.ts,.tsx services/ updater/ --report-unused-disable-directives",
"lint:all": "eslint services/ updater/",
"stylelint": "stylelint",
"actionlint": "node-actionlint",
"prettier": "prettier",
@@ -23,7 +23,7 @@
"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",
"rapodiff": "docker exec oodikone_backend_1 node /opt/app-root/src/src/rapodiff/rapodiff.js",
"rapodiff": "docker exec -t oodikone_backend_1 /opt/app-root/src/node_modules/.bin/tsx /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",
"install:both": "npm run install:local && npm run install:docker"
@@ -38,29 +38,27 @@
},
"homepage": "https://github.com/UniversityOfHelsinkiCS/oodikone#readme",
"devDependencies": {
"@cypress/code-coverage": "^3.12.39",
"@cypress/code-coverage": "^3.13.6",
"@eslint/js": "^9.14.0",
"@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",
"cypress": "^13.15.2",
"eslint": "^9.14.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-custom-alias": "^1.3.2",
"eslint-plugin-cypress": "^3.0.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-cypress": "^4.1.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"globals": "^15.12.0",
"husky": "^9.1.6",
"lint-staged": "^15.2.10",
"node-actionlint": "^1.2.2",
"nyc": "^17.0.0",
"nyc": "^17.1.0",
"prettier": "3.3.3",
"stylelint": "^16.2.1",
"stylelint-config-standard": "^36.0.0"
"stylelint": "^16.10.0",
"stylelint-config-standard": "^36.0.1",
"typescript-eslint": "^8.13.0"
},
"dependencies": {
"lodash": "^4.17.21",
+15 -3
View File
@@ -5,18 +5,30 @@
{
"groupName": "all non-major dependencies",
"groupSlug": "all-minor-patch",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["minor", "patch"]
"matchUpdateTypes": ["minor", "patch"],
"matchCurrentVersion": "!/^0/",
"automerge": true,
"matchPackageNames": ["*"]
},
{
"matchDatasources": ["docker"],
"matchPackageNames": ["node"],
"versionCompatibility": "^(?<version>[^-]+)(?<compatibility>-.*)?$",
"versioning": "node"
},
{
"matchDatasources": ["docker"],
"matchPackageNames": ["node"],
"matchUpdateTypes": ["minor", "patch"],
"enabled": false
}
],
"rebaseWhen": "never",
"vulnerabilityAlerts": {
"labels": ["security"]
}
},
"addLabels": ["dependencies"],
"rangeStrategy": "bump",
"schedule": ["before 5:00am"],
"timezone": "Europe/Helsinki"
}
+20 -8
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# This file includes functions that are used to setup oodikone. File doesn't run anything
# by itself and should be sourced from other script.
# This file includes functions that are used to setup Oodikone
# File doesn't run anything by itself and should be sourced from other scripts
# === Config ===
@@ -202,16 +202,13 @@ set_up_oodikone_from_scratch() {
)
infomsg "Cleaning up docker setup and node_modules from possible previous installations"
local compose_down_args="--remove-orphans --volumes --rmi all"
"$PROJECT_ROOT"/run.sh both down "$compose_down_args" || infomsg "Cleaning errored, but will continue"
docker-compose --file "$PROJECT_ROOT"/docker-compose.ci.yml "$compose_down_args" || infomsg "Cleaning errored, but will continue"
"$PROJECT_ROOT"/run.sh both down --remove-orphans --volumes --rmi all || infomsg "Cleaning errored, but will continue"
for folder in "${folders_to_set_up[@]}"; do
cd "$folder" || die "Couldn't change directory to folder $folder"
rm -rf node_modules|| die "Couldn't remove node_modules in folder $folder"
done
infomsg "Installing npm packages locally to root and each subfolder to enable \
linting and formatting"
infomsg "Installing npm packages locally to root and each subfolder to enable linting and formatting"
for folder in "${folders_to_set_up[@]}"; do
cd "$folder" || die "Couldn't change directory to folder $folder"
@@ -228,7 +225,22 @@ linting and formatting"
infomsg "Building images"
"$PROJECT_ROOT"/run.sh both anon build
successmsg "Setup ready, oodikone can be started! See README for more info."
TARGET_PATH="$PROJECT_ROOT/services/shared"
LINK_PATHS=(
"$PROJECT_ROOT/services/backend/src/shared"
"$PROJECT_ROOT/services/frontend/src/shared"
)
for SYMLINK_PATH in "${LINK_PATHS[@]}"; do
infomsg "Linking $TARGET_PATH to $SYMLINK_PATH"
if [ ! -L "$SYMLINK_PATH" ]; then
ln -s "$TARGET_PATH" "$SYMLINK_PATH"
else
infomsg "Symbolic link $SYMLINK_PATH already exists. Skipping creation."
fi
done
successmsg "Setup ready, Oodikone can be started! See README for more info."
}
docker_prune() {
+2 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# This file includes some utility functions that are used in oodikone scripts.
# This file includes some utility functions that are used in Oodikone scripts
# Functions are based on https://betterdev.blog/minimal-safe-bash-script-template/
# Print messages and logs that are not script output to stderr
@@ -27,7 +27,7 @@ warningmsg() {
}
# Quit and exit with given message and error code
# By default uses exit code 1, message must be given.
# By default uses exit code 1, message must be given
die() {
local msg=$1
local code=${2-1}
+5 -6
View File
@@ -1,4 +1,4 @@
FROM registry.access.redhat.com/ubi9/nodejs-20-minimal as build
FROM registry.access.redhat.com/ubi9/nodejs-22-minimal AS build
ENV TZ="Europe/Helsinki"
@@ -19,17 +19,16 @@ ENV SENTRY_ENVIRONMENT=$SENTRY_ENVIRONMENT
ARG SENTRY_RELEASE
ENV SENTRY_RELEASE=$SENTRY_RELEASE
COPY ./package*.json ./
COPY ./backend/package*.json ./
RUN npm ci --include=dev
COPY . .
COPY ./shared ./src/shared
COPY ./backend .
RUN npm run build
FROM registry.access.redhat.com/ubi9/nodejs-20-minimal
FROM registry.access.redhat.com/ubi9/nodejs-22-minimal
ENV TZ="Europe/Helsinki"
+1 -1
View File
@@ -1,4 +1,4 @@
FROM node:20-alpine
FROM node:22-alpine
ENV TZ="Europe/Helsinki"
+1
View File
@@ -1,3 +1,4 @@
require('./src/util/sentry') // Sentry must be the first import
require('./src/app')
const logger = require('./src/util/logger')
+2306 -2646
View File
File diff suppressed because it is too large Load Diff
+23 -23
View File
@@ -2,49 +2,49 @@
"name": "backend",
"private": true,
"scripts": {
"build": "tsc",
"rapodiff": "node /opt/app-root/src/src/rapodiff.js"
"build": "tsc"
},
"dependencies": {
"@sentry/node": "^5.30.0",
"@sentry/tracing": "^6.18.2",
"@sentry/node": "^8.37.1",
"@sentry/profiling-node": "^8.37.1",
"axios": "^0.28.1",
"bluebird": "^3.7.2",
"bullmq": "^4.14.2",
"compression": "^1.7.4",
"bullmq": "^5.25.4",
"compression": "^1.7.5",
"cors": "^2.8.5",
"cron": "^1.8.2",
"express": "^4.19.2",
"cron": "^3.1.9",
"express": "^4.21.1",
"express-async-errors": "^3.1.1",
"lodash": "^4.17.21",
"lru-cache": "^10.2.2",
"lru-cache": "^11.0.2",
"moment": "^2.30.1",
"moment-timezone": "^0.5.37",
"moment-timezone": "^0.5.46",
"morgan": "^1.10.0",
"oodikone-shared": "file:../oodikone-shared",
"papaparse": "^5.4.1",
"pg": "^8.11.5",
"redis": "^3.1.2",
"pg": "^8.13.1",
"redis": "^4.7.0",
"reflect-metadata": "^0.2.2",
"sequelize": "^6.37.3",
"sequelize": "^6.37.5",
"sequelize-typescript": "^2.1.6",
"umzug": "^2.3.0",
"umzug": "^3.8.2",
"unfuck-utf8-headers-middleware": "^1.0.1",
"winston": "^3.8.2",
"winston": "^3.17.0",
"winston-gelf-transporter": "^1.0.2"
},
"devDependencies": {
"@types/compression": "^1.7.5",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/lodash": "^4.17.6",
"@types/lodash": "^4.17.13",
"@types/morgan": "^1.9.9",
"@types/node": "^20.14.2",
"@types/umzug": "^2.3.7",
"@types/validator": "^13.12.0",
"tsx": "^4.16.2",
"typescript": "^5.5.3"
"@types/node": "^22.9.0",
"@types/validator": "^13.12.2",
"tsx": "^4.19.2",
"typescript": "^5.6.3"
},
"overrides": {
"winston-gelf-transporter": {
"gelf-pro": "^1.3.4"
"gelf-pro": "^1.3.12"
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ const express = require('express')
const { baseUrl, backendPort } = require('./config')
const { initializeDatabaseConnection, dbConnections } = require('./database/connection')
const { startCron } = require('./events')
const routes = require('./routes')
const routes = require('./routes').default
const logger = require('./util/logger')
require('./worker/worker')
+16 -9
View File
@@ -3,10 +3,6 @@ const isDev = process.env.NODE_ENV === 'development'
const isStaging = process.env.REACT_APP_STAGING === 'true'
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,6 +19,8 @@ const jamiUrl = process.env.JAMI_URL || ''
// Importer client
const importerUrl = process.env.IMPORTER_DB_API_URL || ''
const importerToken = process.env.IMPORTER_DB_API_TOKEN || ''
const importerDbApiUser = process.env.IMPORTER_DB_API_USER || ''
const importerDbApiPassword = process.env.IMPORTER_DB_API_PASSWORD || ''
// Sisu
const sisUrl = process.env.SIS_HOST
@@ -35,14 +33,21 @@ const frontUrl = process.env.FRONT_URL
const backendPort = 8080
// System run in whose environment
const serviceProvider = process.env.SERVICE_PROVIDER || 'Toska'
const serviceProvider = process.env.SERVICE_PROVIDER ? process.env.SERVICE_PROVIDER.toLowerCase() : 'toska'
// Optional logout-url configuration
const configLogoutUrl = serviceProvider === 'Toska' ? undefined : process.env.LOGOUT_URL
const configLogoutUrl = serviceProvider === 'toska' ? undefined : process.env.LOGOUT_URL
// Other stuff
const { DB_URL_KONE, DB_URL_USER, SECRET_TOKEN, SIS_DB_URL, SIS_UPDATER_URL, CRYPT_KEY } = process.env
const rootOrgId = process.env.ROOT_ORG_ID || 'hy-university-root-id'
const languageCenterViewEnabled =
process.env.LANGUAGE_CENTER_VIEW_ENABLED == null ? true : process.env.LANGUAGE_CENTER_VIEW_ENABLED === 'true'
const concurrentWorkers = process.env.CONCURRENT_WORKERS ? parseInt(process.env.CONCURRENT_WORKERS, 10) : 2
let DB_MAX_CONNECTIONS = parseInt(process.env.DB_MAX_CONNECTIONS, 10)
if (Number.isNaN(DB_MAX_CONNECTIONS)) {
DB_MAX_CONNECTIONS = 5 // sequelize's default
@@ -75,9 +80,6 @@ module.exports = {
SIS_DB_URL,
SIS_UPDATER_URL,
runningInCI,
sentryRelease,
sentryEnvironment,
sentryDSN,
isProduction,
pateToken,
jamiUrl,
@@ -86,11 +88,16 @@ module.exports = {
backendPort,
importerUrl,
importerToken,
importerDbApiUser,
importerDbApiPassword,
isDev,
isStaging,
rootOrgId,
serviceProvider,
configLogoutUrl,
sisUrl,
sisGrapqlAppAccount,
sisGrapqlAppKey,
languageCenterViewEnabled,
concurrentWorkers,
}
@@ -1,4 +1,4 @@
// Linter was not having any of this so neeeded to add resolveJsonModule:true to tsconfig
// Linter was not having any of this so needed to add resolveJsonModule:true to tsconfig
// export const { facultyCodes, ignoredFacultyCodes, magicFacultyCode } = require('../environment/organizationConfig.json')
export { facultyCodes, ignoredFacultyCodes, magicFacultyCode } from '../environment/organizationConfig.json'
+10 -23
View File
@@ -1,6 +1,6 @@
import EventEmitter from 'events'
import { Sequelize } from 'sequelize-typescript'
import Umzug from 'umzug'
import { SequelizeStorage, Umzug } from 'umzug'
import conf from '../config'
import {
@@ -10,22 +10,18 @@ import {
Credit,
CreditTeacher,
CreditType,
ElementDetail,
CurriculumPeriod,
Enrollment,
Organization,
ProgrammeModule,
ProgrammeModuleChild,
Semester,
SemesterEnrollment,
SISStudyRight,
SISStudyRightElement,
Student,
Studyplan,
Studyright,
StudyrightElement,
StudyrightExtent,
Teacher,
Transfer,
} from '../models'
import {
CustomPopulationSearch,
@@ -70,22 +66,18 @@ class DbConnection extends EventEmitter {
Credit,
CreditTeacher,
CreditType,
ElementDetail,
CurriculumPeriod,
Enrollment,
Organization,
ProgrammeModule,
ProgrammeModuleChild,
Semester,
SemesterEnrollment,
SISStudyRight,
SISStudyRightElement,
Student,
Studyplan,
Studyright,
StudyrightElement,
StudyrightExtent,
Teacher,
Transfer,
],
})
}
@@ -126,7 +118,7 @@ const sequelizeKone = new Sequelize(conf.DB_URL_KONE!, {
],
})
sequelizeKone.query(`SET SESSION search_path to ${conf.DB_SCHEMA_KONE}`)
void sequelizeKone.query(`SET SESSION search_path to ${conf.DB_SCHEMA_KONE}`)
const sequelizeUser = new Sequelize(conf.DB_URL_USER!, {
logging: false,
@@ -160,21 +152,16 @@ const initializeDatabaseConnection = async () => {
const schema = dbName === 'kone-db' ? conf.DB_SCHEMA_KONE : undefined
const migrationsFolder = dbName === 'kone-db' ? 'migrations_kone' : 'migrations_user'
const migrator = new Umzug({
storage: 'sequelize',
storageOptions: {
sequelize: seq,
tableName: 'migrations',
schema,
},
storage: new SequelizeStorage({ sequelize: seq, tableName: 'migrations', schema }),
migrations: {
params: [seq.getQueryInterface(), Sequelize],
path: `${process.cwd()}${conf.isProduction || conf.isStaging ? '/dist' : ''}/src/database/${migrationsFolder}`,
pattern: /\.js$/,
glob: `${process.cwd()}${conf.isProduction || conf.isStaging ? '/dist' : ''}/src/database/${migrationsFolder}/*.js`,
},
context: seq.getQueryInterface(),
logger: console,
})
try {
const migrations = await migrator.up()
logger.info({ message: `${dbName} migrations up to date: `, meta: migrations.map(migration => migration.file) })
await migrator.up()
logger.info({ message: `${dbName} migrations up to date` })
} catch (error) {
logger.error({ message: `${dbName} migrations failed`, meta: error })
throw error
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.sequelize.query(`
-- Adminer 4.6.2 PostgreSQL dump
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.sequelize.query(`
-- Adminer 4.6.2 PostgreSQL dump
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
return queryInterface.sequelize.transaction(async transaction => {
await queryInterface.sequelize.query(
"SELECT setval('mandatory_courses_id_seq', (SELECT MAX(id) FROM mandatory_courses))",
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
return queryInterface.sequelize.transaction(async 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 })
@@ -1,7 +1,9 @@
const { STRING } = require('sequelize')
module.exports = {
up: async (queryInterface, Sequelize) => {
up: async ({ context: queryInterface }) => {
await queryInterface.bulkDelete('tag', [])
await queryInterface.addColumn('tag', 'year', { type: Sequelize.STRING })
await queryInterface.addColumn('tag', 'year', { type: STRING })
},
down: async () => {},
}
@@ -1,7 +1,9 @@
const { BIGINT } = require('sequelize')
module.exports = {
up: async (queryInterface, Sequelize) => {
up: async ({ context: queryInterface }) => {
await queryInterface.addColumn('tag', 'personal_user_id', {
type: Sequelize.BIGINT,
type: BIGINT,
})
},
down: async () => {},
@@ -1,7 +1,7 @@
const { BIGINT, STRING, DATE, ARRAY } = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.createTable('custom_population_searches', {
id: {
type: BIGINT,
@@ -1,7 +1,7 @@
const { STRING, DATE, JSONB } = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.createTable('programme_modules', {
id: {
primaryKey: true,
@@ -4,7 +4,7 @@ const { INTEGER } = require('sequelize')
// this has to be done
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.addColumn('programme_modules', 'order', {
type: INTEGER,
})
@@ -1,7 +1,7 @@
const { INTEGER, STRING, DATE } = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.createTable(
'excluded_courses',
{
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('teacher_course_groups')
await queryInterface.dropTable('course_groups')
},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.sequelize.query('DROP SEQUENCE "course_groups_id_seq"')
await queryInterface.sequelize.query('DROP SEQUENCE "teacher_course_group_id_seq"')
},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('filters')
},
down: async () => {},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('usage_statistics')
},
down: async () => {},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('unit_tag')
},
down: async () => {},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('course_duplicates')
},
down: async () => {},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('programme_module_children')
await queryInterface.dropTable('programme_modules')
},
@@ -1,7 +1,7 @@
const { BIGINT, STRING, DATE } = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.createTable(
'study_guidance_group_tags',
{
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('thesis_courses')
},
down: async () => {},
@@ -1,7 +1,7 @@
const { BIGINT, STRING, DATE, ARRAY } = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.createTable('open_uni_population_searches', {
id: {
type: BIGINT,
@@ -1,7 +1,7 @@
const { INTEGER, STRING, ARRAY, DATE } = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.createTable('progress_criteria', {
code: {
type: STRING,
@@ -1,22 +1,24 @@
const { ARRAY, STRING, INTEGER } = require('sequelize')
module.exports = {
up: async (queryInterface, Sequelize) => {
up: async ({ context: queryInterface }) => {
await queryInterface.addColumn('progress_criteria', 'courses_year_four', {
type: Sequelize.ARRAY(Sequelize.STRING),
type: ARRAY(STRING),
})
await queryInterface.addColumn('progress_criteria', 'courses_year_five', {
type: Sequelize.ARRAY(Sequelize.STRING),
type: ARRAY(STRING),
})
await queryInterface.addColumn('progress_criteria', 'courses_year_six', {
type: Sequelize.ARRAY(Sequelize.STRING),
type: ARRAY(STRING),
})
await queryInterface.addColumn('progress_criteria', 'credits_year_four', {
type: Sequelize.INTEGER,
type: INTEGER,
})
await queryInterface.addColumn('progress_criteria', 'credits_year_five', {
type: Sequelize.INTEGER,
type: INTEGER,
})
await queryInterface.addColumn('progress_criteria', 'credits_year_six', {
type: Sequelize.INTEGER,
type: INTEGER,
})
},
down: async () => {},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('mandatory_courses')
await queryInterface.dropTable('mandatory_course_labels')
},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.sequelize.query('DROP SEQUENCE "mandatory_courses_id_seq"')
await queryInterface.sequelize.query('DROP SEQUENCE "mandatory_course_labels_id_seq"')
},
@@ -1,16 +1,18 @@
const { STRING } = require('sequelize')
module.exports = {
up: async (queryInterface, Sequelize) => {
up: async ({ context: queryInterface }) => {
await queryInterface.sequelize.query('TRUNCATE TABLE progress_criteria')
await queryInterface.sequelize.query('TRUNCATE TABLE excluded_courses')
await queryInterface.removeConstraint('excluded_courses', 'excluded_courses_programme_code_course_code_key')
await queryInterface.addColumn('progress_criteria', 'curriculum_version', {
type: Sequelize.STRING,
type: STRING,
})
await queryInterface.addColumn('excluded_courses', 'curriculum_version', {
type: Sequelize.STRING,
type: STRING,
})
},
down: async queryInterface => {
down: async ({ context: queryInterface }) => {
await queryInterface.removeColumn('progress_criteria', 'curriculum_version')
await queryInterface.removeColumn('excluded_courses', 'curriculum_version')
},
@@ -1,7 +1,7 @@
const Sequelize = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.createTable('study_programme_pins', {
user_id: {
primaryKey: true,
@@ -12,7 +12,7 @@ module.exports = {
},
})
},
down: async queryInterface => {
down: async ({ context: queryInterface }) => {
await queryInterface.dropTable('study_programme_pins')
},
}
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface =>
up: async ({ context: queryInterface }) =>
queryInterface.sequelize.query(
`
--
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface =>
up: async ({ context: queryInterface }) =>
queryInterface.sequelize.query(
`
--
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
return queryInterface.sequelize.transaction(async transaction => {
await queryInterface.sequelize.query(
'ALTER TABLE user_accessgroup DROP CONSTRAINT IF EXISTS "user_accessgroup_accessGroupId_fkey"',
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
return queryInterface.bulkDelete('access_groups', {
group_code: 'oodilearn',
})
@@ -1,5 +1,5 @@
module.exports = {
up: queryInterface => {
up: ({ context: queryInterface }) => {
return queryInterface.bulkInsert('access_groups', [
{
group_code: 'cooldata',
@@ -1,5 +1,5 @@
module.exports = {
up: queryInterface => {
up: ({ context: queryInterface }) => {
return queryInterface.bulkInsert('access_groups', [
{
group_code: 'courseStatistics',
@@ -1,5 +1,5 @@
module.exports = {
up: queryInterface => {
up: ({ context: queryInterface }) => {
return queryInterface.bulkDelete('access_groups', [
{
group_code: 'faculties',
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.dropTable('user_unit')
},
down: async () => {},
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface =>
up: async ({ context: queryInterface }) =>
queryInterface.sequelize.query(`
--- Some lääkis special programmes
INSERT INTO faculty_programmes VALUES ('H30', '447000', 'NOW()', 'NOW()');
@@ -1,5 +1,5 @@
module.exports = {
up: async queryInterface =>
up: async ({ context: queryInterface }) =>
queryInterface.sequelize.query(`
--- Some kasvis special programmes
INSERT INTO faculty_programmes VALUES ('H60', 'ED400', 'NOW()', 'NOW()');
@@ -1,10 +1,10 @@
const { STRING } = require('sequelize')
module.exports = {
up: async queryInterface => {
up: async ({ context: queryInterface }) => {
await queryInterface.addColumn('users', 'sisu_person_id', STRING)
},
down: async queryInterface => {
down: async ({ context: queryInterface }) => {
await queryInterface.removeColumn('users', 'sisu_person_id')
},
}

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