Skip to content

Commit

Permalink
Merge pull request #1046 from UniversityOfHelsinkiCS/trunk
Browse files Browse the repository at this point in the history
improved fixing
  • Loading branch information
sasumaki authored Jun 25, 2019
2 parents 6ceca87 + 31df156 commit dc0b02f
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 53 deletions.
2 changes: 1 addition & 1 deletion cypress/integration/Student_statistics.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('Student Statistics tests', () => {
it('Can jump to course', () => {
cy.get('.prompt').type('Oinonen')
cy.contains('011143561').click()
cy.contains('Toinen kotimainen kieli (40061)').siblings().within(() => { cy.get('.arrow').click() })
cy.contains('Toinen kotimainen kieli (40061)').siblings().within(() => { cy.get('.level').click() })
cy.url().should('include', '/coursestatistics')
cy.contains('Toinen kotimainen kieli')
})
Expand Down
2 changes: 1 addition & 1 deletion services/backend/updater_writer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ stan.on('connect', function () {
sub.on('message', async (msg) => {
const data = JSON.parse(msg.getData())
if (data.studentInfo) {
await updateStudent(data)
await updateStudent(data, stan)
} else {
await updateMeta(data)
}
Expand Down
3 changes: 2 additions & 1 deletion services/backend/updater_writer/updater/database_updater.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const updateStudyRights = (studyRights, transaction) => studyRights.map(async ({
])
})

const updateStudent = async (student) => {
const updateStudent = async (student, stan) => {
const { studentInfo, studyAttainments, semesterEnrollments, studyRights } = student
const transaction = await sequelize.transaction()
try {
Expand All @@ -72,6 +72,7 @@ const updateStudent = async (student) => {
console.log('Transaction aborted')
} else if (err.message === 'deadlock detected') {
console.log('Deadlock suicide')
stan.close()
process.exit(1)
} else {
console.log(err.parent)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { Component, Fragment } from 'react'
import { connect } from 'react-redux'
import { string, arrayOf, object, func, bool, shape } from 'prop-types'
import { Header, Segment, Button, Icon, Popup, Tab, Grid } from 'semantic-ui-react'
import { Header, Segment, Button, Icon, Popup, Tab, Grid, Checkbox } from 'semantic-ui-react'
import { withRouter } from 'react-router-dom'
import _ from 'lodash'
import XLSX from 'xlsx'
Expand All @@ -24,13 +24,28 @@ const popupTimeoutLength = 1000


class PopulationStudents extends Component {
state = { containsStudyTracks: false, students: [] }
state = {
containsStudyTracks: false,
students: [],
checked: false,
checkedStudents: []
}

async componentDidMount() {
const roles = await userRoles()
const admin = roles.includes('admin')
await this.props.getTagsByStudytrack(this.props.queryStudyrights[0])
this.setState({ admin, containsStudyTracks: this.containsStudyTracks() })

const initialCheckedStudents = []
this.props.selectedStudents.forEach((sn) => {
const check = {
studentnumber: sn,
checked: false
}
initialCheckedStudents.push(check)
})
this.setState({ checkedStudents: initialCheckedStudents })
}

containsStudyTracks = () => {
Expand Down Expand Up @@ -71,6 +86,40 @@ class PopulationStudents extends Component {
clearTimeout(this.timeout)
}

handleAllCheck = () => {
const newCheckedStudents = []
this.props.selectedStudents.forEach((sn) => {
const check = {
studentnumber: sn,
checked: !this.state.checked
}
newCheckedStudents.push(check)
})
this.setState({ checkedStudents: newCheckedStudents })
this.setState({ checked: !this.state.checked })
}

handleSingleCheck = (studentnumber) => {
const checker = this.state.checkedStudents.find(check => check.studentnumber === studentnumber)
const idx = this.state.checkedStudents.indexOf(checker)
const tempArr = [...this.state.checkedStudents]
tempArr.splice(idx, 1, ({ studentnumber: checker.studentnumber, checked: !checker.checked }))
this.setState({ checkedStudents: tempArr })
}

falsifyChecks = () => {
const newCheckedStudents = []
this.props.selectedStudents.forEach((sn) => {
const check = {
studentnumber: sn,
checked: false
}
newCheckedStudents.push(check)
})
this.setState({ checkedStudents: newCheckedStudents })
this.setState({ checked: false })
}

renderStudentTable() {
if (!this.props.showList) {
return null
Expand Down Expand Up @@ -340,15 +389,23 @@ class PopulationStudents extends Component {

const tagRows = this.props.selectedStudents
.map(sn => students[sn])
.map(s => (
<div key={s.studentNumber}>
<TagStudent
tags={this.props.tags}
studentnumber={s.studentNumber}
studentstags={s.tags}
studytrack={this.props.queryStudyrights[0]}
/>
</div>))
.map((s) => {
const check = this.state.checkedStudents.find(c => c.studentnumber === s.studentNumber) || false
return (
<div key={s.studentNumber}>
<Checkbox
checked={check.checked}
onChange={() => this.handleSingleCheck(s.studentNumber)}
/>
<TagStudent
tags={this.props.tags}
studentnumber={s.studentNumber}
studentstags={s.tags}
studytrack={this.props.queryStudyrights[0]}
/>
</div>)
})

const panes = [
{
menuItem: 'General',
Expand Down Expand Up @@ -407,9 +464,14 @@ class PopulationStudents extends Component {
menuItem: 'Tags',
render: () => (
<Tab.Pane>
<Checkbox
checked={this.state.checked}
onChange={() => this.handleAllCheck()}
/>
<TagPopulation
falsifyChecks={() => this.falsifyChecks()}
tags={this.props.tags}
studentnumbers={this.props.selectedStudents}
checkedStudents={this.state.checkedStudents}
studytrack={this.props.queryStudyrights[0]}
/>
{tagRows}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class StudentDetails extends Component {
`${isStudyModuleCredit ? `${getTextIn(course.name, language)} [Study Module]` : getTextIn(course.name, language)} (${course.code})`,
<div>{icon}{grade}</div>,
credits,
<Icon style={{ cursor: 'pointer' }} name="arrow up" onClick={() => this.pushQueryToUrl({ courseCodes: [course.code], separate: false, fromYear: year - 1, toYear: year + 1 })} />
<Icon style={{ cursor: 'pointer' }} name="level up" onClick={() => this.pushQueryToUrl({ courseCodes: [course.code], separate: false, fromYear: year - 1, toYear: year + 1 })} />
]
})
return (
Expand Down
40 changes: 27 additions & 13 deletions services/oodikone2-frontend/src/components/TagPopulation/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import React, { useState, useEffect } from 'react'
import { withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import { Button, Dropdown } from 'semantic-ui-react'
import { arrayOf, string, shape, func } from 'prop-types'
import { arrayOf, string, shape, func, bool } from 'prop-types'

import {
createStudentTagAction,
getStudentTagsByStudytrackAction
} from '../../redux/tagstudent'


const TagPopulation = ({ createStudentTag, tags, studentnumbers, getStudentTagsByStudytrack, studytrack }) => {
const TagPopulation = ({ falsifyChecks, createStudentTag, tags, checkedStudents, getStudentTagsByStudytrack, studytrack, created }) => {
const [options, setOptions] = useState([])
const [selectedValue, setSelected] = useState('')

Expand All @@ -19,22 +19,30 @@ const TagPopulation = ({ createStudentTag, tags, studentnumbers, getStudentTagsB
setOptions(createdOptions)
}, [])

useEffect(() => {
if (created) {
getStudentTagsByStudytrack(studytrack)
}
}, [created])

const handleChange = (event, { value }) => {
event.preventDefault()
setSelected(value)
}

const handleSubmit = (event) => {
event.preventDefault()
studentnumbers.forEach((studentnumber) => {
const tag = {
tag_id: selectedValue,
studentnumber
falsifyChecks()
checkedStudents.forEach((student) => {
if (student.checked) {
const tag = {
tag_id: selectedValue,
studentnumber: student.studentnumber
}
createStudentTag(tag)
setSelected('')
}
createStudentTag(tag)
setSelected('')
})
getStudentTagsByStudytrack(studytrack)
}

return (
Expand All @@ -47,18 +55,24 @@ const TagPopulation = ({ createStudentTag, tags, studentnumbers, getStudentTagsB
onChange={handleChange}
value={selectedValue}
/>
<Button onClick={handleSubmit}>add tag to population</Button>
<Button onClick={handleSubmit}>add tag to multiple students</Button>
</div>
)
}

TagPopulation.propTypes = {
createStudentTag: func.isRequired,
getStudentTagsByStudytrack: func.isRequired,
studentnumbers: arrayOf(string).isRequired,
checkedStudents: arrayOf(shape({ studentnumber: string, checked: bool })).isRequired,
tags: arrayOf(shape({ tag_id: string, tagname: string, studytrack: string })).isRequired,
studytrack: string.isRequired
studytrack: string.isRequired,
created: bool.isRequired,
falsifyChecks: func.isRequired
}

const mapStateToProps = ({ tagstudent }) => ({
created: tagstudent.created
})


export default withRouter(connect(null, { createStudentTag: createStudentTagAction, getStudentTagsByStudytrack: getStudentTagsByStudytrackAction })(TagPopulation))
export default withRouter(connect(mapStateToProps, { createStudentTag: createStudentTagAction, getStudentTagsByStudytrack: getStudentTagsByStudytrackAction })(TagPopulation))
40 changes: 21 additions & 19 deletions services/oodikone2-frontend/src/components/TagStudent/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,27 @@ const TagStudent = ({
</List.Item>))

return (
<div>
<List horizontal>
<List.Item >
<List.Content>
{studentnumber}
</List.Content>
</List.Item>)
{studentsTags}
</List>
<Dropdown
placeholder="Tag"
search
selection
options={tagOptions}
onChange={handleChange}
value={selectedValue}
/>
<Button onClick={handleSubmit}>give tag to student</Button>
</div>
<List horizontal>
<List.Item >
<List.Content>
{studentnumber}
</List.Content>
</List.Item>
{studentsTags}
<List.Item>
<List.Content>
<Dropdown
placeholder="Tag"
search
selection
options={tagOptions}
onChange={handleChange}
value={selectedValue}
/>
<Button onClick={handleSubmit}>give tag to student</Button>
</List.Content>
</List.Item>
</List>
)
}

Expand Down
11 changes: 7 additions & 4 deletions services/oodikone2-frontend/src/redux/tagstudent.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const deleteStudentTagAction = (id) => {
return callController(route, prefix, data, method)
}

const reducer = (state = { data: [], success: false }, action) => {
const reducer = (state = { data: [], success: false, created: false }, action) => {
switch (action.type) {
case 'GET_STUDENT_TAGS_ATTEMPT':
return {
Expand Down Expand Up @@ -91,17 +91,20 @@ const reducer = (state = { data: [], success: false }, action) => {
case 'CREATE_STUDENT_TAG_ATTEMPT':
return {
...state,
pending: true
pending: true,
created: false
}
case 'CREATE_STUDENT_TAG_FAILURE':
return {
...state,
pending: false
pending: false,
created: false
}
case 'CREATE_STUDENT_TAG_SUCCESS':
return {
...state,
pending: false
pending: false,
created: true
}
case 'DELETE_STUDENT_TAG_ATTEMPT':
return {
Expand Down
9 changes: 8 additions & 1 deletion services/updater_scheduler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,14 @@ stan.on('connect', async () => {
scheduleSub.on('message', async (_) => {
scheduleAllStudentsAndMeta()
})
const opts = stan.subscriptionOptions();
opts.setManualAckMode(true);
opts.setAckWait(30 * 60 * 1000); // 1min
opts.setDeliverAllAvailable()
opts.setDurableName('durable')
opts.setMaxInFlight(20)

const statusSub = stan.subscribe('status')
const statusSub = stan.subscribe('status', opts)

statusSub.on('message', async (msg) => {
const message = msg.getData().split(':')
Expand Down Expand Up @@ -134,6 +140,7 @@ stan.on('connect', async () => {
const isStudent = !!isValidStudentId(task)
logger.info(`Status changed for ${task} to ${status}`, { task: task, status: status, student: isStudent })
await updateTask(task, status, isStudent ? 'student' : 'other')
msg.ack()
})

})
Expand Down

0 comments on commit dc0b02f

Please sign in to comment.