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