mirror of
https://github.com/UniversityOfHelsinkiCS/oodikone.git
synced 2026-10-12 19:03:39 +03:00
Split kone_data to separate database entirely
This commit is contained in:
+2
-1
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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 () => {
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
+1
-2
@@ -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)
|
||||
}
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user