src/flux/student-store.js
import uniq from 'lodash/array/uniq'
import stringify from 'json-stable-stringify'
import Reflux from 'reflux'
import Immutable from 'immutable'
import range from 'lodash/utility/range'
import forEach from 'lodash/collection/forEach'
import Student from '../models/student'
import demoStudent from '../models/demo-student.json'
import studentActions from './student-actions'
// import notificationActions from './notification-actions'
import parseSIS from '../lib/parse-sis'
export const REFRESH_AREAS = 'gobbldygook/stores/student/refresh-areas'
export const REFRESH_COURSES = 'gobbldygook/stores/student/refresh-courses'
function cleanLocalStorage() {
localStorage.removeItem('activeStudentId')
localStorage.removeItem('student-v3.0a6')
}
const studentStore = Reflux.createStore({
listenables: studentActions,
init() {
this.students = Immutable.Map()
this.history = Immutable.Stack()
this.future = Immutable.Stack()
this._loadData()
},
getInitialState() {
return this.students
},
undo() {
if (this.history.size) {
// console.log('undoing...')
this.future = this.future.push(this.students)
this.students = this.history.first()
this.history = this.history.pop()
this._postChange()
}
},
redo() {
if (this.future.size) {
// console.log('redoing...')
this.history = this.history.push(this.students)
this.students = this.future.first()
this.future = this.future.pop()
this._postChange()
}
},
_preChange() {
this.history = this.history.push(this.students)
this.future = this.future.clear()
},
_postChange() {
// console.log('students', this.students)
this.students.forEach(student => student.save())
this._saveStudentIds()
this.trigger(this.students)
},
resetStudentToDemo(studentId) {
// console.info(`resetting student ${studentId} to the demo student`)
const rawStudent = demoStudent
rawStudent.id = studentId
const student = new Student(rawStudent)
this._preChange()
this.students = this.students.set(studentId, student)
this._postChange()
},
reloadStudents() {
const inMemory = this.students.map(s => s.id).toList()
const onDisk = Immutable.List(JSON.parse(localStorage.getItem('studentIds')))
if (!(inMemory.equals(onDisk))) {
this._loadData()
}
},
_saveStudentIds() {
localStorage.setItem('studentIds', stringify(this.students.map(s => s.id).toArray()))
},
_loadData(studentId) {
// console.log('studentStore._loadData')
// studentIds is a list of IDs we know about.
let studentIds =
// Get the list of students we know about, or the string 'null',
// if localStorage doesn't have the key 'studentIds'.
JSON.parse(localStorage.getItem('studentIds')) ||
// If that fails, grab the really old file 'student-v3.0a6'
['student-v3.0a6']
if (studentId) {
studentIds.push(studentId)
}
studentIds = uniq(studentIds)
// Fetch and load the students from their IDs
const localStudents = Immutable.List(studentIds)
// pull the students from localStorage
.map(id => [id, localStorage.getItem(id)])
// Remove any broken students from localStorage
.map(([id, rawStudent]) => {
if (rawStudent === '[object Object]') {
localStorage.removeItem(id)
}
return rawStudent
})
// filter out any that don't exist
.filterNot(rawStudent => rawStudent === null)
// and any that are bad
.filterNot(rawStudent => rawStudent === '[object Object]')
// then process them
.map(rawStudent => {
// basicStudent defaults to an empty object so that the constructor
// has something to build from.
let basicStudent = {}
try {
basicStudent = JSON.parse(rawStudent)
}
catch (e) {
// notificationActions.logError('error parsing student', basicStudent)
console.error(e)
}
if (basicStudent.id === 'student-v3.0a6') {
delete basicStudent.id
}
// Make the student…
const fleshedStudent = new Student(basicStudent)
// and save them, of course
fleshedStudent.save()
return fleshedStudent
})
.groupBy(student => student.id)
.map(student => student.get(0))
// Add them to students
this.students = this.students.merge(localStudents)
// Update the studentIds list from the current list of students
this._saveStudentIds()
// Clean up localStorage
cleanLocalStorage()
this._postChange()
},
initStudent() {
const fleshedStudent = new Student().withMutations(student => {
forEach(range(student.matriculation, student.graduation), year => {
student = student.addSchedule({year, index: 1, active: true, semester: 1})
student = student.addSchedule({year, index: 1, active: true, semester: 2})
student = student.addSchedule({year, index: 1, active: true, semester: 3})
})
})
fleshedStudent.save()
this._preChange()
this._loadData(fleshedStudent.id)
},
refreshData({areas=false, courses=false}) {
if (areas) {
this.emitter.emit(REFRESH_AREAS)
}
if (courses) {
this.emitter.emit(REFRESH_AREAS)
}
if (areas || courses) {
this.students = this.students.withMutations(students => {
return students.map(s => s.checkGraduatability())
})
}
},
importStudent({data, type}) {
let stu = undefined
if (type === 'application/json') {
try {
stu = JSON.parse(data)
}
catch (err) {
throw err
}
}
else if (type === 'text/html') {
const parser = new DOMParser()
let html
try {
html = parser.parseFromString(data, 'text/html')
}
catch (err) {
throw err
}
try {
stu = parseSIS(html)
}
catch (err) {
throw err
}
}
if (stu) {
this._preChange()
const fleshedStudent = new Student(stu)
fleshedStudent.save()
this._loadData(fleshedStudent.id)
}
},
destroyStudent(studentId) {
this._preChange()
this.students = this.students.delete(studentId)
localStorage.removeItem(studentId)
this._saveStudentIds()
this._postChange()
},
_change(studentId, method, ...args) {
this._preChange()
this.students = this.students.set(studentId, this.students.get(studentId)[method](...args))
this.students = this.students.set(studentId, this.students.get(studentId).checkGraduatability())
this._postChange()
},
_alter(pathToData, method, ...args) {
this._preChange()
const studentId = pathToData[0]
this.students = this.students.setIn(pathToData, this.students.getIn(pathToData)[method](...args))
this.students = this.students.set(studentId, this.students.get(studentId).checkGraduatability())
this._postChange()
},
/* eslint-disable no-multi-spaces, brace-style */
changeName(studentId, ...args) { this._change(studentId, 'changeName', ...args) },
changeAdvisor(studentId, ...args) { this._change(studentId, 'changeAdvisor', ...args) },
changeCreditsNeeded(studentId, ...args) { this._change(studentId, 'changeCreditsNeeded', ...args) },
changeMatriculation(studentId, ...args) { this._change(studentId, 'changeMatriculation', ...args) },
changeGraduation(studentId, ...args) { this._change(studentId, 'changeGraduation', ...args) },
changeSetting(studentId, key, value) { this._change(studentId, 'changeSetting', key, value) },
addArea(studentId, ...args) { this._change(studentId, 'addArea', ...args) },
addSchedule(studentId, ...args) { this._change(studentId, 'addSchedule', ...args) },
addFabrication(studentId, ...args) { this._change(studentId, 'addFabrication', ...args) },
setOverride(studentId, ...args) { this._change(studentId, 'setOverride', ...args) },
removeOverride(studentId, ...args) { this._change(studentId, 'removeOverride', ...args) },
removeArea(studentId, ...args) { this._change(studentId, 'removeArea', ...args) },
removeMultipleAreas(studentId, ...args) { this._change(studentId, 'removeMultipleAreas', ...args) },
destroySchedule(studentId, ...args) { this._change(studentId, 'destroySchedule', ...args) },
destroyMultipleSchedules(studentId, ...args) { this._change(studentId, 'destroyMultipleSchedules', ...args) },
moveCourse(studentId, ...args) { this._change(studentId, 'moveCourse', ...args) },
editArea(studentId, areaId, ...args) { this._change(studentId, 'editArea', areaId, ...args) },
renameSchedule(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'rename', ...args) },
reorderSchedule(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'reorder', ...args) },
moveSchedule(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'move', ...args) },
addCourse(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'addCourse', ...args) },
removeCourse(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'removeCourse', ...args) },
reorderCourse(studentId, scheduleId, ...args) { this._alter([studentId, 'schedules', scheduleId], 'reorderCourse', ...args) },
reorderArea(studentId, areaId, ...args) { this._alter([studentId, 'studies', areaId], 'reorder', ...args) },
/* eslint-enable no-multi-spaces, brace-style */
})
window.store = studentStore
export default studentStore