Split kone_data to separate database entirely

This commit is contained in:
Rochet2
2019-07-02 15:59:16 +03:00
parent 0be37d6687
commit e265e9b006
40 changed files with 678 additions and 564 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
data
pgdata
pgdata_kone
user_pgdata
analytics_pgdata
usage_pgdata
@@ -26,4 +27,4 @@ services/updater_scheduler/active_student_numbers.txt
services/oodikone2-backend/debug.log
datastore
services/updater_api/debug.log
services/backend/updater_writer/dumps
services/backend/updater_writer/dumps
+1 -1
View File
@@ -6,6 +6,6 @@ require_once('plugins/login-servers.php');
* @param string
*/
return new AdminerLoginServers(
$servers = array("db" => "db", "user_db" => "user_db", "analytics_db" => "analytics_db"),
$servers = array("db" => "db", "db_kone" => "db_kone", "user_db" => "user_db", "analytics_db" => "analytics_db"),
$driver = "pgsql"
);
+15 -1
View File
@@ -84,6 +84,18 @@ services:
- POSTGRES_MULTIPLE_DATABASES=tkt_oodi,tkt_oodi_test,tkt_oodi_real
- POSTGRES_USER=postgres
db_kone:
image: postgres:9.6.3
ports:
- "5425:5432"
volumes:
- ./pgdata_kone:/var/lib/postgresql/data
- ./scripts/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d:ro
container_name: db_kone
environment:
- POSTGRES_MULTIPLE_DATABASES=db_kone
- POSTGRES_USER=postgres
redis:
image: redis
command: ["redis-server", "--appendonly", "yes"]
@@ -117,7 +129,9 @@ services:
WAIT_HOSTS: redis:6379,db:5432,nats:4222
REDIS: redis
DB_URL: postgres://postgres@db:5432/tkt_oodi
DB_URL_KONE: postgres://postgres@db_kone:5432/db_kone
TEST_DB: postgres://postgres@db:5432/tkt_oodi_test
TEST_DB_KONE: postgres://postgres@db_kone:5432/db_kone_test
FRONT_URL: http://localhost:8000
TOKEN_SECRET: IWannaBeTheVeryBest
OODILEARN_URL: http://oodilearn:5000
@@ -127,7 +141,6 @@ services:
USAGESERVICE_SECRET: Luukk4inenApprov3dVitunMinttuSalainenAvain666NurmivaaraPJ2019
ANALYTICS_URL: http://analytics:4568
NATS_URI: nats://nats:4222
DB_SCHEMA_KONE: kone_data
volumes:
- ./services/backend/oodikone2-backend/:/usr/src/app
- ./services/backend/shared/models:/usr/src/app/src/models
@@ -156,6 +169,7 @@ services:
USER_ADMINER_URL: http://localhost:5050/?pgsql=user_db&username=postgres
ANALYTICS_ADMINER_URL: http://localhost:5050/?pgsql=analytics_db&username=postgres
USAGE_ADMINER_URL: http://localhost:5050/?pgsql=usage_db&username=postgres
KONE_ADMINER_URL: http://localhost:5050/?pgsql=db_kone&username=postgres
volumes:
- ./services/oodikone2-frontend/:/usr/src/app
container_name: frontend
+10 -1
View File
@@ -5,6 +5,7 @@ ANONDB_DIR=anonyymioodi
BACKUP_DIR=backups
PSQL_DB_BACKUP="$ANONDB_DIR/anon.sqz"
USER_DB_BACKUP="$ANONDB_DIR/user-dump.sqz"
KONE_DB_BACKUP="$ANONDB_DIR/anon_kone.sqz"
PSQL_REAL_DB_BACKUP="$BACKUP_DIR/latest-pg.sqz"
USER_REAL_DB_BACKUP="$BACKUP_DIR/latest-user-pg.sqz"
@@ -70,6 +71,10 @@ restore_real_userdb_from_backup () {
time pg_restore -U postgres -h localhost -p 5422 --no-owner -F c --dbname=user_db_real -j4 $USER_REAL_DB_BACKUP
}
restore_dbkone_from_backup () {
time pg_restore -U postgres -h localhost -p 5425 --no-owner -F c --dbname=db_kone -j4 $KONE_DB_BACKUP
}
# oodilearn
# restore_mongodb_from_backup () {
# docker exec -t mongo_db mongorestore -d oodilearn "/dump"
@@ -97,6 +102,8 @@ db_anon_setup_full () {
ping_psql "oodi_db" "tkt_oodi"
ping_psql "oodi_db" "tkt_oodi_test"
retry restore_psql_from_backup
ping_psql "db_kone" "db_kone"
retry restore_dbkone_from_backup
# echo "Restoring MongoDB from backup"
# retry restore_mongodb_from_backup
echo "Restore user db from backup"
@@ -120,11 +127,13 @@ reset_real_db () {
reset_db () {
docker-compose down
docker-compose up -d db user_db
docker-compose up -d db user_db db_kone
ping_psql "oodi_db" "tkt_oodi"
docker exec -u postgres oodi_db dropdb "tkt_oodi"
ping_psql "oodi_db" "tkt_oodi_test"
docker exec -u postgres oodi_db dropdb "tkt_oodi_test"
ping_psql "db_kone" "db_kone"
docker exec -u postgres db_kone dropdb "db_kone"
ping_psql "oodi_user_db" "user_db"
docker exec -u postgres oodi_user_db dropdb "user_db"
db_anon_setup_full
@@ -1,19 +1,22 @@
require('dotenv').config()
const { NODE_ENV } = process.env
let DB_URL = process.env.DB_URL
const isTest = process.env.NODE_ENV === 'test'
let DB_URL = process.env.DB_URL
let DB_URL_KONE = process.env.DB_URL_KONE
if (isTest) {
DB_URL = process.env.TEST_DB
DB_URL = process.env.TEST_DB_KONE
}
else if (NODE_ENV === 'anon') {
DB_URL = process.env.ANON_DB
DB_URL = process.env.ANON_DB_KONE
}
const frontend_addr = process.env.FRONT_URL
const redis = process.env.REDIS
const TOKEN_SECRET = process.env.TOKEN_SECRET
const DB_SCHEMA = process.env.DB_SCHEMA || 'public'
const DB_SCHEMA_KONE = process.env.DB_SCHEMA_KONE || 'kone_data'
const DB_SCHEMA_KONE = process.env.DB_SCHEMA_KONE || 'public'
const CERT_PATH = process.env.CERT_PATH // production/staging only
const KEY_PATH = process.env.KEY_PATH // production/staging only
const OODILEARN_URL = process.env.OODILEARN_URL
@@ -58,10 +61,25 @@ if (process.env.NODE_ENV === 'dev' || isTest) {
}
module.exports = {
frontend_addr, DB_URL, redis, TOKEN_SECRET, DB_SCHEMA, DB_SCHEMA_KONE, OODI_ADDR, CERT_PATH, KEY_PATH, FEATURES, OODILEARN_URL,
USERSERVICE_URL: formatURL(USERSERVICE_URL), ACCESS_TOKEN_HEADER_KEY, PORT,
frontend_addr,
DB_URL,
DB_URL_KONE,
redis,
TOKEN_SECRET,
DB_SCHEMA,
DB_SCHEMA_KONE,
OODI_ADDR,
CERT_PATH,
KEY_PATH,
FEATURES,
OODILEARN_URL,
USERSERVICE_URL: formatURL(USERSERVICE_URL),
ACCESS_TOKEN_HEADER_KEY,
PORT,
ANALYTICS_URL: formatURL(ANALYTICS_URL),
USAGESERVICE_URL,
requiredGroup, OODI_SECRET, OODI_SECRET_HEADER_KEY,
requiredGroup,
OODI_SECRET,
OODI_SECRET_HEADER_KEY,
isTest
}
@@ -10,14 +10,13 @@ const sequelize = new Sequelize(conf.DB_URL, {
})
sequelize.query(`SET SESSION search_path to ${conf.DB_SCHEMA}`)
const sequelizeKone = new Sequelize(conf.DB_URL, {
const sequelizeKone = new Sequelize(conf.DB_URL_KONE, {
schema: conf.DB_SCHEMA_KONE,
searchPath: conf.DB_SCHEMA_KONE,
logging: false,
operatorsAliases: false
})
sequelizeKone.query(`SET SESSION search_path to ${conf.DB_SCHEMA_KONE}`)
// See https://github.com/sequelize/sequelize/issues/10875
const runMigrations = async () => {
try {
@@ -74,7 +73,8 @@ const runMigrationsKone = async () => {
}
}
const migrationPromise = !conf.isTest ? runMigrations().then(() => runMigrationsKone()) : Promise.resolve()
const migrationPromise = !conf.isTest ? runMigrations() : Promise.resolve()
const migrationPromiseKone = !conf.isTest ? runMigrationsKone() : Promise.resolve()
const forceSyncDatabase = async () => {
try {
@@ -95,5 +95,6 @@ module.exports = {
sequelize,
sequelizeKone,
migrationPromise,
migrationPromiseKone,
forceSyncDatabase
}
@@ -1,5 +1,6 @@
const fs = require('fs')
const { Migration, MigrationKone } = require('../models/index')
const { Migration } = require('../models/index')
const { MigrationKone } = require('../models/models_kone')
const DEFAULT_PATH = `${process.cwd()}/src/database/migrations`
const DEFAULT_PATH_KONE = `${process.cwd()}/src/database/migrations_kone`
@@ -73,6 +73,7 @@ const StartNats = () => {
e
)
}
console.error(e)
console.log(
`Failed to update throughput stats for code: ${code}, reason: ${
e.message
@@ -93,6 +94,7 @@ const StartNats = () => {
} catch (e) {
console.error(e)
}
console.error(e)
console.log(`Failed to update productivity stats for code: ${code}, reason: ${e.message}`)
}
ready += 1
@@ -89,6 +89,7 @@ router.get('/v2/studyprogrammes/productivity/recalculate', async (req, res) => {
console.error(e)
return
}
console.error(e)
console.log(`Failed to update productivity stats for code: ${code}, reason: ${e.message}`)
}
ready += 1
@@ -149,6 +150,7 @@ router.get('/v2/studyprogrammes/throughput/recalculate', async (req, res) => {
console.error(e)
return
}
console.error(e)
console.log(`Failed to update throughput stats for code: ${code}, reason: ${e.message}`)
}
ready += 1
@@ -1,5 +1,8 @@
const { redisClient } = require('../services/redis')
const { Teacher, CourseGroup } = require('../models')
const { Teacher } = require('../models')
const { CourseGroup, TeacherCourseGroup } = require('../models/models_kone')
const { Op } = require('sequelize')
const { flatMap } = require('lodash')
const {
getCurrentAcademicYear,
@@ -26,18 +29,27 @@ const getCurrentAcademicYearSemesterCode = async () => {
return academicYear[0].semestercode
}
const getTeachersForCourseGroup = (courseGroupId) => {
return Teacher.findAll({
attributes: ['name', 'code', 'id'],
const getTeachersForCourseGroup = async (courseGroupId) => {
const courseGroups = await CourseGroup.findAll({
model: CourseGroup,
include: {
model: CourseGroup,
attributes: [],
model: TeacherCourseGroup,
required: true,
where: {
id: courseGroupId
},
},
where: {
id: courseGroupId
},
})
const teacherIds = flatMap(courseGroups, e => e.teacher_course_groups.map(tcg => tcg.teacher_id))
const teachers = await Teacher.findAll({
attributes: ['name', 'code', 'id'],
where: {
id: {
[Op.in]: teacherIds
}
},
})
return teachers
}
const createCourseGroup = (name) => CourseGroup.create({ name })
@@ -141,17 +153,13 @@ const getCoursesByTeachers = async (teacherIds, semesterCode) => {
}
const addTeacher = async (courseGroupId, teacherid) => {
const group = await CourseGroup.findByPk(courseGroupId)
const teacher = await Teacher.findByPk(teacherid)
if (!group || !teacher) return
return await group.addTeacher(teacher)
if (!teacher) return
return TeacherCourseGroup.create({ teacher_id: teacherid, course_group_id: courseGroupId })
}
const removeTeacher = async (courseGroupId, teacherid) => {
const group = await CourseGroup.findByPk(courseGroupId)
const teacher = await Teacher.findByPk(teacherid)
if (!group || !teacher) return
return await group.removeTeacher(teacher)
const removeTeacher = (courseGroupId, teacherid) => {
return TeacherCourseGroup.destroy({ where: { teacher_id: teacherid, course_group_id: courseGroupId } })
}
module.exports = {
@@ -4,8 +4,11 @@ const {
sequelize, Student, Credit,
Course, CourseType, Discipline,
ElementDetails, StudyrightElement,
Studyright, Semester, CourseDuplicates,
Studyright, Semester,
} = require('../models')
const {
sequelizeKone, CourseDuplicates,
} = require('../models/models_kone')
const Op = Sequelize.Op
const { CourseYearlyStatsCounter } = require('../services/course_yearly_stats_counter')
const _ = require('lodash')
@@ -409,7 +412,7 @@ const deleteDuplicateCode = async (code) => {
coursecode: code
}
})
await sequelize.query(
await sequelizeKone.query(
`DELETE FROM course_duplicates
WHERE groupid in ( SELECT groupid FROM course_duplicates
group by groupid
@@ -421,12 +424,17 @@ const deleteDuplicateCode = async (code) => {
}
}
const getDuplicateCodesWithCourses = () => {
return CourseDuplicates.findAll({
include: {
model: Course,
const getDuplicateCodesWithCourses = async () => {
const courseDuplicates = await CourseDuplicates.findAll()
const courses = await Course.findAll({
where: {
code: {
[Op.in]: courseDuplicates.map(e => e.coursecode)
}
}
})
const codeToCourse = courses.reduce((acc, c) => { acc[c.code] = c; return acc }, {})
return courseDuplicates.map(cd => ({ ...cd.get(), course: codeToCourse[cd.coursecode] }))
}
const getDuplicatesToIdMap = () => {
@@ -1,5 +1,5 @@
const Sequelize = require('sequelize')
const { Filters } = require('../models')
const { Filters } = require('../models/models_kone')
const Op = Sequelize.Op
@@ -1,10 +1,7 @@
const Sequelize = require('sequelize')
const _ = require('lodash')
const { MandatoryCourseLabels } = require('../models')
const { MandatoryCourseLabels, sequelizeKone } = require('../models/models_kone')
const { Op } = Sequelize
const {
sequelize
} = require('../models/index')
const create = async (studyprogramme_id, { label }) => {
const labels = await labelsByStudyprogramme(studyprogramme_id)
@@ -23,7 +20,7 @@ const move = async (studyprogramme_id, { id }, direction) => {
if (indexToSwap < 0 || indexToSwap >= orderedlabels.length) return null;
[orderedlabels[indexToMove], orderedlabels[indexToSwap]] = [orderedlabels[indexToSwap], orderedlabels[indexToMove]]
const newOrder = orderedlabels.map((e, index) => ({ ...e, orderNumber: index }))
await sequelize.transaction(async transaction => {
await sequelizeKone.transaction(async transaction => {
for (let e of newOrder) {
await MandatoryCourseLabels.update(
{orderNumber: e.orderNumber}, { where: { studyprogramme_id, id: e.id }, transaction }
@@ -1,15 +1,12 @@
const Sequelize = require('sequelize')
const { Course, MandatoryCourse, MandatoryCourseLabels } = require('../models')
const { Course } = require('../models')
const { MandatoryCourse, MandatoryCourseLabels } = require('../models/models_kone')
const { Op } = Sequelize
const byStudyprogramme = (studyProgrammeId) => {
const courses = MandatoryCourse.findAll({
const byStudyprogramme = async (studyProgrammeId) => {
const mandatoryCourses = await MandatoryCourse.findAll({
attributes: ['course_code'],
include: [
{
model: Course,
attributes: ['name'],
},
{
model: MandatoryCourseLabels,
attributes: ['id', 'label', 'orderNumber'],
@@ -22,10 +19,20 @@ const byStudyprogramme = (studyProgrammeId) => {
}
})
return courses.map(course => ({
name: course.course.name,
code: course.course_code,
label: course.mandatory_course_label
const courses = await Course.findAll({
model: Course,
where: {
code: {
[Op.in]: mandatoryCourses.map(c => c.course_code)
}
}
})
const courseCodeToCourse = courses.reduce((acc, c) => { acc[c.code] = c; return acc }, {})
return mandatoryCourses.map(mc => ({
name: courseCodeToCourse[mc.course_code].name,
code: mc.course_code,
label: mc.mandatory_course_label
}))
}
@@ -36,14 +43,10 @@ const create = (studyProgrammeId, code) => {
})
}
const find = (studyProgrammeId, code) => {
return MandatoryCourse.findOne({
const find = async (studyProgrammeId, code) => {
const mandatoryCourse = await MandatoryCourse.findOne({
attributes: ['course_code', 'label'],
include: [
{
model: Course,
attributes: ['name'],
},
{
model: MandatoryCourseLabels,
attributes: ['id', 'label', 'orderNumber'],
@@ -58,6 +61,14 @@ const find = (studyProgrammeId, code) => {
}
}
})
const course = await Course.findOne({
where: {
code: mandatoryCourse.course_code
}
})
return { ...mandatoryCourse.get(), course: course.get() }
}
const remove = (studyProgrammeId, code) => {
@@ -3,9 +3,11 @@ const moment = require('moment')
const { orderBy } = require('lodash')
const {
Student, Credit, Course, sequelize, Studyright, StudyrightExtent, ElementDetails,
Discipline, CourseType, SemesterEnrollment, Semester, Transfers, StudyrightElement,
Tag, TagStudent
Discipline, CourseType, SemesterEnrollment, Semester, Transfers, StudyrightElement
} = require('../models')
const {
Tag, TagStudent
} = require('../models/models_kone')
const { getMainCodesMap, byName } = require('./courses')
const { CourseStatsCounter } = require('./course_stats_counter')
const { getPassingSemester, semesterEnd, semesterStart } = require('../util/semester')
@@ -214,17 +216,6 @@ const getStudentsIncludeCoursesBetween = async (studentnumbers, startDate, endDa
}
}
},
{
model: TagStudent,
attributes: ['id'],
include: [
{
model: Tag,
attributes: ['tag_id', 'tagname'],
}
],
}
],
where: {
studentnumber: {
@@ -233,8 +224,25 @@ const getStudentsIncludeCoursesBetween = async (studentnumbers, startDate, endDa
},
})
if (tag) {
const studentTags = await TagStudent.findAll({
attributes: ['id', 'studentnumber'],
include: [
{
model: Tag,
attributes: ['tag_id', 'tagname'],
}
],
})
const studentNumberToTags = studentTags.reduce((acc, t) => {
acc[t.studentnumber] = acc[t.studentnumber] || []
acc[t.studentnumber].push(t)
return acc
}, {})
const studentsWithSearchedTag = {}
students.forEach(student => {
student.tag_students = studentNumberToTags[student.studentnumber]
if (student.tag_students.some(t => t.tag.tag_id === tag)) {
studentsWithSearchedTag[student.studentnumber] = true
}
@@ -376,18 +384,18 @@ const formatStudentsForApi = async (students, startDate, endDate, { studyRights
stats.students.push(formatStudentForPopulationStatistics(student, startDate, endDate))
return stats
}, {
students: [],
extents: {},
semesters: {},
transfers: {
targets: {},
sources: {}
},
studyrights: {
degrees: [],
programmes: []
}
})
students: [],
extents: {},
semesters: {},
transfers: {
targets: {},
sources: {}
},
studyrights: {
degrees: [],
programmes: []
}
})
const transferredStudyright = (s) => {
const studyright = s.studyrights.find(s => s.studyrightElements
@@ -434,8 +442,13 @@ const optimizedStatisticsOf = async (query) => {
await studentnumbersWithAllStudyrightElements(
studyRights, startDate, endDate, exchangeStudents, cancelledStudents, nondegreeStudents
)
const students =
await getStudentsIncludeCoursesBetween(studentnumbers, startDate, dateMonthsFromNow(startDate, months), studyRights, query.tag)
const students = await getStudentsIncludeCoursesBetween(
studentnumbers,
startDate,
dateMonthsFromNow(startDate, months),
studyRights,
query.tag
)
const formattedStudents = await formatStudentsForApi(students, startDate, endDate, query)
return formattedStudents
@@ -3,8 +3,11 @@ const { Op } = sequelize
const moment = require('moment')
const { flatMap } = require('lodash')
const { Credit, Student, Course, Provider, Studyright, StudyrightElement,
ElementDetails, ThesisCourse, ThesisTypeEnums
ElementDetails
} = require('../models')
const {
ThesisCourse, ThesisTypeEnums
} = require('../models/models_kone')
const { studentnumbersWithAllStudyrightElements } = require('./populations')
const { semesterStart, semesterEnd } = require('../util/semester')
const isNumber = str => !Number.isNaN(Number(str))
@@ -170,30 +173,39 @@ const graduatedStatsForStudytrack = async (studytrack, since) => {
return graduatedStatsFromStudyrights(studyrights)
}
const findProgrammeThesisCredits = code => Credit.findAll({
include: {
model: Course,
required: true,
const findProgrammeThesisCredits = async code => {
const thesiscourses = await ThesisCourse.findAll({
model: ThesisCourse,
where: {
programmeCode: code
}
})
const courseCodeToThesisCourse = thesiscourses.reduce((acc, tc) => { acc[tc.courseCode] = tc; return acc }, {})
const credits = await Credit.findAll({
include: {
model: ThesisCourse,
model: Course,
required: true,
where: {
programmeCode: code
code: {
[Op.in]: thesiscourses.map(tc => tc.courseCode)
}
}
},
where: {
credittypecode: {
[Op.ne]: 10
}
}
},
where: {
credittypecode: {
[Op.ne]: 10
}
}
}).map(credit => {
const { id, course, attainment_date } = credit
const { code, thesis_courses } = course
const { thesisType: type } = thesis_courses[0]
const year = attainment_date && attainment_date.getFullYear()
return { id, code, type, year }
})
})
return credits.map(credit => {
const { id, course, attainment_date } = credit
const { code } = course
const year = attainment_date && attainment_date.getFullYear()
return { id, code, type: courseCodeToThesisCourse[code].thesisType, year }
})
}
const thesisProductivityFromCredits = credits => {
const stats = {}
@@ -290,16 +302,18 @@ const creditsAfter = (studentnumbers, startDate) => {
}
const thesesFromClass = async (studentnumbers, startDate, code) => {
return Credit.findAll({
const thesiscourses = await ThesisCourse.findAll({
where: {
programmeCode: code
}
})
const credits = await Credit.findAll({
include: {
model: Course,
required: true,
include: {
attributes: ['thesisType'],
model: ThesisCourse,
required: true,
where: {
programmeCode: code
where: {
code: {
[Op.in]: thesiscourses.map(tc => tc.courseCode)
}
}
},
@@ -314,12 +328,16 @@ const thesesFromClass = async (studentnumbers, startDate, code) => {
[Op.in]: studentnumbers
}
}
}).reduce((acc, curr) => {
curr.course.thesis_courses.map(th => {
acc[th.thesisType] = acc[th.thesisType] ? acc[th.thesisType] + 1 : 1
})
})
const courseCodeToThesisCourse = thesiscourses.reduce((acc, tc) => { acc[tc.courseCode] = tc; return acc }, {})
const courseCodes = credits.map(c => c.course.code)
const theses = courseCodes.reduce((acc, code) => {
const thesisType = courseCodeToThesisCourse[code].thesisType
acc[thesisType] = acc[thesisType] ? acc[thesisType] + 1 : 1
return acc
}, {})
return theses
}
const graduationsFromClass = async (studentnumbers, studytrack) => {
@@ -11,7 +11,8 @@ const {
thesisProductivityFromCredits,
thesisProductivityForStudytrack
} = require('./studytrack')
const { sequelize, ThesisTypeEnums } = require('../models')
const { sequelize } = require('../models')
const { ThesisTypeEnums } = require('../models/models_kone')
const { readFileSync } = require('fs')
const { MASTER, BACHELOR } = ThesisTypeEnums
@@ -1,5 +1,5 @@
const Sequelize = require('sequelize')
const { Tag } = require('../models')
const { Tag } = require('../models/models_kone')
const Op = Sequelize.Op
@@ -1,5 +1,5 @@
const Sequelize = require('sequelize')
const { TagStudent, Tag } = require('../models')
const { TagStudent, Tag } = require('../models/models_kone')
const Op = Sequelize.Op
@@ -1,5 +1,5 @@
const { Op } = require('sequelize')
const { Teacher, Credit, Course, Semester, Provider, CourseGroup } = require('../models/index')
const { Teacher, Credit, Course, Semester, Provider } = require('../models/index')
const splitByEmptySpace = str => str.replace(/\s\s+/g, ' ').split(' ')
@@ -40,11 +40,6 @@ const bySearchTerm = async (rawTerm) => {
codeLike(terms),
matchesId(searchTerm)
]
},
include: {
model: CourseGroup,
attributes: ['id', 'name'],
required: false
}
})
}
@@ -93,7 +88,7 @@ const markCredit = (stats, passed, failed, credits, transferred) => {
...stats,
credits: transferred ? stats.credits : stats.credits + credits,
passed: stats.passed + 1,
transferred: transferred ? stats.transferred + credits : stats.transferred
transferred: transferred ? stats.transferred + credits : stats.transferred
}
} else if (failed) {
return {
@@ -1,4 +1,4 @@
const { ThesisCourse } = require('../models/index')
const { ThesisCourse } = require('../models/models_kone')
const createThesisCourse = (programme, course, type) => ThesisCourse.create({
programmeCode: programme,
@@ -1,5 +1,6 @@
const { forceSyncDatabase } = require('../database/connection')
const { Course, ElementDetails, ThesisTypeEnums, ThesisCourse } = require('../models')
const { Course, ElementDetails } = require('../models')
const { ThesisTypeEnums, ThesisCourse } = require('../models/models_kone')
const { createThesisCourse, deleteThesisCourse, findProgrammeTheses } = require('./thesis')
const courseOne = {
@@ -1,6 +1,6 @@
const _ = require('lodash')
const moment = require('moment')
const { UsageStatistic } = require('../models')
const { UsageStatistic } = require('../models/models_kone')
const Sequelize = require('sequelize')
const { Op } = Sequelize
@@ -1,6 +1,6 @@
const Transport = require('winston-transport')
const uuid = require('uuid')
const { UsageStatistic } = require('../models')
const { UsageStatistic } = require('../models/models_kone')
module.exports = class LogSaverTransport extends Transport {
log(payload, callback) {
@@ -16,7 +16,7 @@ module.exports = class LogSaverTransport extends Transport {
status: payload.status,
data: payload
}
UsageStatistic.create(object)
}
callback()
@@ -2,7 +2,7 @@ const test = require('ava')
const supertest = require('supertest')
const app = require('../../src/app')
const api = supertest(app)
const { sequelize } = require('../../src/models')
const { sequelizeKone } = require('../../src/models/models_kone')
const conf = require('../../src/conf-backend')
const jwt = require('jsonwebtoken')
@@ -22,9 +22,9 @@ test.before(async () => {
expiresIn: '24h'
})
//console.log(token)
sequelize.query(
`DELETE
FROM tag_student
sequelizeKone.query(
`DELETE
FROM tag_student
WHERE tags_tagname = 'test3'`
)
})
@@ -0,0 +1,10 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(async transaction => {
await queryInterface.dropTable('course_duplicates', { transaction })
await queryInterface.dropTable('thesis_courses', { transaction })
})
},
down: async () => {
}
}
@@ -1,10 +0,0 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
return Sequelize.transaction(async transaction => {
await queryInterface.renameTable('teacher_course_group', 'teacher_course_groups', { transaction })
})
},
down: async () => {
}
}
@@ -1,57 +0,0 @@
const conf = require('../../conf-backend')
module.exports = {
up: async (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(async transaction => {
await queryInterface.createTable(
'mandatory_course_labels',
{
studyprogramme_id: {
type: Sequelize.STRING
},
id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
label: {
type: Sequelize.STRING
},
orderNumber: {
type: Sequelize.INTEGER
},
createdAt: {
type: Sequelize.DATE
},
updatedAt: {
type: Sequelize.DATE
}
},
{ transaction }
)
await queryInterface.addIndex('mandatory_course_labels', ['studyprogramme_id'], { transaction })
// Temporarily hardcode schema here since sequelize has issues:
// https://github.com/sequelize/sequelize/issues/10875
await queryInterface.removeColumn({ tableName: 'mandatory_courses', schema: conf.DB_SCHEMA_KONE },
'label', { transaction, }
)
await queryInterface.addColumn(
{ tableName: 'mandatory_courses', schema: 'kone_data' },
'label',
{
type: Sequelize.BIGINT,
references: {
model: 'mandatory_course_labels',
key: 'id'
},
onDelete: 'SET NULL',
onUpdate: 'CASCADE'
},
{ transaction }
)
})
},
down: async () => {
}
}
@@ -1,8 +0,0 @@
module.exports = {
up: async (queryInterface) => {
await queryInterface.dropTable('tag')
await queryInterface.dropTable('tag_student')
},
down: async () => {
}
}
@@ -1,26 +0,0 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('tag', {
createdAt: {
type: Sequelize.DATE
},
updatedAt: {
type: Sequelize.DATE
},
tagname: {
type: Sequelize.STRING,
},
tag_id: {
type: Sequelize.BIGINT,
autoIncrement: true,
primaryKey: true
},
studytrack: {
type: Sequelize.STRING,
primaryKey: true,
}
})
},
down: async () => {
}
}
@@ -1,8 +0,0 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn({ tableName: 'tag', schema: 'kone_data' }, 'tag_id')
await queryInterface.addColumn({ tableName: 'tag', schema: 'kone_data' }, 'tag_id', { type: Sequelize.BIGINT, primaryKey: true, allowNull: false, autoIncrement: true, unique:true })
},
down: async () => {
}
}
@@ -1,25 +0,0 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('tag_student', {
createdAt: {
type: Sequelize.DATE
},
updatedAt: {
type: Sequelize.DATE
},
tag_id: {
type: Sequelize.BIGINT,
primaryKey: true,
reference: {
model: 'tag',
key: 'tag_id'
}
},
studentnumber: {
type: Sequelize.STRING
}
})
},
down: async () => {
}
}
@@ -1,17 +0,0 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn({ tableName: 'tag_student', schema: 'kone_data' }, 'tag_id')
await queryInterface.addColumn({ tableName: 'tag_student', schema: 'kone_data' }, 'id', { type: Sequelize.BIGINT, primaryKey: true, allowNull: false, autoIncrement: true })
await queryInterface.sequelize.query(`ALTER TABLE tag_student ADD PRIMARY KEY (id);`)
await queryInterface.addColumn({ tableName: 'tag_student', schema: 'kone_data' }, 'tag_id', {
type: Sequelize.BIGINT,
allowNull: false,
references: {
model: 'tag',
key: 'tag_id'
},
})
},
down: async () => {
}
}
@@ -0,0 +1,135 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.sequelize.query(`
-- Adminer 4.6.2 PostgreSQL dump
CREATE TABLE "public"."course_duplicates" (
"groupid" integer NOT NULL,
"coursecode" character varying(255) NOT NULL,
"createdAt" timestamptz,
"updatedAt" timestamptz,
CONSTRAINT "course_duplicates_pkey" PRIMARY KEY ("coursecode")
) WITH (oids = false);
CREATE TYPE enum_thesis_courses_thesisType AS ENUM ('MASTER', 'BACHELOR');
CREATE TABLE "public"."thesis_courses" (
"programmeCode" character varying(255) NOT NULL,
"courseCode" character varying(255) NOT NULL,
"thesisType" enum_thesis_courses_thesisType,
"createdAt" timestamptz,
"updatedAt" timestamptz,
CONSTRAINT "thesis_courses_pkey" PRIMARY KEY ("programmeCode", "courseCode")
) WITH (oids = false);
-- 2019-07-01 15:28:04.971883+00
-- Adminer 4.6.2 PostgreSQL dump
CREATE SEQUENCE course_groups_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE "public"."course_groups" (
"id" bigint DEFAULT nextval('course_groups_id_seq') NOT NULL,
"name" character varying(255),
"createdAt" timestamptz,
"updatedAt" timestamptz,
"programmeid" character varying(255),
CONSTRAINT "course_groups_name_key" UNIQUE ("name"),
CONSTRAINT "course_groups_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE TABLE "public"."filters" (
"id" character varying(255) NOT NULL,
"name" character varying(255),
"filters" jsonb,
"population" jsonb,
"createdAt" timestamptz NOT NULL,
"updatedAt" timestamptz NOT NULL,
"description" character varying(255) DEFAULT '',
CONSTRAINT "filters_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE SEQUENCE mandatory_course_labels_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE "public"."mandatory_course_labels" (
"studyprogramme_id" character varying(255),
"id" bigint DEFAULT nextval('mandatory_course_labels_id_seq') NOT NULL,
"label" character varying(255),
"orderNumber" integer,
"createdAt" timestamptz,
"updatedAt" timestamptz,
CONSTRAINT "mandatory_course_labels_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE INDEX "mandatory_course_labels_studyprogramme_id" ON "public"."mandatory_course_labels" USING btree ("studyprogramme_id");
CREATE SEQUENCE mandatory_courses_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE "public"."mandatory_courses" (
"id" bigint DEFAULT nextval('mandatory_courses_id_seq') NOT NULL,
"course_code" character varying(255),
"studyprogramme_id" character varying(255),
"createdAt" timestamptz,
"updatedAt" timestamptz,
"label" bigint,
CONSTRAINT "mandatory_courses_pkey" PRIMARY KEY ("id"),
CONSTRAINT "mandatory_courses_label_fkey" FOREIGN KEY (label) REFERENCES mandatory_course_labels(id) ON UPDATE CASCADE ON DELETE SET NULL NOT DEFERRABLE
) WITH (oids = false);
CREATE SEQUENCE tag_tag_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE "public"."tag" (
"createdAt" timestamptz,
"updatedAt" timestamptz,
"tagname" character varying(255),
"studytrack" character varying(255) NOT NULL,
"tag_id" bigint DEFAULT nextval('tag_tag_id_seq') NOT NULL,
CONSTRAINT "tag_tag_id_key" UNIQUE ("tag_id")
) WITH (oids = false);
CREATE SEQUENCE tag_student_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE "public"."tag_student" (
"createdAt" timestamptz,
"updatedAt" timestamptz,
"studentnumber" character varying(255),
"id" bigint DEFAULT nextval('tag_student_id_seq') NOT NULL,
"tag_id" bigint NOT NULL,
CONSTRAINT "tag_student_pkey" PRIMARY KEY ("id"),
CONSTRAINT "tag_student_tag_id_fkey" FOREIGN KEY (tag_id) REFERENCES tag(tag_id) NOT DEFERRABLE
) WITH (oids = false);
CREATE SEQUENCE teacher_course_group_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1;
CREATE TABLE "public"."teacher_course_groups" (
"id" bigint DEFAULT nextval('teacher_course_group_id_seq') NOT NULL,
"teacher_id" character varying(255),
"course_group_id" bigint,
"createdAt" timestamptz,
"updatedAt" timestamptz,
CONSTRAINT "teacher_course_group_pkey" PRIMARY KEY ("id")
) WITH (oids = false);
CREATE TABLE "public"."unit_tag" (
"tags_tagname" character varying(255) NOT NULL,
"unit_id" bigint NOT NULL,
CONSTRAINT "unit_tag_pkey" PRIMARY KEY ("tags_tagname", "unit_id")
) WITH (oids = false);
-- 2019-07-01 15:26:14.147541+00
`)
},
down: async () => {
}
}
+5 -257
View File
@@ -1,186 +1,5 @@
const Sequelize = require('sequelize')
const { sequelize, sequelizeKone, migrationPromise } = require('../database/connection')
const conf = require('../conf-backend')
const ThesisTypeEnums = {
MASTER: 'MASTER',
BACHELOR: 'BACHELOR'
}
const CourseGroup = sequelize.define('course_groups', {
id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
unique: true
}
},
{
schema: conf.DB_SCHEMA_KONE
})
const TeacherCourseGroup = sequelize.define('teacher_course_groups', {
course_group_id: {
type: Sequelize.BIGINT,
},
teacher_id: {
type: Sequelize.STRING,
}
},
{
schema: conf.DB_SCHEMA_KONE
})
const MandatoryCourse = sequelize.define('mandatory_courses', {
course_code: {
type: Sequelize.STRING,
},
studyprogramme_id: {
type: Sequelize.STRING
},
label: {
type: Sequelize.BIGINT
}
},
{
schema: conf.DB_SCHEMA_KONE
})
const Filters = sequelize.define('filters',
{
id: {
type: Sequelize.STRING,
primaryKey: true
},
name: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
filters: {
type: Sequelize.JSONB
},
population: {
type: Sequelize.JSONB
}
},
{
schema: conf.DB_SCHEMA_KONE
}
)
const UsageStatistic = sequelize.define('usage_statistics', {
id: {
primaryKey: true,
type: Sequelize.STRING
},
username: {
type: Sequelize.STRING
},
name: {
type: Sequelize.STRING
},
time: {
type: Sequelize.INTEGER
},
admin: {
type: Sequelize.BOOLEAN
},
method: {
type: Sequelize.STRING
},
URL: {
type: Sequelize.STRING
},
status: {
type: Sequelize.INTEGER
},
data: {
type: Sequelize.JSONB
},
}, {
timestamps: false,
schema: conf.DB_SCHEMA_KONE
})
const TagStudent = sequelize.define('tag_student',
{
id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
studentnumber: {
type: Sequelize.STRING,
},
tag_id: {
type: Sequelize.BIGINT,
},
},
{
tableName: 'tag_student',
schema: conf.DB_SCHEMA_KONE
}
)
const Tag = sequelize.define('tag',
{
tagname: {
type: Sequelize.STRING
},
tag_id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
studytrack: {
primaryKey: true,
type: Sequelize.STRING
}
},
{
tableName: 'tag',
schema: conf.DB_SCHEMA_KONE
}
)
const MandatoryCourseLabels = sequelize.define('mandatory_course_labels',
{
id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
studyprogramme_id: {
type: Sequelize.STRING,
},
label: {
type: Sequelize.STRING
},
orderNumber: {
type: Sequelize.INTEGER,
},
},
{
schema: conf.DB_SCHEMA_KONE
}
)
const MigrationKone = sequelize.define('migrationsKone', {
name: {
type: Sequelize.STRING,
primaryKey: true
}
}, {
tableName: 'migrations',
timestamps: false,
schema: conf.DB_SCHEMA_KONE
})
const { sequelize, migrationPromise } = require('../database/connection')
const Student = sequelize.define('student',
{
@@ -602,42 +421,6 @@ const CreditTeacher = sequelize.define('credit_teacher', {
}
})
const ThesisCourse = sequelize.define('thesis_courses', {
programmeCode: {
primaryKey: true,
type: Sequelize.STRING,
references: {
model: ElementDetails,
key: 'code'
}
},
courseCode: {
primaryKey: true,
type: Sequelize.STRING,
references: {
model: Course,
key: 'code'
}
},
thesisType: {
type: Sequelize.ENUM([ThesisTypeEnums.BACHELOR, ThesisTypeEnums.MASTER])
}
})
const CourseDuplicates = sequelize.define('course_duplicates', {
groupid: {
primaryKey: true,
type: Sequelize.INTEGER
},
coursecode: {
type: Sequelize.STRING,
references: {
model: Course,
key: 'code'
}
}
})
const ErrorData = sequelize.define('error_data', {
id: {
primaryKey: true,
@@ -654,17 +437,6 @@ Student.hasMany(Credit, { foreignKey: 'student_studentnumber', sourceKey: 'stude
Credit.belongsTo(Course, { foreignKey: 'course_code' })
Course.hasMany(Credit, { foreignKey: 'course_code' })
MandatoryCourse.belongsTo(Course, { foreignKey: 'course_code' })
MandatoryCourse.belongsTo(MandatoryCourseLabels, { foreignKey: 'label', sourceKey: 'id' })
MandatoryCourseLabels.hasMany(MandatoryCourse, { foreignKey: 'label', sourceKey: 'id' })
TagStudent.belongsTo(Tag, { foreignKey: 'tag_id', sourceKey: 'tag_id' })
TagStudent.belongsTo(Student, { foreignKey: 'studentnumber' })
Student.hasMany(TagStudent, { foreignKey: 'studentnumber', sourceKey: 'studentnumber' })
Tag.hasOne(ElementDetails, { foreignKey: 'code', sourceKey: 'studytrack' })
Tag.hasMany(TagStudent, { foreignKey: 'tag_id', sourceKey: 'tag_id' })
Studyright.belongsTo(Student, { foreignKey: 'student_studentnumber', targetKey: 'studentnumber' })
Student.hasMany(Studyright, { foreignKey: 'student_studentnumber', sourceKey: 'studentnumber' })
@@ -715,37 +487,18 @@ Student.belongsToMany(CourseRealisation, { through: CourseEnrollment, foreignKey
Credit.belongsToMany(Teacher, { through: CreditTeacher, foreignKey: 'credit_id' })
Teacher.belongsToMany(Credit, { through: CreditTeacher, foreignKey: 'teacher_id' })
Teacher.belongsToMany(CourseGroup, { through: TeacherCourseGroup, foreignKey: 'teacher_id' })
CourseGroup.belongsToMany(Teacher, { through: TeacherCourseGroup, foreignKey: 'course_group_id' })
CourseGroup.belongsTo(ElementDetails, { foreignKey: 'programmeid' })
Credit.belongsTo(Semester, { foreignKey: { name: 'semestercode', allowNull: false } })
Course.hasMany(ThesisCourse, { foreignKey: 'courseCode', sourceKey: 'code' })
ThesisCourse.belongsTo(Course, { foreignKey: 'courseCode', targetKey: 'code' })
ElementDetails.hasMany(ThesisCourse, { foreignKey: 'programmeCode', sourceKey: 'code' })
ThesisCourse.belongsTo(ElementDetails, { foreignKey: 'programmeCode', targetKey: 'code' })
CourseDuplicates.belongsTo(Course, { foreignKey: 'coursecode' })
module.exports = {
MandatoryCourse,
Student,
Credit,
Studyright,
Course,
TagStudent,
Tag,
Teacher,
sequelize,
sequelizeKone,
migrationPromise,
Organisation,
StudentList,
StudyrightElement,
ElementDetails,
Filters,
StudyrightExtent,
CourseType,
CreditType,
@@ -759,14 +512,9 @@ module.exports = {
CourseRealisationType,
CourseRealisation,
CourseEnrollment,
Migration,
MigrationKone,
CreditTeacher,
UsageStatistic,
CourseGroup,
ThesisCourse,
CourseDuplicates,
ThesisTypeEnums,
MandatoryCourseLabels,
ErrorData
ErrorData,
Migration,
migrationPromise,
sequelize
}
@@ -0,0 +1,239 @@
const Sequelize = require('sequelize')
const {
sequelizeKone,
migrationPromiseKone
} = require('../database/connection')
const ThesisTypeEnums = {
MASTER: 'MASTER',
BACHELOR: 'BACHELOR'
}
const CourseGroup = sequelizeKone.define(
'course_groups',
{
id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
name: {
type: Sequelize.STRING,
unique: true
},
programmeid: {
type: Sequelize.STRING,
}
}
)
const TeacherCourseGroup = sequelizeKone.define('teacher_course_groups', {
course_group_id: {
type: Sequelize.BIGINT
},
teacher_id: {
type: Sequelize.STRING
}
})
const MandatoryCourse = sequelizeKone.define('mandatory_courses', {
course_code: {
type: Sequelize.STRING
},
studyprogramme_id: {
type: Sequelize.STRING
},
label: {
type: Sequelize.BIGINT
}
})
const Filters = sequelizeKone.define('filters', {
id: {
type: Sequelize.STRING,
primaryKey: true
},
name: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
filters: {
type: Sequelize.JSONB
},
population: {
type: Sequelize.JSONB
}
})
const UsageStatistic = sequelizeKone.define(
'usage_statistics',
{
id: {
primaryKey: true,
type: Sequelize.STRING
},
username: {
type: Sequelize.STRING
},
name: {
type: Sequelize.STRING
},
time: {
type: Sequelize.INTEGER
},
admin: {
type: Sequelize.BOOLEAN
},
method: {
type: Sequelize.STRING
},
URL: {
type: Sequelize.STRING
},
status: {
type: Sequelize.INTEGER
},
data: {
type: Sequelize.JSONB
}
},
{
timestamps: false
}
)
const TagStudent = sequelizeKone.define(
'tag_student',
{
id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
studentnumber: {
type: Sequelize.STRING
},
tag_id: {
type: Sequelize.BIGINT
}
},
{
tableName: 'tag_student'
}
)
const Tag = sequelizeKone.define(
'tag',
{
tagname: {
type: Sequelize.STRING
},
tag_id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
studytrack: {
primaryKey: true,
type: Sequelize.STRING
}
},
{
tableName: 'tag'
}
)
const MandatoryCourseLabels = sequelizeKone.define('mandatory_course_labels', {
id: {
primaryKey: true,
type: Sequelize.BIGINT,
autoIncrement: true
},
studyprogramme_id: {
type: Sequelize.STRING
},
label: {
type: Sequelize.STRING
},
orderNumber: {
type: Sequelize.INTEGER
}
})
const MigrationKone = sequelizeKone.define(
'migrations',
{
name: {
type: Sequelize.STRING,
primaryKey: true
}
},
{
tableName: 'migrations',
timestamps: false
}
)
const CourseDuplicates = sequelizeKone.define('course_duplicates', {
groupid: {
primaryKey: true,
type: Sequelize.INTEGER
},
coursecode: {
type: Sequelize.STRING,
}
})
const ThesisCourse = sequelizeKone.define('thesis_courses', {
programmeCode: {
primaryKey: true,
type: Sequelize.STRING
},
courseCode: {
primaryKey: true,
type: Sequelize.STRING
},
thesisType: {
type: Sequelize.ENUM([ThesisTypeEnums.BACHELOR, ThesisTypeEnums.MASTER])
}
})
MandatoryCourse.belongsTo(MandatoryCourseLabels, {
foreignKey: 'label',
sourceKey: 'id'
})
MandatoryCourseLabels.hasMany(MandatoryCourse, {
foreignKey: 'label',
sourceKey: 'id'
})
TeacherCourseGroup.belongsTo(CourseGroup, {
foreignKey: 'course_group_id',
sourceKey: 'id'
})
CourseGroup.hasMany(TeacherCourseGroup, {
foreignKey: 'course_group_id',
sourceKey: 'id'
})
TagStudent.belongsTo(Tag, { foreignKey: 'tag_id', sourceKey: 'tag_id' })
Tag.hasMany(TagStudent, { foreignKey: 'tag_id', sourceKey: 'tag_id' })
module.exports = {
TeacherCourseGroup,
MandatoryCourse,
TagStudent,
Tag,
Filters,
UsageStatistic,
CourseGroup,
CourseDuplicates,
ThesisCourse,
ThesisTypeEnums,
MandatoryCourseLabels,
MigrationKone,
migrationPromiseKone,
sequelizeKone
}
+40 -17
View File
@@ -1,4 +1,4 @@
const { sequelize } = require('../database/connection')
const { sequelize, sequelizeKone } = require('../database/connection')
const conf = require('../conf-backend')
const getStudentNumbers = () => sequelize.query(
@@ -111,29 +111,52 @@ const getAcademicYearCoursesByTeacherIds = (teacherIds, startSemester) => {
const getAcademicYearStatisticsForStudyProgramme = async (programmeid, startSemester) => {
const endSemester = startSemester + 1
const result = await sequelize.query(
const courseGroupData = await sequelizeKone.query(
`
SELECT cg.id AS id,cg.name AS name,
COUNT(DISTINCT c.course_code) AS courses,
COALESCE(SUM(credits), 0) AS credits,
COUNT(DISTINCT student_studentnumber) AS students
FROM ${conf.DB_SCHEMA_KONE}.course_groups cg
left join ${conf.DB_SCHEMA_KONE}.teacher_course_groups tcg
SELECT cg.id AS id, cg.name AS name, tcg.teacher_id as teacher_id, cg.programmeid as programmeid
FROM course_groups cg
left join teacher_course_groups tcg
ON tcg.course_group_id = cg.id
left join credit_teachers ct
ON ct.teacher_id = tcg.teacher_id
left join credit c
ON c.id = ct.credit_id
AND c.semestercode >= :startSemester
AND c.semestercode <= :endSemester
WHERE cg.programmeid = :programmeid
GROUP BY cg.programmeid,cg.id,cg.name`,
`,
{
replacements: { startSemester, endSemester, programmeid },
replacements: { programmeid },
type: sequelize.QueryTypes.SELECT,
}
)
return result
const courseGroupIdToCGData = courseGroupData.reduce((acc, cgd) => {
acc[cgd.id] = acc[cgd.id] || { name: cgd.name, id: cgd.id, programmeid: cgd.programmeid, teacherids: [] }
if (cgd.teacher_id) acc[cgd.id].teacherids.push(cgd.teacher_id)
return acc
}, {})
const resultPromises = Object.keys(courseGroupIdToCGData).map(async cgid => {
const { name, id, programmeid, teacherids } = courseGroupIdToCGData[cgid]
if (teacherids.length === 0) return { name, id, programmeid, courses: 0, credits: 0, students: 0 }
const stats = await sequelize.query(
`
SELECT
COUNT(DISTINCT c.course_code) AS courses,
COALESCE(SUM(credits), 0) AS credits,
COUNT(DISTINCT student_studentnumber) AS students
FROM credit_teachers ct
left join credit c
ON c.id = ct.credit_id
AND c.semestercode >= :startSemester
AND c.semestercode <= :endSemester
WHERE ct.teacher_id IN (:teacherids)
`,
{
replacements: { startSemester, endSemester, teacherids },
type: sequelize.QueryTypes.SELECT,
}
)
if (stats.length !== 1) throw "expecting single row"
const { courses, credits, students } = stats[0]
return { name, id, programmeid, courses, credits, students }
})
return Promise.all(resultPromises)
}
module.exports = {
@@ -64,8 +64,7 @@ class CourseGroupAddTeacher extends Component {
const searchResultColumns = [
{ key: 'teacherid', title: 'Teacher ID', getRowVal: s => s.id, headerProps: { onClick: null, sorted: null } },
{ key: 'username', title: 'Username', getRowVal: s => s.code, headerProps: { onClick: null, sorted: null } },
{ key: 'name', title: 'Name', getRowVal: s => s.name, headerProps: { onClick: null, sorted: null } },
{ key: 'coursegroup', title: 'Course groups', getRowVal: s => s.course_groups.map(cg => cg.name).join(), headerProps: { onClick: null, sorted: null, colSpan: 2 } },
{ key: 'name', title: 'Name', getRowVal: s => s.name, headerProps: { onClick: null, sorted: null, colSpan: 2 } },
{ key: 'icon', getRowVal: () => (<Icon name="add user" />), cellProps: { collapsing: true }, headerProps: { onClick: null, sorted: null } }
]
@@ -10,7 +10,7 @@ import './navigationBar.css'
import { logout, login, returnToSelf } from '../../apiConnection'
import LanguageChooser from '../LanguageChooser'
const { USER_ADMINER_URL, ADMINER_URL, ANALYTICS_ADMINER_URL, USAGE_ADMINER_URL } = process.env
const { USER_ADMINER_URL, ADMINER_URL, ANALYTICS_ADMINER_URL, USAGE_ADMINER_URL, KONE_ADMINER_URL } = process.env
class NavigationBar extends Component {
state = {
@@ -91,6 +91,16 @@ class NavigationBar extends Component {
icon="database"
/>
)}
{KONE_ADMINER_URL && (
<Dropdown.Item
onClick={() => {
const win = window.open(KONE_ADMINER_URL, '_blank')
win.focus()
}}
text="Kone database"
icon="database"
/>
)}
{USAGE_ADMINER_URL && (
<Dropdown.Item
onClick={() => {
@@ -69,6 +69,7 @@ module.exports = (env, args) => {
ANALYTICS_ADMINER_URL: JSON.stringify(process.env.ANALYTICS_ADMINER_URL),
USER_ADMINER_URL: JSON.stringify(process.env.USER_ADMINER_URL),
ADMINER_URL: JSON.stringify(process.env.ADMINER_URL),
KONE_ADMINER_URL: JSON.stringify(process.env.KONE_ADMINER_URL),
USAGE_ADMINER_URL: JSON.stringify(process.env.USAGE_ADMINER_URL)
}
}),