213 lines
6.3 KiB
JavaScript
213 lines
6.3 KiB
JavaScript
import Event from './event.js'
|
|
import * as utils from './utils.js';
|
|
|
|
export default class Assignment {
|
|
constructor(obj) {
|
|
this.original_assignment = obj.original_assignment;
|
|
this.location = obj.location;
|
|
this.start_time = new utils.Time(obj.start_time);
|
|
this.end_time = new utils.Time(obj.end_time);
|
|
this.notes = obj.notes;
|
|
this.track = obj.track;
|
|
this.days = obj.days;
|
|
this.people_by_day = obj.people_by_day;
|
|
this.squishable = obj.squishable ?? false;
|
|
|
|
if (!this.people_by_day) {
|
|
this.people_by_day = {};
|
|
for (const day of this.days) {
|
|
this.people_by_day[day] = {staff: [], students: []};
|
|
}
|
|
} else if ('all_days' in this.people_by_day) {
|
|
const only = this.people_by_day['all_days'];
|
|
this.people_by_day = {};
|
|
for (const day of this.days) {
|
|
this.people_by_day[day] = {...only};
|
|
}
|
|
}
|
|
|
|
const expectedDays = Object.keys(this.people_by_day);
|
|
if (!this.days || !utils.setEqual(this.days, expectedDays)) {
|
|
// This might not be in order; shouldn't matter.
|
|
this.days = expectedDays;
|
|
}
|
|
|
|
this.all_staff = new Set(Object.values(this.people_by_day)
|
|
.flatMap(day => day['staff']));
|
|
this.all_students = new Set(Object.values(this.people_by_day)
|
|
.flatMap(day => day['students']));
|
|
}
|
|
|
|
asJsonObject() {
|
|
return {
|
|
'location': this.location,
|
|
'start_time': this.start_time.asJsonObject(),
|
|
'end_time': this.end_time.asJsonObject(),
|
|
'notes': this.notes,
|
|
'track': this.track,
|
|
'days': this.days,
|
|
'people_by_day': this.people_by_day,
|
|
'squishable': this.squishable,
|
|
}
|
|
}
|
|
|
|
asJson() {
|
|
return JSON.stringify(this.asJsonObject(), null, 2);
|
|
}
|
|
|
|
cloneWithoutTrack() {
|
|
return new Assignment(
|
|
{...this.asJsonObject(),
|
|
track: undefined,
|
|
original_assignment: this.original_assignment ?? this});
|
|
}
|
|
|
|
get timeRange() {
|
|
return new utils.TimeRange(this);
|
|
}
|
|
|
|
toString() {
|
|
return "[" + this.days.join('') + "] (" + this.start_time.to12HourString()
|
|
+ "-" + this.end_time.to12HourString() + ") " + this.location;
|
|
}
|
|
|
|
isOnRowForTime(time) {
|
|
return this.start_time.cmp(time) <= 0 && this.end_time.cmp(time) > 0;
|
|
}
|
|
|
|
hasPersonOnDay(person, day) {
|
|
if (!(day in this.people_by_day)) {
|
|
return false;
|
|
}
|
|
|
|
if (this.isOnEverySchedule()) return true;
|
|
|
|
return this.hasPersonExplicitlyOnDay(person, day);
|
|
}
|
|
|
|
hasPersonExplicitlyOnDay(person, day) {
|
|
if (!(day in this.people_by_day)) {
|
|
return false;
|
|
}
|
|
|
|
const kind = person.kind === 'student' ? 'students' : person.kind;
|
|
const byKind = this.people_by_day[day][kind];
|
|
return byKind.includes(person.name);
|
|
}
|
|
|
|
isOnEverySchedule() {
|
|
if (this.track === 'all') return true;
|
|
if (!person) return true;
|
|
|
|
// Squishable assignments with no names are for everyone.
|
|
if (this.squishable
|
|
&& Object.values(this.people_by_day)
|
|
.flatMap(forDay => [].concat(...Object.values(forDay)))
|
|
.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
hasPersonExplicitlyOnAnyDay(person) {
|
|
const kind = person.kind === 'student' ? 'students' : person.kind;
|
|
return Object.values(this.people_by_day)
|
|
.some(byDay => byDay[kind].includes(person.name));
|
|
}
|
|
|
|
asEvent(day, granularity, location_only) {
|
|
const people = this.people_by_day[day];
|
|
const staff = people['staff'] ?? [];
|
|
const students = people['students'] ?? [];
|
|
const num_people = staff.length + students.length;
|
|
|
|
let compact_mode = false;
|
|
if (granularity) {
|
|
const row_count = this.timeRange.duration.dividedBy(granularity);
|
|
const needed_rows = location_only ? 2 : 2 + num_people;
|
|
compact_mode = needed_rows > row_count;
|
|
}
|
|
|
|
const classes = [];
|
|
if (compact_mode) classes.push('compact');
|
|
|
|
const description = document.createElement('span');
|
|
description.classList.add('event_description');
|
|
|
|
const locationSpan = document.createElement('span');
|
|
locationSpan.innerText = this.location;
|
|
locationSpan.classList.add('location');
|
|
|
|
if (location_only) {
|
|
classes.push('location_only');
|
|
description.appendChild(locationSpan);
|
|
description.appendChild(document.createElement('br'));
|
|
} else {
|
|
if (compact_mode) locationSpan.classList.add('compact');
|
|
description.appendChild(locationSpan);
|
|
if (!compact_mode || num_people > 0) {
|
|
description.appendChild(document.createElement('br'));
|
|
}
|
|
|
|
function addNames(names, className) {
|
|
let first = true;
|
|
for (const name of names.sort()) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
if (compact_mode) {
|
|
description.appendChild(document.createTextNode(',\n'));
|
|
} else {
|
|
description.appendChild(document.createElement('br'));
|
|
description.appendChild(document.createTextNode('\n'));
|
|
}
|
|
}
|
|
const nameSpan = document.createElement('span');
|
|
nameSpan.classList.add(className);
|
|
if (compact_mode) nameSpan.classList.add('compact');
|
|
nameSpan.innerText = name;
|
|
description.appendChild(nameSpan);
|
|
}
|
|
}
|
|
|
|
addNames(staff, 'staff');
|
|
|
|
description.appendChild(document.createElement('br'));
|
|
description.appendChild(document.createTextNode('\n'));
|
|
|
|
addNames(students, 'student');
|
|
|
|
if (this.notes) {
|
|
const notesSpan = document.createElement('span');
|
|
notesSpan.classList.add('notes');
|
|
notesSpan.innerText = this.notes;
|
|
|
|
if (compact_mode) {
|
|
notesSpan.classList.add('compact');
|
|
description.appendChild(document.createTextNode('\n'));
|
|
} else {
|
|
description.appendChild(document.createElement('br'));
|
|
}
|
|
|
|
description.appendChild(notesSpan);
|
|
}
|
|
}
|
|
|
|
let originalAssignment = this;
|
|
while (originalAssignment.original_assignment) {
|
|
originalAssignment = originalAssignment.original_assignment;
|
|
}
|
|
|
|
return new Event({
|
|
'assignment': originalAssignment,
|
|
'start_time': this.start_time,
|
|
'end_time': this.end_time,
|
|
'track': this.track,
|
|
'description': description,
|
|
'classes': classes,
|
|
'title': this.location,
|
|
});
|
|
}
|
|
}
|