schedule-grid-js/www/assignment.js

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,
});
}
}