mirror of
https://github.com/UniversityOfHelsinkiCS/oodikone.git
synced 2026-10-12 19:03:39 +03:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35b7c4e38e | ||
|
|
00db629958 | ||
|
|
307d87c3e6 | ||
|
|
265c24421e | ||
|
|
8269e4bce8 | ||
|
|
24c9e7b50f | ||
|
|
06a2b0655e | ||
|
|
fa55bcfa79 | ||
|
|
6b48e86848 | ||
|
|
8d131131f7 | ||
|
|
478079ac74 | ||
|
|
38f40c1dc5 | ||
|
|
983f110ffa |
@@ -2,6 +2,7 @@
|
||||
Master:
|
||||
[](https://travis-ci.org/UniversityOfHelsinkiCS/oodikone2-frontend)
|
||||
|
||||
Trunk:
|
||||
[](https://travis-ci.org/UniversityOfHelsinkiCS/oodikone2-frontend)
|
||||
|
||||
|
||||
|
||||
@@ -50,6 +50,10 @@ export const dateFromApiToDisplay = date =>
|
||||
export const sortDatesWithFormat = (d1, d2, dateFormat) =>
|
||||
moment(d1, dateFormat) - moment(d2, dateFormat)
|
||||
|
||||
export const byDateDesc = (a, b) => new Date(b.date) - new Date(a.date)
|
||||
|
||||
export const byName = (a, b) => a.name.localeCompare(b.name)
|
||||
|
||||
/* This should be done in backend */
|
||||
export const removeInvalidCreditsFromStudent = student => ({
|
||||
...student,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Search, Dropdown, Header, List, Button } from 'semantic-ui-react'
|
||||
import CourseStatistics from '../CourseStatistics'
|
||||
import Timeout from '../Timeout'
|
||||
|
||||
import { byDateDesc, reformatDate, byName } from '../../common'
|
||||
import { findCourses } from '../../redux/courses'
|
||||
import { findCourseInstances, getCourseInstanceStatistics, removeInstance } from '../../redux/courseInstances'
|
||||
|
||||
@@ -24,14 +25,14 @@ class Courses extends Component {
|
||||
state = {
|
||||
isLoading: false,
|
||||
searchStr: '',
|
||||
selectedCourse: { name: 'No course', code: 'No code' }
|
||||
selectedCourse: { name: 'No course selected', code: '' }
|
||||
}
|
||||
|
||||
resetComponent = () => {
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
searchStr: '',
|
||||
selectedCourse: { name: 'No course', code: 'No code' }
|
||||
selectedCourse: { name: 'No course selected', code: '' }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -81,11 +82,12 @@ class Courses extends Component {
|
||||
render() {
|
||||
const { isLoading, searchStr, selectedCourse } = this.state
|
||||
const { courseInstances, selectedInstances } = this.props
|
||||
const courseList = this.props.courseList.map(course => ({ ...course, key: `${course.name}-${course.code}` }))
|
||||
|
||||
const instanceList = courseInstances ? courseInstances.map(instance => ({
|
||||
const courseList = this.props.courseList.sort(byName).map(course => ({ ...course, key: `${course.name}-${course.code}` }))
|
||||
|
||||
const instanceList = courseInstances ? courseInstances.sort(byDateDesc).map(instance => ({
|
||||
key: instance.id,
|
||||
text: `${instance.date} (${instance.students} students)`,
|
||||
text: `${reformatDate(instance.date, 'DD.MM.YYYY')} (${instance.students} students)`,
|
||||
value: instance.id
|
||||
})) : []
|
||||
|
||||
@@ -100,7 +102,6 @@ class Courses extends Component {
|
||||
{instance.date}
|
||||
</List.Item>))
|
||||
|
||||
// const t = this.props.translate;
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Search
|
||||
@@ -115,7 +116,7 @@ class Courses extends Component {
|
||||
/>
|
||||
|
||||
<Header as="h2">
|
||||
{selectedCourse.name}
|
||||
{selectedCourse.name} {selectedCourse.code ? `(${selectedCourse.code})` : ''}
|
||||
</Header>
|
||||
|
||||
<Dropdown
|
||||
@@ -135,7 +136,7 @@ class Courses extends Component {
|
||||
<CourseStatistics
|
||||
key={i.id}
|
||||
courseName={i.course.name}
|
||||
instanceDate={i.date}
|
||||
instanceDate={reformatDate(i.date, 'DD.MM.YYYY')}
|
||||
stats={i.statistics}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -18,7 +18,7 @@ class CreditAccumulationGraph extends Component {
|
||||
state = {
|
||||
combinedStudentData: undefined,
|
||||
studentCreditLines: [],
|
||||
intervalObject: undefined,
|
||||
timeout: undefined,
|
||||
loading: true
|
||||
}
|
||||
|
||||
@@ -26,61 +26,63 @@ class CreditAccumulationGraph extends Component {
|
||||
const { students } = this.props
|
||||
|
||||
const combinedStudentData = this.createCombinedStudentData(students)
|
||||
this.getMoreCreditLines()
|
||||
this.setState({ combinedStudentData, loading: true })
|
||||
const timeout = setTimeout(() => this.getMoreCreditLines(students), 1000)
|
||||
this.setState({ combinedStudentData, timeout, loading: true })
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.students) {
|
||||
const nextStudents = nextProps.students.map(student => student.studentNumber)
|
||||
const oldStudents = this.props.students.map(student => student.studentNumber)
|
||||
const changed = nextStudents.some(student => !oldStudents.includes(student)) ||
|
||||
oldStudents.some(student => !nextStudents.includes(student))
|
||||
if (changed) {
|
||||
const { students } = nextProps
|
||||
|
||||
const timeout = setTimeout(() => this.getMoreCreditLines(students), 1000)
|
||||
const combinedStudentData = this.createCombinedStudentData(students)
|
||||
this.setState({ combinedStudentData, timeout, loading: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.state.intervalObject) {
|
||||
clearInterval(this.state.intervalObject.interval)
|
||||
if (this.state.timeout) {
|
||||
clearInterval(this.state.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
getMoreCreditLines = () => {
|
||||
const { students } = this.props
|
||||
const { intervalObject } = this.state
|
||||
const MAX_SPLICES = Math.min(Math.ceil(students.length / 30), 1) // Made up
|
||||
if (!intervalObject) {
|
||||
const interval = setInterval(() => this.getMoreCreditLines(), 1000)
|
||||
this.setState({ intervalObject: { interval, tick: 0 } })
|
||||
return
|
||||
}
|
||||
const newInterval = { ...intervalObject, tick: 1 + intervalObject.tick }
|
||||
let loading = true
|
||||
if (newInterval.tick === MAX_SPLICES) {
|
||||
clearInterval(intervalObject.interval)
|
||||
loading = false
|
||||
}
|
||||
getMoreCreditLines = (students) => {
|
||||
const studentCreditLines = this.state.studentCreditLines.concat(this.createStudentCreditLines(
|
||||
students.slice(
|
||||
this.state.studentCreditLines.length,
|
||||
Math.ceil(students.length / (MAX_SPLICES - intervalObject.tick))
|
||||
),
|
||||
students,
|
||||
this.isSingleStudentGraph(students)
|
||||
))
|
||||
|
||||
this.setState({ intervalObject: newInterval, studentCreditLines, loading })
|
||||
this.setState({ studentCreditLines, loading: false })
|
||||
}
|
||||
|
||||
getXAxisMonth = (date, startDate) =>
|
||||
Math.max(moment(date, API_DATE_FORMAT).diff(moment(startDate, API_DATE_FORMAT), 'days') / 30, 0)
|
||||
|
||||
getReferenceLineForStudent = (student) => {
|
||||
const { courses, started } = student
|
||||
const lastDate = moment(_.maxBy(courses, course => moment(course.date)).date)
|
||||
const lastMonth = Math.ceil(this.getXAxisMonth(lastDate, started))
|
||||
const lastCredits = lastMonth * (55 / 12)
|
||||
try {
|
||||
const { courses, started } = student
|
||||
const lastDate = moment(_.maxBy(courses, course => moment(course.date)).date)
|
||||
const lastMonth = Math.ceil(this.getXAxisMonth(lastDate, started))
|
||||
const lastCredits = lastMonth * (55 / 12)
|
||||
|
||||
return [{
|
||||
month: 0,
|
||||
referenceCredits: 0,
|
||||
date: reformatDate(started, DISPLAY_DATE_FORMAT)
|
||||
},
|
||||
{
|
||||
month: lastMonth,
|
||||
referenceCredits: lastCredits,
|
||||
date: reformatDate(lastDate, DISPLAY_DATE_FORMAT)
|
||||
}]
|
||||
return [{
|
||||
month: 0,
|
||||
referenceCredits: 0,
|
||||
date: reformatDate(started, DISPLAY_DATE_FORMAT)
|
||||
},
|
||||
{
|
||||
month: lastMonth,
|
||||
referenceCredits: lastCredits,
|
||||
date: reformatDate(lastDate, DISPLAY_DATE_FORMAT)
|
||||
}]
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -183,8 +185,12 @@ class CreditAccumulationGraph extends Component {
|
||||
const referenceData = this.getReferenceLineForStudent(students[0])
|
||||
combinedStudentData = combinedStudentData.concat(referenceData)
|
||||
}
|
||||
return combinedStudentData.sort((c1, c2) =>
|
||||
sortDatesWithFormat(c1.date, c2.date, DISPLAY_DATE_FORMAT))
|
||||
try {
|
||||
return combinedStudentData.sort((c1, c2) =>
|
||||
sortDatesWithFormat(c1.date, c2.date, DISPLAY_DATE_FORMAT))
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { isEmpty } from 'lodash'
|
||||
import StudentInfoCard from '../StudentInfoCard'
|
||||
import CreditAccumulationGraph from '../CreditAccumulationGraph'
|
||||
import SearchResultTable from '../SearchResultTable'
|
||||
import { removeInvalidCreditsFromStudent } from '../../common'
|
||||
import { removeInvalidCreditsFromStudent, byDateDesc, reformatDate } from '../../common'
|
||||
|
||||
import sharedStyles from '../../styles/shared'
|
||||
|
||||
@@ -45,7 +45,10 @@ class StudentDetails extends Component {
|
||||
return (
|
||||
<SearchResultTable
|
||||
headers={courseHeaders}
|
||||
rows={courseRows}
|
||||
rows={courseRows.sort(byDateDesc).map(c => ({
|
||||
...c,
|
||||
date: reformatDate(c.date, 'DD.MM.YYYY')
|
||||
}))}
|
||||
noResultText={translate('common.noResults')}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
},
|
||||
"studentStatistics": {
|
||||
"header" : ["Student statistics"],
|
||||
"searchPlaceholder": ["Search with a student number or name"],
|
||||
"searchPlaceholder": ["Search with a student number or name (surname firstname)"],
|
||||
"chartTitle" : ["Credit Accumulation"]
|
||||
},
|
||||
"tags": {
|
||||
|
||||
Reference in New Issue
Block a user