Yearly update teacher leaderboard stats button

This commit is contained in:
Tuomo Torppa
2019-06-03 14:37:51 +03:00
parent b5f166f3cb
commit 28ee032a60
5 changed files with 128 additions and 55 deletions
@@ -20,6 +20,12 @@ router.get('/top', async (req, res) => {
res.json(result)
})
router.post('/top', async (req, res) => {
const { startyearcode, endyearcode } = req.body
res.status(200).end()
await topteachers.findAndSaveTeachers(startyearcode, endyearcode)
})
router.get('/top/categories', async (req, res) => {
const result = await topteachers.getCategoriesAndYears()
res.json(result)
@@ -1,5 +1,6 @@
const { Op } = require('sequelize')
const sequelize = require('sequelize')
const { Semester } = require('../models/index')
const { Op } = sequelize
const getSemestersAndYears = async before => {
const semesters = await Semester.findAll({
@@ -24,6 +25,15 @@ const getSemestersAndYears = async before => {
return result
}
const getMaxYearcode = async () => {
const aa = await Semester.findAll({
attributes: [[sequelize.fn('max', sequelize.col('yearcode')), 'maxYearCode']],
raw: true
})
return aa[0].maxYearCode
}
module.exports = {
getSemestersAndYears
getSemestersAndYears,
getMaxYearcode
}
@@ -1,5 +1,5 @@
const { redisClient } = require('./redis')
const { getSemestersAndYears } = require('./semesters')
const { getSemestersAndYears, getMaxYearcode } = require('./semesters')
const { Teacher, Semester, Credit, Course } = require('../models/index')
const { Op } = require('sequelize')
@@ -126,6 +126,13 @@ const findTopTeachers = async (yearcode) => {
}
}
const findAndSaveTeachers = async (startcode = 50, to) => {
const endcode = to ? to : await getMaxYearcode()
for (let code = startcode; code <= endcode; code++) {
await findAndSaveTopTeachers(code)
}
}
const findAndSaveTopTeachers = async yearcode => {
const { all, openuni } = await findTopTeachers(yearcode)
await setTeacherStats(ID.OPENUNI, yearcode, openuni)
@@ -138,6 +145,7 @@ module.exports = {
setTeacherStats,
getCategoriesAndYears,
findTopTeachers,
findAndSaveTeachers,
findAndSaveTopTeachers,
ID
}
@@ -1,31 +1,20 @@
import React, { Component } from 'react'
import { Segment, Form } from 'semantic-ui-react'
import { connect } from 'react-redux'
import { func, arrayOf, shape, string, any } from 'prop-types'
import { func, arrayOf, shape, string, any, number } from 'prop-types'
import { getTopTeachers } from '../../redux/teachersTop'
class LeaderForm extends Component {
state={
selectedyear: null,
selectedcategory: null
}
componentDidMount() {
const { year, category } = this.defaultValues()
if (year && category) {
this.updateAndSubmitForm({
this.props.updateAndSubmitForm({
selectedyear: year,
selectedcategory: category
})
}
}
updateAndSubmitForm = (args) => {
this.setState(args)
const { selectedyear, selectedcategory } = { ...this.state, ...args }
this.props.getTopTeachers(selectedyear, selectedcategory)
}
defaultValues = () => {
const { yearoptions, categoryoptions } = this.props
const [defaultyear = {}] = yearoptions
@@ -36,8 +25,6 @@ class LeaderForm extends Component {
}
}
handleChange = (e, { value, name }) => this.updateAndSubmitForm({ [name]: value })
render() {
const { yearoptions, categoryoptions } = this.props
return (
@@ -51,8 +38,8 @@ class LeaderForm extends Component {
options={yearoptions}
selection
search
value={this.state.selectedyear}
onChange={this.handleChange}
value={this.props.selectedyear}
onChange={this.props.handleChange}
/>
<Form.Dropdown
name="selectedcategory"
@@ -61,8 +48,8 @@ class LeaderForm extends Component {
options={categoryoptions}
selection
search
value={this.state.selectedcategory}
onChange={this.handleChange}
value={this.props.selectedcategory}
onChange={this.props.handleChange}
/>
</Form.Group>
</Form>
@@ -74,7 +61,10 @@ class LeaderForm extends Component {
LeaderForm.propTypes = {
yearoptions: arrayOf(shape({})).isRequired,
categoryoptions: arrayOf(shape({ key: any, value: any, text: string })).isRequired,
getTopTeachers: func.isRequired
updateAndSubmitForm: func.isRequired,
handleChange: func.isRequired,
selectedcategory: string, // eslint-disable-line
selectedyear: number // eslint-disable-line
}
export default connect(null, { getTopTeachers })(LeaderForm)
@@ -1,45 +1,102 @@
import React, { Component } from 'react'
import { Segment, Message } from 'semantic-ui-react'
import { Segment, Message, Button, Popup } from 'semantic-ui-react'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import { func, arrayOf, bool, shape, any, string } from 'prop-types'
import { getTopTeachersCategories } from '../../redux/teachersTopCategories'
import { getTopTeachers } from '../../redux/teachersTop'
import TeacherStatisticsTable from '../TeacherStatisticsTable'
import LeaderForm from './LeaderForm'
import { callApi } from '../../apiConnection'
class TeacherLeaderBoard extends Component {
state={}
state = {
selectedyear: null,
selectedcategory: null,
recalculating: false,
isOpen: false
}
componentDidMount() {
this.props.getTopTeachersCategories()
}
componentDidMount() {
this.props.getTopTeachersCategories()
}
render() {
const { statistics, updated, isLoading, yearoptions, categoryoptions } = this.props
return (
<div>
{ isLoading
? <Segment basic loading={isLoading} />
: (
<div>
<Message
header="Teacher leaderboard"
content="Teachers who have produced the most credits from all departments."
/>
<LeaderForm yearoptions={yearoptions} categoryoptions={categoryoptions} />
<Segment>
<Message size="tiny" content={`Last updated: ${updated}`} />
<TeacherStatisticsTable
statistics={statistics}
onClickFn={e => this.props.history.push(`/teachers/${e.target.innerText}`)}
/>
</Segment>
</div>
)
}
</div>
)
}
handleOpen = () => {
this.setState({ isOpen: true, recalculating: true })
this.timeout = setTimeout(() => {
this.setState({ isOpen: false })
}, 5000)
}
handleClose = () => {
this.setState({ isOpen: false })
clearTimeout(this.timeout)
}
refresh = () => {
const { selectedyear } = this.state
callApi('/teachers/top', 'post', { startyearcode: selectedyear, endyearcode: selectedyear + 1 }, null)
}
updateAndSubmitForm = (args) => {
this.setState({ recalculating: false, ...args })
const { selectedyear, selectedcategory } = { ...this.state, ...args }
this.props.getTopTeachers(selectedyear, selectedcategory)
}
handleChange = (e, { value, name }) => this.updateAndSubmitForm({ [name]: value })
render() {
const { statistics, updated, isLoading, yearoptions, categoryoptions } = this.props
const { selectedcategory, selectedyear, recalculating } = this.state
return (
<div>
{isLoading
? <Segment basic loading={isLoading} />
: (
<div>
<Message>
<Message.Header>Teacher leaderboard</Message.Header>
Teachers who have produced the most credits from all departments.
</Message>
<LeaderForm
yearoptions={yearoptions}
categoryoptions={categoryoptions}
handleChange={this.handleChange}
updateAndSubmitForm={this.updateAndSubmitForm}
selectedcategory={selectedcategory}
selectedyear={selectedyear}
/>
<Segment>
<Message>
{`Last updated: ${updated}`}
</Message>
<Popup
trigger={
<Button
disabled={recalculating}
content="Recalculate this year"
onClick={() => { this.refresh() }}
/>}
content="Recalculation started. Recalculation might take multiple minutes. Refresh page to see the results"
on="click"
open={this.state.isOpen}
onClose={this.handleClose}
onOpen={this.handleOpen}
/>
<TeacherStatisticsTable
statistics={statistics}
onClickFn={e => this.props.history.push(`/teachers/${e.target.innerText}`)}
/>
</Segment>
</div>
)
}
</div>
)
}
}
TeacherLeaderBoard.propTypes = {
@@ -48,6 +105,7 @@ TeacherLeaderBoard.propTypes = {
yearoptions: arrayOf(shape({})).isRequired,
history: shape({}).isRequired,
updated: string.isRequired,
getTopTeachers: func.isRequired,
getTopTeachersCategories: func.isRequired,
categoryoptions: arrayOf(shape({ key: any, text: string, value: any })).isRequired
}
@@ -76,5 +134,6 @@ const mapStateToProps = ({ teachersTop, teachersTopCategories }) => {
}
export default connect(mapStateToProps, {
getTopTeachersCategories
getTopTeachersCategories,
getTopTeachers
})(withRouter(TeacherLeaderBoard))