Skip to content

Commit

Permalink
Merge pull request #978 from UniversityOfHelsinkiCS/trunk
Browse files Browse the repository at this point in the history
studyprogrammeeee
  • Loading branch information
sasumaki authored Jun 4, 2019
2 parents f17ce53 + b5a1953 commit 3b16525
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 66 deletions.
89 changes: 54 additions & 35 deletions services/backend/oodikone2-backend/src/services/studytrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const formatCredit = credit => {
return { id, year, credits }
}

const fuckup = (provider, since, studentnumbers) => Credit.findAll({
const getCreditsForStudentsInThatProgram = (provider, since, studentnumbers, failed) => Credit.findAll({
attributes: ['id', 'course_code', 'credits', 'attainment_date', 'student_studentnumber'],
include: {
model: Course,
Expand All @@ -48,9 +48,12 @@ const fuckup = (provider, since, studentnumbers) => Credit.findAll({
},
student_studentnumber: {
[Op.in]: studentnumbers
},
grade: {
[Op.notIn]: failed
}
}
}).map(formatCredit)
})

const getCreditsForProvider = (provider, since) => Credit.findAll({
attributes: ['id', 'course_code', 'credits', 'attainment_date'],
Expand Down Expand Up @@ -129,26 +132,10 @@ const graduatedStatsFromStudyrights = studyrights => {
stats[year] = {
graduated: graduated + 1,
timesToGraduation: stats[year] ?
[...stats[year].timesToGraduation, timeToGraduation || 0] : [ timeToGraduation || 0]
[...stats[year].timesToGraduation, timeToGraduation || 0] : [timeToGraduation || 0]
}
graduationTimes = [...graduationTimes, timeToGraduation || 0]
})
const median = (values) => {
if (values.length === 0) return 0

values.sort((a, b) => a - b)

var half = Math.floor(values.length / 2)

if (values.length % 2)
return values[half]

return (values[half - 1] + values[half]) / 2.0
}
Object.keys(stats).forEach(year => {
stats[year].medianGraduationTime = median(stats[year].timesToGraduation)
})
stats['medianGraduationTime'] = median(graduationTimes)
return stats
}

Expand Down Expand Up @@ -202,33 +189,38 @@ const thesisProductivityForStudytrack = async code => {
return thesisProductivityFromCredits(credits)
}

const combineStatistics = (creditStats, studyrightStats, thesisStats) => {
const combineStatistics = (creditStats, studyrightStats, thesisStats, creditsForPercentage) => {
const stats = { ...creditStats }
Object.keys(stats).forEach(year => {
const thesis = thesisStats[year] || {}
stats[year].graduated = studyrightStats[year] ? studyrightStats[year].graduated : 0
stats[year].medianGraduationTime = studyrightStats[year] ? studyrightStats[year].medianGraduationTime : 0
// stats[year].medianGraduationTime = studyrightStats[year] ? studyrightStats[year].medianGraduationTime : 0
stats[year].bThesis = thesis.bThesis || 0
stats[year].mThesis = thesis.mThesis || 0
stats[year].creditsForPercentage = creditsForPercentage[year] || 0
})
return Object.values(stats)
}

// providercode here
const productivityStatsForStudytrack = async (studytrack, since) => {
const providercode = studytrackToProviderCode(studytrack)
const year = new Date(since).getFullYear()
const startDate = `${year}-${semesterStart['FALL']}`
const endDate = `${moment(since, 'YYYY').add(1, 'years').format('YYYY')}-${semesterEnd['SPRING']}`
const studentnumbers = await studentnumbersWithAllStudyrightElements([studytrack], startDate, endDate, false, false)
const promises = [
graduatedStatsForStudytrack(studytrack, since),
productivityStatsForProvider(providercode, since),
thesisProductivityForStudytrack(studytrack)
thesisProductivityForStudytrack(studytrack),
getCreditsFromStudyprogrammeStudents(studytrack, since, studentnumbers)
]
const [studyrightStats, creditStats, thesisStats] = await Promise.all(
const [studyrightStats, creditStats, thesisStats, creditsForPercentage] = await Promise.all(
promises
)
return {
id: studytrack,
status: null,
data: combineStatistics(creditStats, studyrightStats, thesisStats)
data: combineStatistics(creditStats, studyrightStats, thesisStats, creditsForPercentage)
}
}

Expand Down Expand Up @@ -373,10 +365,23 @@ const tranferredToStudyprogram = async (studentnumbers, startDate, studytrack, e
})
}

const formatCreditsForPercentage = (credits) => {
return credits.map(formatCredit).reduce(function (acc, curr) {
var key = curr['year']
if (!acc[key]) {
acc[key] = []
}
acc[key] = Number(acc[key]) + Number(curr.credits)
return acc
}, {})
}

const getCreditsFromStudyprogrammeStudents = async (studytrack, startDate, studentnumbers) => {
const providercode = studytrackToProviderCode(studytrack)
const test = await fuckup(providercode, startDate, studentnumbers)
return test.map(formatCredit)
const failed = ['0', 'Hyl.', 'Luop', 'Eisa']
const credits = await getCreditsForStudentsInThatProgram(providercode, startDate, studentnumbers, failed)
const formattedStudentCredits = formatCreditsForPercentage(credits)
return formattedStudentCredits
}

const productivityStats = async (studentnumbers, startDate, studytrack, endDate) => {
Expand Down Expand Up @@ -417,19 +422,29 @@ const throughputStatsForStudytrack = async (studytrack, since) => {
const years = getYears(since)
// studyprogramme starts with K if bachelors and M if masters
const graduationTimeLimit = studytrack[0] === 'K' ? 36 : 24
const median = (values) => {
if (values.length === 0) return 0

values.sort((a, b) => a - b)

var half = Math.floor(values.length / 2)

if (values.length % 2)
return values[half]

return (values[half - 1] + values[half]) / 2.0
}
let allGraduationTimes = []
const arr = await Promise.all(years.map(async year => {
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 getCreditsFromStudyprogrammeStudents(studytrack, startDate, studentnumbers)

const [credits, graduated, theses, genders, countries, transferred] =
await productivityStats(studentnumbers, startDate, studytrack, endDate)
delete genders[null]
delete countries[null]
const test = creditsForStudyprogramme.map(formatCredit)
console.log(test)

const creditValues = credits.reduce((acc, curr) => {
acc.mte30 = curr >= 30 ? acc.mte30 + 1 : acc.mte30
acc.mte60 = curr >= 60 ? acc.mte60 + 1 : acc.mte60
Expand All @@ -451,20 +466,24 @@ const throughputStatsForStudytrack = async (studytrack, since) => {
totals.countries[countryKey] + Number(countries[countryKey]) :
Number(countries[countryKey])
})
const inTargetTime = graduated.filter(g =>
moment(g.enddate).diff(g.startstududate, 'months') <= graduationTimeLimit).length
const graduationTimes = graduated.map(g => moment(g.enddate).diff(g.studystartdate, 'months'))
const inTargetTime = graduationTimes.filter(time =>
time <= graduationTimeLimit).length
allGraduationTimes = [...allGraduationTimes, ...graduationTimes]

totals.thesisM = theses.MASTER ? totals.thesisM + theses.MASTER : totals.thesisM
totals.thesisB = theses.BACHELOR ? totals.thesisB + theses.BACHELOR : totals.thesisB
totals.students = totals.students + credits.length
totals.graduated = totals.graduated + graduated.length,
totals.inTargetTime = totals.inTargetTime + inTargetTime,
totals.transferred = totals.transferred + transferred.count
totals.medianGraduationTime = median(allGraduationTimes)
totals.inTargetTime = totals.inTargetTime + inTargetTime
totals.transferred = totals.transferred + transferred.count
return {
year: `${year}-${year + 1}`,
credits: credits.map(cr => cr === null ? 0 : cr),
creditsForStudyprogramme: creditsForStudyprogramme.map(cr => cr === null ? 0 : cr),
creditsForStudyprogramme: creditsForStudyprogramme,
graduated: graduated.length,
medianGraduationTime: median(graduationTimes),
inTargetTime,
thesisM: theses.MASTER || 0,
thesisB: theses.BACHELOR || 0,
Expand Down
27 changes: 16 additions & 11 deletions services/backend/oodikone2-backend/src/services/studytrack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ test('graduatedStatsFromStudyrights calculates stats correctly', () => {
]
const stats = graduatedStatsFromStudyrights(studyrights)
expect(stats).toMatchObject({
2015: { graduated: 2., medianGraduationTime: 0, timesToGraduation: [ 0, 0 ]},
2014: { graduated: 1., medianGraduationTime: 0, timesToGraduation: [ 0 ]}
2015: { graduated: 2. },
2014: { graduated: 1. }
})
})

Expand All @@ -137,37 +137,42 @@ test('combineStatistics returns correctly formatted array', () => {
2014: { year: 2014, credits: 20 }
}
const studyrightStats = {
2015: { graduated: 2, medianGraduationTime: 1.5, timesToGraduation: [1, 2] },
2016: { graduated: 1, medianGraduationTime: 0, timesToGraduation: [ 0 ] }
2015: { graduated: 2 },
2016: { graduated: 1 }
}
const thesisStats = {
2014: { mThesis: 1 },
2015: { mThesis: 2, bThesis: 1 }
}
const stats = combineStatistics(creditStats, studyrightStats, thesisStats)
const creditsForPercentage = {
2014: 10,
2015: 22
}

const stats = combineStatistics(creditStats, studyrightStats, thesisStats, creditsForPercentage)
expect(stats).toContainEqual({
year: 2015,
mThesis: 2,
bThesis: 1,
credits: 40,
medianGraduationTime: 1.5,
graduated: 2
graduated: 2,
creditsForPercentage: 22
})
expect(stats).toContainEqual({
year: 2014,
mThesis: 1,
bThesis: 0,
credits: 20,
graduated: 0,
medianGraduationTime: 0
creditsForPercentage: 10
})
expect(stats).toContainEqual({
year: 2016,
mThesis: 0,
bThesis: 0,
credits: 5,
graduated: 1,
medianGraduationTime: 0
creditsForPercentage: 0
})
})

Expand All @@ -179,15 +184,15 @@ test('productivityStatsForStudytrack integrates', async () => {
bThesis: 0,
mThesis: 1,
credits: 40,
medianGraduationTime: 0
creditsForPercentage: 0
})
expect(stats.data).toContainEqual({
year: 2016,
graduated: 1,
mThesis: 0,
bThesis: 0,
credits: 5,
medianGraduationTime: 0
creditsForPercentage: 0
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ const ProductivityTable = ({ productivity, thesis, loading, error, studyprogramm
if (thesis) {
thesisTypes = thesis.map(t => t.thesisType)
}
const headerList = ['Year', 'Credits', thesisTypes.includes('MASTER') && 'Masters Thesis', thesisTypes.includes('BACHELOR') && 'Bachelors Thesis', 'Graduated', 'Graduation median time'].filter(_ => _)
const headerList = ['Year', 'Credits', thesisTypes.includes('MASTER') && 'Masters Thesis', thesisTypes.includes('BACHELOR') && 'Bachelors Thesis', 'Graduated', 'Credits given to students in selected programme'].filter(_ => _)

const refresh = () => {
callApi('/v2/studyprogrammes/productivity/recalculate', 'get', null, { code: studyprogramme })
.then(() => { dispatchGetProductivity(studyprogramme) })
}
console.log(productivity)
return (
<React.Fragment>
<Header>
Expand All @@ -32,7 +31,7 @@ const ProductivityTable = ({ productivity, thesis, loading, error, studyprogramm
productivity.lastUpdated
? moment(productivity.lastUpdated).format('HH:mm:ss MM-DD-YYYY')
: 'unknown'
} ${productivity.status || ''}`}
} ${productivity.status || ''}`}
</Header.Subheader>
)}
</Grid.Column>
Expand All @@ -58,21 +57,21 @@ const ProductivityTable = ({ productivity, thesis, loading, error, studyprogramm
<Table.Body>
{productivity && productivity.data
? productivity.data
.sort((year1, year2) => year2.year - year1.year)
.map(year => (
<Table.Row key={year.year}>
<Table.Cell>{year.year}</Table.Cell>
<Table.Cell>{Math.floor(year.credits)}</Table.Cell>
{thesisTypes.includes('BACHELOR') && (
<Table.Cell>{year.bThesis}</Table.Cell>
)}
{thesisTypes.includes('MASTER') && (
<Table.Cell>{year.mThesis}</Table.Cell>
)}
<Table.Cell>{year.graduated}</Table.Cell>
<Table.Cell>{year.medianGraduationTime} months</Table.Cell>
</Table.Row>
))
.sort((year1, year2) => year2.year - year1.year)
.map(year => (
<Table.Row key={year.year}>
<Table.Cell>{year.year}</Table.Cell>
<Table.Cell>{Math.floor(year.credits)}</Table.Cell>
{thesisTypes.includes('BACHELOR') && (
<Table.Cell>{year.bThesis}</Table.Cell>
)}
{thesisTypes.includes('MASTER') && (
<Table.Cell>{year.mThesis}</Table.Cell>
)}
<Table.Cell>{year.graduated}</Table.Cell>
<Table.Cell>{year.creditsForPercentage}</Table.Cell>
</Table.Row>
))
: null}
</Table.Body>
</Table>
Expand All @@ -89,7 +88,8 @@ ProductivityTable.propTypes = {
credits: number,
mThesis: number,
bThesis: number,
graduated: number
graduated: number,
creditsForPercentage: number
}))
}),
thesis: arrayOf(shape({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,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="2">Graduated</Table.HeaderCell>
<Table.HeaderCell colSpan="3">Graduated</Table.HeaderCell>

<Table.HeaderCell rowSpan="2">Transferred to this program</Table.HeaderCell>
{
Expand All @@ -88,6 +88,8 @@ const ThroughputTable = ({ history, throughput, thesis, loading, error, studypro
{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>

{renderCountries ? countries.map(country => <Table.HeaderCell key={country} content={country} />) : null}
<Table.HeaderCell content="≥ 30" />
<Table.HeaderCell content="≥ 60" />
Expand Down Expand Up @@ -121,6 +123,7 @@ const ThroughputTable = ({ history, throughput, thesis, loading, error, studypro
))}
<Table.Cell>{year.graduated}</Table.Cell>
<Table.Cell>{year.inTargetTime}</Table.Cell>
<Table.Cell>{year.medianGraduationTime ? `${year.medianGraduationTime} months` : '∞'}</Table.Cell>
<Table.Cell>{year.transferred}</Table.Cell>
{renderCountries ? countries.map(country => (
<Table.Cell key={year.year + country}>
Expand Down Expand Up @@ -152,6 +155,7 @@ const ThroughputTable = ({ history, throughput, thesis, loading, error, studypro
))}
<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>
<Table.HeaderCell>{throughput.totals.transferred}</Table.HeaderCell>
{renderCountries ? Object.keys(throughput.totals.countries).map(countryKey => (
<Table.HeaderCell key={`${countryKey}total`}>
Expand Down

0 comments on commit 3b16525

Please sign in to comment.