diff --git a/docker/docker-compose.dev.real.yml b/docker/docker-compose.dev.real.yml index 0231ff06dc..b28b48d1a3 100644 --- a/docker/docker-compose.dev.real.yml +++ b/docker/docker-compose.dev.real.yml @@ -8,3 +8,7 @@ services: userservice: environment: DB_URL: postgres://postgres@user_db:5432/user_db_real + + updater_writer: + environment: + DB_URL: postgres://postgres@db:5432/tkt_oodi_real diff --git a/package.json b/package.json index a64671409a..201f7829cd 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "docker": "docker-compose -f docker-compose.yml -f ./docker/docker-compose.dev.yml", "docker:build": "npm run docker -- build", "docker:up": "npm run docker -- up -d", + "docker:up:real": "npm run docker -- -f ./docker/docker-compose.dev.real.yml up", "docker:down": "npm run docker -- down", "docker:logs": "npm run docker -- logs -f", "docker:logs:backend": "npm run docker -- logs -f backend", diff --git a/services/backend/oodikone2-backend/src/services/doo_api_database_updater/oodi_data_mapper.js b/services/backend/oodikone2-backend/src/services/doo_api_database_updater/oodi_data_mapper.js index b4348d653c..a36c0c4a77 100644 --- a/services/backend/oodikone2-backend/src/services/doo_api_database_updater/oodi_data_mapper.js +++ b/services/backend/oodikone2-backend/src/services/doo_api_database_updater/oodi_data_mapper.js @@ -250,12 +250,7 @@ const getTransfersFromData = (data, studentnumber) => { const studytracks = data.elements.filter(element => element.element_id === 20) const sorted = _.sortBy(studytracks, 'end_date') let transfers = [] - let i = 0 - while (i < sorted.length) { - if (i === 0) { - i++ - continue - } + for (let i = 1; i < sorted.length; ++i) { let target = sorted[i].code let source = sorted[i - 1].code let transferdate = sorted[i - 1].end_date @@ -266,7 +261,6 @@ const getTransfersFromData = (data, studentnumber) => { transferdate, studentnumber, studyrightid: `${data.studyright_id}`}) - i++ } return(transfers) } diff --git a/services/backend/oodikone2-backend/src/services/populations.js b/services/backend/oodikone2-backend/src/services/populations.js index 209c2a1844..77b64a2e0d 100644 --- a/services/backend/oodikone2-backend/src/services/populations.js +++ b/services/backend/oodikone2-backend/src/services/populations.js @@ -393,32 +393,28 @@ 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 [momentstart, momentend] = [moment(startDate), moment(endDate)] const transferredStudyright = (s) => { - const studyright = s.studyrights.find(s => s.studyrightElements - .map(d => d.element_detail.code) - .includes(studyRights.programme)) - - if (studyright) { - s.transferredStudyright = moment(startDate).isAfter(moment(studyright.startdate)) - if (s.transferredStudyright) { - const previousRights = studyright.studyrightElements - .filter(e => e.element_detail.type === 20 && e.element_detail.code !== studyRights.programme) - s.previousRights = previousRights - } - + const transferred_from = s.transfers.find( + t => t.target.code === studyRights.programme && + moment(t.transferdate).isBetween(momentstart, momentend) + ) + if (transferred_from) { + s.transferredStudyright = true + s.transferSource = transferred_from.source } return s } diff --git a/services/backend/oodikone2-backend/src/services/studytrack.js b/services/backend/oodikone2-backend/src/services/studytrack.js index 3dcb760173..5cbda1bf78 100644 --- a/services/backend/oodikone2-backend/src/services/studytrack.js +++ b/services/backend/oodikone2-backend/src/services/studytrack.js @@ -3,7 +3,7 @@ const { Op } = sequelize const moment = require('moment') const { flatMap } = require('lodash') const { Credit, Student, Course, Provider, Studyright, StudyrightElement, - ElementDetails + ElementDetails, Transfers } = require('../models') const { ThesisCourse, ThesisTypeEnums @@ -397,32 +397,15 @@ const countriesFromClass = async (studentnumbers) => { } const tranferredToStudyprogram = async (studentnumbers, startDate, studytrack, endDate) => { - return Studyright.findAndCountAll({ - include: { - include: { - model: ElementDetails, - where: { - type: { - [Op.eq]: 20 - } - } - }, - model: StudyrightElement, - required: true, - where: { - code: { - [Op.eq]: studytrack - }, - startdate: { - [Op.gt]: moment(startDate).add(1, 'days'), // because somehow startdates have a time that is not 00:00 - [Op.lt]: new Date(endDate) - } - } - }, + return Transfers.count({ where: { - student_studentnumber: { + studentnumber: { [Op.in]: studentnumbers - } + }, + transferdate: { + [Op.between]: [startDate, endDate] + }, + targetcode: studytrack } }) } @@ -591,7 +574,7 @@ const throughputStatsForStudytrack = async (studytrack, since) => { totals.ended = totals.ended + endedStudyright.count, totals.medianGraduationTime = median(allGraduationTimes) totals.inTargetTime = totals.inTargetTime + inTargetTime - totals.transferred = totals.transferred + transferredTo.count + totals.transferred = totals.transferred + transferredTo return { year: `${year}-${year + 1}`, credits: credits.map(cr => cr === null ? 0 : cr), @@ -604,7 +587,7 @@ const throughputStatsForStudytrack = async (studytrack, since) => { genders, countries, creditValues, - transferred: transferredTo.count, + transferred: transferredTo, ended: endedStudyright.count } })) diff --git a/services/backend/shared/migrations/20190705_00_transfers_delete_on_studyright_delete.js b/services/backend/shared/migrations/20190705_00_transfers_delete_on_studyright_delete.js new file mode 100644 index 0000000000..6dc8acc30d --- /dev/null +++ b/services/backend/shared/migrations/20190705_00_transfers_delete_on_studyright_delete.js @@ -0,0 +1,17 @@ +module.exports = { + up: async (queryInterface, Sequelize) => { + return queryInterface.sequelize.transaction(async transaction => { + await queryInterface.sequelize.query('ALTER TABLE transfers DROP CONSTRAINT "transfers_studyrightid_fkey"', { transaction }) + await queryInterface.sequelize.query('ALTER TABLE transfers DROP CONSTRAINT "transfers_sourcecode_fkey"', { transaction }) + await queryInterface.sequelize.query('ALTER TABLE transfers DROP CONSTRAINT "transfers_studentnumber_fkey"', { transaction }) + await queryInterface.sequelize.query('ALTER TABLE transfers DROP CONSTRAINT "transfers_targetcode_fkey"', { transaction }) + await queryInterface.sequelize.query('ALTER TABLE transfers ADD CONSTRAINT "transfers_studyrightid_fkey" FOREIGN KEY (studyrightid) REFERENCES studyright(studyrightid) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE', { transaction }) + await queryInterface.sequelize.query('ALTER TABLE transfers ADD CONSTRAINT "transfers_sourcecode_fkey" FOREIGN KEY (sourcecode) REFERENCES element_details(code) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE', { transaction }) + await queryInterface.sequelize.query('ALTER TABLE transfers ADD CONSTRAINT "transfers_studentnumber_fkey" FOREIGN KEY (studentnumber) REFERENCES student(studentnumber) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE', { transaction }) + await queryInterface.sequelize.query('ALTER TABLE transfers ADD CONSTRAINT "transfers_targetcode_fkey" FOREIGN KEY (targetcode) REFERENCES element_details(code) ON UPDATE CASCADE ON DELETE CASCADE NOT DEFERRABLE', { transaction }) + await queryInterface.sequelize.query('DELETE FROM transfers WHERE studyrightid IS NULL OR sourcecode IS NULL OR studentnumber IS NULL OR targetcode IS NULL', { transaction }) + }) + }, + down: async () => { + } +} diff --git a/services/backend/shared/models/index.js b/services/backend/shared/models/index.js index 242295e855..c5d1ab3188 100644 --- a/services/backend/shared/models/index.js +++ b/services/backend/shared/models/index.js @@ -342,9 +342,26 @@ const Provider = sequelize.define('provider', { }) const Transfers = sequelize.define('transfers', { + id: { + primaryKey: true, + type: Sequelize.BIGINT, + autoIncrement: true + }, + studyrightid: { + type: Sequelize.BIGINT, + }, + sourcecode: { + type: Sequelize.STRING + }, + targetcode: { + type: Sequelize.STRING + }, transferdate: { type: Sequelize.DATE - } + }, + studentnumber: { + type: Sequelize.STRING + }, }, { indexes: [ { diff --git a/services/oodikone2-frontend/src/components/PopulationStudents/index.jsx b/services/oodikone2-frontend/src/components/PopulationStudents/index.jsx index 88650b14a7..e1b243e46f 100644 --- a/services/oodikone2-frontend/src/components/PopulationStudents/index.jsx +++ b/services/oodikone2-frontend/src/components/PopulationStudents/index.jsx @@ -140,7 +140,7 @@ class PopulationStudents extends Component { copyToClipboard(clipboardString) } - const transferFrom = s => (s.previousRights[0] && getTextIn(s.previousRights[0].element_detail.name, this.props.language)) + const transferFrom = s => getTextIn(s.transferSource.name, this.props.language) const priorityText = (studyRights) => { const codes = this.studyrightCodes(studyRights, 'prioritycode')