Skip to content

Commit

Permalink
Merge pull request #994 from UniversityOfHelsinkiCS/trunk
Browse files Browse the repository at this point in the history
tags, trasferredFrom and feature toggled shit away
  • Loading branch information
sasumaki authored Jun 7, 2019
2 parents b77a04e + 4825930 commit 9c93054
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('tag_students', {
await queryInterface.createTable('tag_student', {
createdAt: {
type: Sequelize.DATE
},
Expand Down
4 changes: 3 additions & 1 deletion services/backend/oodikone2-backend/src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const ping = require('./routes/ping')
const oodi = require('./routes/oodi')
const task = require('./routes/tasks')
const feedback = require('./routes/feedback')
const tags = require('./routes/tags')

module.exports = (app, url) => {
app.use(url, log)
Expand All @@ -37,12 +38,13 @@ module.exports = (app, url) => {
app.use(url, semesters)
app.use(`${url}/teachers`, auth.roles(['teachers']), teachers)
app.use(`${url}/users`, auth.roles(['users']), users)
app.use(`${url}/feedback`, auth.roles(['users']), feedback)
app.use(`${url}/feedback`, feedback)
app.use(`${url}/usage`, auth.roles(['usage']), usage)
app.use(`${url}/oodilearn`, auth.roles(['oodilearn']), oodilearn)
app.use(`${url}/course-groups`, auth.roles(['coursegroups']), courseGroups)
app.use(`${url}/mandatory_courses`, mandatoryCourses),
app.use(`${url}/mandatory-course-labels`, mandatoryCourseLabels),
app.use(`${url}/oodi`, auth.roles(['dev']), oodi)
app.use(url, auth.roles(['dev', 'admin']), task)
app.use(url, tags)
}
40 changes: 40 additions & 0 deletions services/backend/oodikone2-backend/src/routes/tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const router = require('express').Router()
const Tags = require('../services/tags')
const TagStudent = require('../services/tagstudent')

router.get('/tags', async (req, res) => {
try {
const tags = await Tags.findTags()
res.status(200).json(tags)
} catch (err) {
console.log(err)
res.status(400).json(err)
}
})

router.post('/tags', async (req, res) => {
const { tag } = req.body
try {
const result = await Tags.createNewTag(tag)
res.status(200).json(result)
} catch (err) {
console.log(err)
res.status(400).json(err.message)
}
})

router.get('/studenttags', async (req, res) => {
try {
const result = await TagStudent.getStudentTags()
res.status(200).json(result)
} catch (err) {
console.log(err)
res.status(400).json(err)
}
})

router.post('/studenttags', async (req,res) => {
console.log('trying to post?')
})

module.exports = router
72 changes: 58 additions & 14 deletions services/backend/oodikone2-backend/src/services/studytrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,41 @@ const tranferredToStudyprogram = async (studentnumbers, startDate, studytrack, e
})
}

const transferredFromStudyprogram = async (studentnumbers, startDate, studytrack, endDate) => {
const enddate = new Date() < new Date(endDate) ? new Date() : new Date(endDate)
return Studyright.findAndCountAll({
include: {
include: {
model: ElementDetails,
where: {
type: {
[Op.eq]: 20
}
}
},
model: StudyrightElement,
required: true,
where: {
code: {
[Op.eq]: studytrack
},
enddate: {
[Op.gte]: new Date(startDate),
[Op.lt]: enddate
}
}
},
where: {
student_studentnumber: {
[Op.in]: studentnumbers
},
graduated: {
[Op.eq]: 0
}
}
})
}

const formatCreditsForProductivity = (credits) => {
return credits.map(formatCredit).reduce(function (acc, curr) {
var key = curr['year']
Expand All @@ -417,13 +452,16 @@ const transferredCreditsForProductivity = async (studytrack, since) => {
return formattedCredits
}

const productivityStats = async (studentnumbers, startDate, studytrack, endDate) => {
return Promise.all([creditsAfter(studentnumbers, startDate),
graduationsFromClass(studentnumbers, studytrack),
thesesFromClass(studentnumbers, startDate, studytrack),
gendersFromClass(studentnumbers),
countriesFromClass(studentnumbers),
tranferredToStudyprogram(studentnumbers, startDate, studytrack, endDate)])
const statsForClass = async (studentnumbers, startDate, studytrack, endDate) => {
return Promise.all([
creditsAfter(studentnumbers, startDate),
graduationsFromClass(studentnumbers, studytrack),
thesesFromClass(studentnumbers, startDate, studytrack),
gendersFromClass(studentnumbers),
countriesFromClass(studentnumbers),
tranferredToStudyprogram(studentnumbers, startDate, studytrack, endDate),
transferredFromStudyprogram(studentnumbers, startDate, studytrack, endDate)
])
}

const getYears = (since) => {
Expand Down Expand Up @@ -472,10 +510,14 @@ const throughputStatsForStudytrack = async (studytrack, since) => {
const startDate = `${year}-${semesterStart['FALL']}`
const endDate = `${moment(year, 'YYYY').add(1, 'years').format('YYYY')}-${semesterEnd['SPRING']}`
const studentnumbers = await studentnumbersWithAllStudyrightElements([studytrack], startDate, endDate, false, false)
const creditsForStudyprogramme = await productivityCreditsFromStudyprogrammeStudents(studytrack, startDate, studentnumbers)

const [credits, graduated, theses, genders, countries, transferred] =
await productivityStats(studentnumbers, startDate, studytrack, endDate)
const creditsForStudyprogramme =
await productivityCreditsFromStudyprogrammeStudents(studytrack, startDate, studentnumbers)

const [credits, graduated, theses, genders, countries, transferredTo, transferredFrom] =
await statsForClass(studentnumbers, startDate, studytrack, endDate)
//console.log(year)
//console.log(transferredFrom.rows.map(r => r.get({ plain: true })))
// theres so much shit in the data that transefferFrom doesnt rly mean anything
delete genders[null]
delete countries[null]
const creditValues = credits.reduce((acc, curr) => {
Expand Down Expand Up @@ -508,9 +550,10 @@ const throughputStatsForStudytrack = async (studytrack, since) => {
totals.thesisB = theses.BACHELOR ? totals.thesisB + theses.BACHELOR : totals.thesisB
totals.students = totals.students + credits.length
totals.graduated = totals.graduated + graduated.length,
totals.medianGraduationTime = median(allGraduationTimes)
totals.transferredFrom = totals.transferredFrom + transferredFrom.count,
totals.medianGraduationTime = median(allGraduationTimes)
totals.inTargetTime = totals.inTargetTime + inTargetTime
totals.transferred = totals.transferred + transferred.count
totals.transferred = totals.transferred + transferredTo.count
return {
year: `${year}-${year + 1}`,
credits: credits.map(cr => cr === null ? 0 : cr),
Expand All @@ -523,7 +566,8 @@ const throughputStatsForStudytrack = async (studytrack, since) => {
genders,
countries,
creditValues,
transferred: transferred.count
transferred: transferredTo.count,
transferredFrom: transferredFrom.count
}
}))

Expand Down
15 changes: 15 additions & 0 deletions services/backend/oodikone2-backend/src/services/tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const Sequelize = require('sequelize')
const { Tag } = require('../models')

const Op = Sequelize.Op

const findTags = () => Tag.findAll({})

const createNewTag = async (tag) => {
return Tag.create(tag)
}

module.exports = {
createNewTag,
findTags
}
8 changes: 8 additions & 0 deletions services/backend/oodikone2-backend/src/services/tagstudent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const Sequelize = require('sequelize')
const { TagStudent } = require('../models')

const getStudentTags = () => TagStudent.findAll({})

module.exports = {
getStudentTags
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, { Fragment, useEffect, useState } from 'react'
import moment from 'moment'
import { Header, Loader, Table, Button, Grid, Icon } from 'semantic-ui-react'
import { shape, number, arrayOf, bool, string, func } from 'prop-types'
Expand All @@ -7,16 +7,28 @@ import { withRouter } from 'react-router'
import { flatten, uniq } from 'lodash'
import { callApi } from '../../../apiConnection'
import { getThroughput } from '../../../redux/throughput'
import { userRoles } from '../../../common'

const ThroughputTable = ({ history, throughput, thesis, loading, error, studyprogramme,
dispatchGetThroughput }) => {
const [roles, setRoles] = useState(undefined)
const setFuckingRoles = async () => {
setRoles(await userRoles())
}
useEffect(() => {
setFuckingRoles()
}, [])
const showPopulationStatistics = (yearLabel) => {
const year = Number(yearLabel.slice(0, 4))
const months = Math.ceil(moment.duration(moment().diff(`${year}-08-01`)).asMonths())
history.push(`/populations?months=${months}&semesters=FALL&semesters=` +
`SPRING&studyRights=%7B"programme"%3A"${studyprogramme}"%7D&year=${year}`)
}
if (error) return <h1>Oh no so error {error}</h1>
let GRADUATED_FEATURE_TOGGLED_ON = false
if (roles) {
GRADUATED_FEATURE_TOGGLED_ON = roles.includes('dev')
}
const data = throughput && throughput.data ? throughput.data.filter(year => year.credits.length > 0) : []
const genders = data.length > 0 ? uniq(flatten(data.map(year => Object.keys(year.genders)))) : []
const countries = data.length > 0 && throughput.totals.countries ? uniq(flatten(data.map(year => Object.keys(year.countries)))).sort() : []
Expand Down Expand Up @@ -67,7 +79,7 @@ const ThroughputTable = ({ history, throughput, thesis, loading, error, studypro
<Table.HeaderCell colSpan={genders.length + 1}>Students</Table.HeaderCell> :
<Table.HeaderCell rowSpan="2">Students</Table.HeaderCell>
}
<Table.HeaderCell colSpan="3">Graduated</Table.HeaderCell>
<Table.HeaderCell colSpan={GRADUATED_FEATURE_TOGGLED_ON ? '3' : '1'}>Graduated</Table.HeaderCell>

<Table.HeaderCell rowSpan="2">Transferred to this program</Table.HeaderCell>
{
Expand All @@ -87,9 +99,12 @@ const ThroughputTable = ({ history, throughput, thesis, loading, error, studypro
{renderGenders ? <Table.HeaderCell content="Total" /> : null}
{genders.map(gender => <Table.HeaderCell key={gender} content={gender} />)}
<Table.HeaderCell >Graduated overall</Table.HeaderCell>
<Table.HeaderCell >Graduated in time</Table.HeaderCell>
<Table.HeaderCell >Graduation median time</Table.HeaderCell>

{GRADUATED_FEATURE_TOGGLED_ON &&
<Fragment>
<Table.HeaderCell >Graduated in time</Table.HeaderCell>
<Table.HeaderCell >Graduation median time</Table.HeaderCell>
</Fragment>
}
{renderCountries ? countries.map(country => <Table.HeaderCell key={country} content={country} />) : null}
<Table.HeaderCell content="≥ 30" />
<Table.HeaderCell content="≥ 60" />
Expand Down Expand Up @@ -122,8 +137,12 @@ const ThroughputTable = ({ history, throughput, thesis, loading, error, studypro
</Table.Cell>
))}
<Table.Cell>{year.graduated}</Table.Cell>
<Table.Cell>{year.inTargetTime}</Table.Cell>
<Table.Cell>{year.medianGraduationTime ? `${year.medianGraduationTime} months` : '∞'}</Table.Cell>
{GRADUATED_FEATURE_TOGGLED_ON &&
<Fragment>
<Table.Cell>{year.inTargetTime}</Table.Cell>
<Table.Cell>{year.medianGraduationTime ? `${year.medianGraduationTime} months` : '∞'}</Table.Cell>
</Fragment>
}
<Table.Cell>{year.transferred}</Table.Cell>
{renderCountries ? countries.map(country => (
<Table.Cell key={year.year + country}>
Expand Down Expand Up @@ -154,8 +173,12 @@ const ThroughputTable = ({ history, throughput, thesis, loading, error, studypro
</Table.HeaderCell>
))}
<Table.HeaderCell>{throughput.totals.graduated}</Table.HeaderCell>
<Table.HeaderCell>{throughput.totals.inTargetTime}</Table.HeaderCell>
<Table.HeaderCell>{throughput.totals.medianGraduationTime ? `${throughput.totals.medianGraduationTime} months` : '∞'}</Table.HeaderCell>
{GRADUATED_FEATURE_TOGGLED_ON &&
<Fragment>
<Table.HeaderCell>{throughput.totals.inTargetTime}</Table.HeaderCell>
<Table.HeaderCell>{throughput.totals.medianGraduationTime ? `${throughput.totals.medianGraduationTime} months` : '∞'}</Table.HeaderCell>
</Fragment>
}
<Table.HeaderCell>{throughput.totals.transferred}</Table.HeaderCell>
{renderCountries ? Object.keys(throughput.totals.countries).map(countryKey => (
<Table.HeaderCell key={`${countryKey}total`}>
Expand Down
6 changes: 5 additions & 1 deletion services/oodikone2-frontend/src/redux/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import thesisCourses from './thesisCourses'
import accessGroups from './accessGroups'
import feedback from './feedback'
import mandatoryCourseLabels from './mandatoryCourseLabels'
import tags from './tags'
import tagstudent from './tagstudent'

export default combineReducers({
locale,
Expand Down Expand Up @@ -88,5 +90,7 @@ export default combineReducers({
oodilearnPopulationCourseSelect,
thesisCourses,
accessGroups,
feedback
feedback,
tags,
tagstudent
})
55 changes: 55 additions & 0 deletions services/oodikone2-frontend/src/redux/tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { callController } from '../apiConnection'

export const getTags = () => {
const route = '/tags'
const prefix = 'GET_TAGS_'
return callController(route, prefix)
}

export const createTag = (tag) => {
const route = '/tags'
const prefix = 'CREATE_TAG_'
const method = 'post'
const data = { tag }
return callController(route, prefix, data, method)
}

const reducer = (state = { data: [] }, action) => {
switch (action.type) {
case 'GET_TAGS_ATTEMPT':
return {
...state,
pending: true
}
case 'GET_TAGS_SUCCESS':
return {
...state,
pending: false,
data: action.response || {}
}
case 'GET_TAGS_FAILURE':
return {
...state,
pending: false
}
case 'CREATE_TAG_ATTEMPT':
return {
...state,
pending: true
}
case 'CREATE_TAG_FAILURE':
return {
...state,
pending: false
}
case 'CREATE_TAG_SUCCESS':
return {
...state,
pending: false
}
default:
return state
}
}

export default reducer
Loading

0 comments on commit 9c93054

Please sign in to comment.