[Updater] Add migration, update models

This commit is contained in:
joonas-a
2026-09-21 15:24:21 +03:00
committed by Aaro Koivukangas
parent b8e9d59217
commit 5bd7fb6792
6 changed files with 112 additions and 64 deletions
+38 -33
View File
@@ -21,15 +21,19 @@ import { EnrollmentModel } from './enrollment'
import { OrganizationModel } from './organization'
@Table({
underscored: false,
underscored: true,
modelName: 'course',
tableName: 'course',
})
export class CourseModel extends Model implements Course {
/* COLUMNS */
@PrimaryKey
@Column(DataType.STRING)
declare id: Course['id']
@Column(DataType.STRING)
declare groupId: Course['id']
@ForeignKey(() => CourseModel)
@Column(DataType.STRING)
declare code: Course['code']
@@ -38,6 +42,39 @@ export class CourseModel extends Model implements Course {
@Column(DataType.STRING)
declare coursetypecode: Course['coursetypecode']
@Column(DataType.BOOLEAN)
declare isStudyModule: Course['isStudyModule']
@Column(DataType.JSONB)
declare name: Course['name']
@Column(DataType.DATE)
declare maxAttainmentDate: Course['maxAttainmentDate']
@Column(DataType.DATE)
declare minAttainmentDate: Course['minAttainmentDate']
@CreatedAt
@Column({ field: 'created_at', type: DataType.DATE })
declare createdAt: Course['createdAt']
@UpdatedAt
@Column({ field: 'updated_at', type: DataType.DATE })
declare updatedAt: Course['updatedAt']
@Column(DataType.JSONB)
declare validityPeriod: Course['validityPeriod']
@Column(DataType.JSONB)
declare substitutionGroups: Course['substitutionGroups']
@Column(DataType.BOOLEAN)
declare isPrimary: Course['isPrimary']
@Column(DataType.STRING)
declare courseUnitType: Course['courseUnitType']
/* RELATIONS */
@BelongsTo(() => CourseTypeModel, { foreignKey: 'coursetypecode', targetKey: 'coursetypecode' })
declare courseType: Course['courseType']
@@ -49,36 +86,4 @@ export class CourseModel extends Model implements Course {
@BelongsToMany(() => OrganizationModel, () => CourseProviderModel, 'coursecode')
declare organizations: Course['organizations']
@Column(DataType.BOOLEAN)
declare is_study_module: Course['is_study_module']
@Column(DataType.JSONB)
declare name: Course['name']
@Column(DataType.DATE)
declare max_attainment_date: Course['max_attainment_date']
@Column(DataType.DATE)
declare min_attainment_date: Course['min_attainment_date']
@CreatedAt
@Column({ field: 'created_at', type: DataType.DATE })
declare createdAt: Course['createdAt']
@UpdatedAt
@Column({ field: 'updated_at', type: DataType.DATE })
declare updatedAt: Course['updatedAt']
@Column(DataType.JSONB)
declare substitutions: Course['substitutions']
@Column(DataType.JSONB)
declare substitution_groups: Course['substitution_groups']
@Column(DataType.STRING)
declare course_unit_type: Course['course_unit_type']
@Column({ field: 'main_course_code', type: DataType.STRING })
declare mainCourseCode: Course['mainCourseCode']
}
+14 -10
View File
@@ -6,21 +6,25 @@ import type { Enrollment } from './enrollment'
import type { Organization } from './organization'
export type Course = {
/* COLUMNS */
id: string
groupId: string
code: string
coursetypecode: string
isStudyModule: boolean
isPrimary: boolean
name: Name
maxAttainmentDate: Date | null
minAttainmentDate: Date | null
createdAt: Date
updatedAt: Date
substitutionGroups: string[][]
validityPeriod: { startDate?: Date; endDate?: Date } | null
courseUnitType: string
/* RELATIONS */
courseType: CourseType
credits: Credit[]
enrollments: Enrollment[]
organizations: Organization[]
is_study_module: boolean
name: Name
max_attainment_date: Date
min_attainment_date: Date
createdAt: Date
updatedAt: Date
substitutions: string[]
substitution_groups: string[][]
course_unit_type: string
mainCourseCode: string
}
@@ -0,0 +1,23 @@
const { BOOLEAN, JSONB, STRING, TEXT } = require('sequelize')
/**
* Add: groupId, validityPeriod, primary field
* Remove: mainCourseCode, old substitutions
* Additionally substitutionGroups now use the groupIds of course units, rather than the codes.
*/
module.exports = {
up: async queryInterface => {
await queryInterface.addColumn('course', 'group_id', STRING)
await queryInterface.addColumn('course', 'validity_period', JSONB)
await queryInterface.addColumn('course', 'is_primary', BOOLEAN)
await queryInterface.removeColumn('course', 'main_course_code')
await queryInterface.removeColumn('course', 'substitutions')
},
down: async queryInterface => {
await queryInterface.removeColumn('course', 'group_id')
await queryInterface.removeColumn('course', 'validity_period')
await queryInterface.removeColumn('course', 'is_primary')
await queryInterface.addColumn('course', 'main_course_code', TEXT)
await queryInterface.addColumn('course', 'substitutions', JSONB)
},
}
@@ -10,22 +10,28 @@ Course.init(
type: STRING,
primaryKey: true,
},
groupId: {
type: STRING,
},
code: {
type: STRING,
},
name: {
type: JSONB,
},
is_study_module: {
isStudyModule: {
type: BOOLEAN,
},
isPrimary: {
type: BOOLEAN,
},
coursetypecode: {
type: STRING,
},
max_attainment_date: {
maxAttainmentDate: {
type: DATE,
},
min_attainment_date: {
minAttainmentDate: {
type: DATE,
},
createdAt: {
@@ -36,22 +42,18 @@ Course.init(
field: 'updated_at',
type: DATE,
},
substitutions: {
substitutionGroups: {
type: JSONB,
},
substitution_groups: {
validityPeriod: {
type: JSONB,
},
course_unit_type: {
type: STRING,
},
mainCourseCode: {
field: 'main_course_code',
courseUnitType: {
type: STRING,
},
},
{
underscored: false,
underscored: true,
sequelize: dbConnections.sequelize,
modelName: 'course',
tableName: 'course',
@@ -236,14 +236,14 @@ export const courseProviderMapper =
shares,
})
export const courseMapper = courseIdToAttainments => (groupId, course, substitution_groups) => {
export const courseMapper = courseIdToAttainments => (groupId, course, substitutionGroups, primaryId) => {
const { id, code, name, study_level, course_unit_type, validity_period } = course
const courseAttainments = courseIdToAttainments[id] ?? []
// Attainments are sorted by attainment_date ASC
const min_attainment_date = courseAttainments[0]?.attainment_date ?? null
const max_attainment_date = courseAttainments[courseAttainments.length - 1]?.attainment_date ?? null
const minAttainmentDate = courseAttainments[0]?.attainment_date ?? null
const maxAttainmentDate = courseAttainments[courseAttainments.length - 1]?.attainment_date ?? null
return {
id,
@@ -251,12 +251,13 @@ export const courseMapper = courseIdToAttainments => (groupId, course, substitut
name,
code,
coursetypecode: study_level,
min_attainment_date,
max_attainment_date,
validity_period,
is_study_module: !course_unit_type, // Only course units have a course_unit_type. If null -> must be a study module
substitution_groups,
course_unit_type,
minAttainmentDate,
maxAttainmentDate,
validityPeriod: validity_period,
isStudyModule: !course_unit_type, // Only course units have a course_unit_type. If null -> must be a study module
isPrimary: primaryId === id,
substitutionGroups,
courseUnitType: course_unit_type,
}
}
@@ -29,6 +29,19 @@ const updateCourses = async (courseIdToAttainments, groupIdToCourse) => {
const mapCourse = courseMapper(courseIdToAttainments)
for (const [groupId, courses] of Object.entries(groupIdToCourse)) {
const now = new Date()
const { id: primaryCourseId } = courses.reduce(
(acc, cur) => {
const start = new Date(cur.validity_period.startDate)
if (start > acc.startDate && start < now) {
acc.id = cur.id
acc.startDate = start
}
return acc
},
{ startDate: new Date(0), id: courses[0].id }
)
for (const course of courses) {
/** @type string[][] @description nested arrays of courseUnit groupIds. Modules have no substitutions. */
const substitutionGroups = course.substitutions?.map(subGroup => subGroup.map(sub => sub.courseUnitGroupId)) ?? []
@@ -55,7 +68,7 @@ const updateCourses = async (courseIdToAttainments, groupIdToCourse) => {
const mapCourseProvider = courseProviderMapper(groupId)
courseProviders.push(...Object.values(organisationsById).map(mapCourseProvider))
mappedCourses.push(mapCourse(groupId, course, substitutionGroups))
mappedCourses.push(mapCourse(groupId, course, substitutionGroups, primaryCourseId))
}
}