Commit 98b1aa43 authored by Zac Wood's avatar Zac Wood

giving up on generating schedules, pushing back to v3

parent b32595db
Pipeline #3285 failed with stage
in 2 minutes and 12 seconds
class Cart {
constructor() {
this.isOpen = false;
this._courses = {};
this._courses = [];
const cartData = document.getElementById('cart-data');
if (cartData) {
this._courses = JSON.parse(cartData.dataset.cart);
}
}
toggle() {
......@@ -24,36 +29,15 @@ class Cart {
for (const courseId in this._courses) {
if (this._courses[courseId].length === 0) delete this._courses[courseId];
}
console.log(courses);
document.getElementById('course-counter').innerText = Object.keys(this._courses).length;
}
async addSections(sections) {
const resp = await fetch(`/sessions/cart?course_id=${sections[0].cid}&section_ids=${sections.map(s => s.id).join(',')}`, { cache: 'no-store' });
async addSection(section) {
const resp = await fetch(`/sessions/cart?&section_id=${section.id}`, { cache: 'no-store' });
const json = await resp.json();
this.courses = json;
}
async addPair(sections) {
const resp = await fetch(`/sessions/cart?course_id=${sections[0].cid}&pair_ids=${sections[0].id},${sections[1].id}`, { cache: 'no-store' });
const json = await resp.json();
this.courses = json;
}
includesPair(pair) {
const ids = pair.map(p => p.id);
for (const courseId in this._courses) {
const pairs = this._courses[courseId];
if (!Array.isArray(pairs[0])) continue;
for (const otherPair of pairs) {
if (JSON.stringify(ids) == JSON.stringify(otherPair)) return true;
}
}
return false;
}
includesSection(obj) {
for (const key in this._courses) {
const list = this._courses[key];
......
......@@ -4,7 +4,6 @@ document.addEventListener('DOMContentLoaded', () => {
const eventsJSON = eventsTemplate.dataset.events;
const events = JSON.parse(eventsJSON);
window.events = events;
console.log(events);
$('#calendar').fullCalendar({
defaultDate: new Date(2019, 0, 14),
defaultView: 'agendaWeek',
......@@ -12,30 +11,27 @@ document.addEventListener('DOMContentLoaded', () => {
events: renderEvents,
});
document.getElementById('numSchedules').innerText = window.events.length;
// document.getElementById('numSchedules').innerText = window.events.length;
}
});
let i = 0;
// let i = 0;
const renderEvents = (start, end, timezone, callback) => {
console.log(window.events[i]);
document.getElementById('currentSchedule').innerText = i + 1;
callback(window.events[i]);
// document.getElementById('currentSchedule').innerText = i + 1;
callback(window.events);
};
const nextSchedule = () => {
if (i + 1 < window.events.length) i++;
// const nextSchedule = () => {
// if (i + 1 < window.events.length) i++;
$('#calendar').fullCalendar('refetchEvents');
// $('#calendar').fullCalendar('refetchEvents');
// };
console.log(window.events[i]);
};
const prevSchedule = () => {
if (i > 0) i--;
// const prevSchedule = () => {
// if (i > 0) i--;
$('#calendar').fullCalendar('refetchEvents');
// $('#calendar').fullCalendar('refetchEvents');
console.log(window.events[i]);
};
// console.log(window.events[i]);
// };
......@@ -3,21 +3,20 @@
const sectionWithCrn = crn => document.getElementById('search-list').querySelector(`[data-crn="${crn}"]`);
const addCourse = async (event, id) => {
event.stopPropagation();
const courseCard = document.getElementById(`course-${id}`);
const title = courseCard.querySelector('.title').innerText;
const sectionsItems = Array.from(courseCard.querySelectorAll('li'));
const filtered = sectionsItems.filter(li => {
return !li.parentNode.classList.contains('pair') || li.dataset.type === 'Lecture';
});
for (const section of filtered) {
await addOrRemoveFromCart(undefined, section);
}
};
// const addCourse = async (event, id) => {
// event && event.stopPropagation();
// const courseCard = document.getElementById(`course-${id}`);
// const title = courseCard.querySelector('.title').innerText;
// const sectionsItems = Array.from(courseCard.querySelectorAll('li'));
// const filtered = sectionsItems.filter(li => {
// return !li.parentNode.classList.contains('pair') || li.dataset.type === 'Lecture';
// });
// for (const section of filtered) {
// await addOrRemoveFromCart(undefined, section);
// }
// }
/**
* Either adds or removes a section from the cart depending on
* if it is currently in the cart.
......@@ -26,47 +25,12 @@ const addOrRemoveFromCart = async (event, sectionNode) => {
event && event.stopPropagation();
const section = { ...sectionNode.dataset };
const parent = sectionNode.parentNode;
if (parent.classList.contains('pair')) {
const otherNode = Array.from(parent.children).filter(c => c != sectionNode)[0];
const other = { ...otherNode.dataset };
let pair;
if (section.type == 'Lecture') {
pair = [section, other];
await this.cart.addPair(pair);
} else {
pair = [other, section];
await this.cart.addPair(pair);
}
if (this.cart.includesPair(pair)) {
console.log('found');
[sectionNode, otherNode].forEach(s => s.classList.add('selected'));
} else {
console.log('not found');
[sectionNode, otherNode].forEach(s => s.classList.remove('selected'));
}
await this.cart.addSection(section);
if (this.cart.includesSection(section)) {
sectionNode.classList.add('selected');
} else {
await this.cart.addSections([section]);
if (this.cart.includesSection(section)) {
sectionNode.classList.add('selected');
} else {
sectionNode.classList.remove('selected');
}
}
};
/**
* Removes a given section from the cart
* @param {Node} DOM Node of the Section in the cart
*/
const removeFromCart = section => {
const sectionInSearch = sectionWithCrn(section.dataset.crn);
if (sectionInSearch) {
sectionInSearch.classList.remove('selected');
sectionNode.classList.remove('selected');
}
this.cart.removeFromSchedule(section.dataset.crn);
};
/**
......@@ -87,5 +51,5 @@ const toggleSections = course => {
* and sets the link in the modal to it.
*/
const setUrlInModal = () => {
document.getElementById('calendar-link').innerText = `https://${window.location.hostname}/api/schedule?crns=${this.cart.ids.join(',')}`;
document.getElementById('calendar-link').innerText = `${window.location.protocol}//${window.location.hostname}/api/schedule?crns=${this.cart._courses.join(',')}`;
};
......@@ -12,3 +12,12 @@
#cart {
display: none;
}
#cart-button {
color: black;
}
#cart-button:hover {
transition: 0.15s;
color: green;
}
......@@ -12,17 +12,12 @@ class ApplicationController < ActionController::Base
end
def set_cart
sections = cookies[:section_ids].split(',').map do |id|
CourseSection.find_by_id(id)
end
@cart = sections.group_by do |s|
s.course.id
end
@cart = JSON.parse(cookies[:cart])
end
def set_cookies
cookies[:crns] = "" if cookies[:crns].nil?
cookies[:section_ids] = "" if cookies[:section_ids].nil?
cookies[:cart] = "[]" if cookies[:cart].nil?
end
end
......@@ -15,22 +15,8 @@ class SchedulesController < ApplicationController
include SchedulesHelper
def show
combined = {}
@cart.each do |cid, sections|
combined[cid] = []
sections.each do |section|
end
end
courses = @cart.values.group_by do |s|
s.course.id
end
puts courses.keys
id_sets = generate_schedules(@cart.values)
@events = generate_fullcalender_events(id_sets)
@all = @cart.map { |id| CourseSection.find_by_id id }
@events = generate_fullcalender_events(@cart)
end
# this works(?)
......
......@@ -8,48 +8,20 @@ class SessionsController < ApplicationController
end
def cart
cart = if cookies[:cart].nil?
{}
else
JSON.parse cookies[:cart]
end
course_id, section_ids, pair_ids = params[:course_id], params[:section_ids], params[:pair_ids]
cart[course_id] ||= []
unless section_ids.nil?
ids = section_ids.split(',')
ids.each do |section_id|
if cart[course_id].include?(section_id)
cart[course_id] = cart[course_id].reject do |a|
a == section_id
end
else
cart[course_id].push(section_id)
end
end
end
unless pair_ids.nil?
pair = pair_ids.split(',')
if cart[course_id].include?(pair)
cart[course_id] = cart[course_id].reject do |a|
a == pair
end
else
cart[course_id].push(pair)
end
section_id = params[:section_id]
if @cart.include?(section_id)
@cart.reject! { |id| section_id == id }
else
@cart << section_id
end
to_delete = cart.keys.select do |cid|
cart[cid].empty?
end
to_delete.each { |key| cart.delete(key) }
cookies[:cart] = cart.to_json
render json: cart.to_json
puts @cart.to_json
cookies[:cart] = @cart.to_json
render json: @cart.to_json
end
private
......
module ApplicationHelper
def in_cart?(id)
@cart.select { |_cid, sections| sections.select { |s| s.id == id }.count.positive? }.count.positive?
end
end
......@@ -9,49 +9,20 @@ module SchedulesHelper
"U": Date.new(2019, 1, 20)
}.freeze
def generate_fullcalender_events(id_sets)
id_sets.map do |id_set|
id_set.to_a.map do |s|
s.days.split('').map do |day|
formatted_date = DAYS[day.to_sym].to_s.tr('-', '')
time = Time.parse(s.start_time).strftime("%H%M%S")
endtime = Time.parse(s.end_time).strftime("%H%M%S")
def generate_fullcalender_events(section_ids)
section_ids.map do |id|
s = CourseSection.find_by_id id
s.days.split('').map do |day|
formatted_date = DAYS[day.to_sym].to_s.tr('-', '')
time = Time.parse(s.start_time).strftime("%H%M%S")
endtime = Time.parse(s.end_time).strftime("%H%M%S")
{
title: s.name,
start: "#{formatted_date}T#{time}",
end: "#{formatted_date}T#{endtime}"
}
end
end.flatten
end
end
def generate_schedules(all_sections)
recur_build(all_sections, 0, Set.new).flatten!.select do |s|
s.to_a.size == all_sections.count
end
end
def recur_build(all_sections, i, set)
num_courses = all_sections.count
course_sections = all_sections[i]
course_sections.map do |section|
new_set = Set.new(set)
fits = true
set.to_a.each do |s|
fits = !section.overlaps?(s)
break if !fits
end
new_set << section if fits
if i == num_courses - 1
new_set
else
recur_build(all_sections, i + 1, new_set)
{
title: s.name,
start: "#{formatted_date}T#{time}",
end: "#{formatted_date}T#{endtime}"
}
end
end
end.flatten
end
end
module SearchHelper
def in_cart?(id)
@cart.include? id.to_s
end
class GenericQueryData
attr_reader :semester
attr_reader :sort_mode
......
<button onclick="nextSchedule()">Next</button>
<button onclick="prevSchedule()">Prev</button>
<span id="currentSchedule">1</span> / <span id="numSchedules">1</span>
<!-- <button onclick="nextSchedule()">Next</button>
<button onclick="prevSchedule()">Prev</button>
<span id="currentSchedule">1</span> / <span id="numSchedules">1</span> -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exportModal" onclick="setUrlInModal()">
Generate Schedule
</button>
<div id="calendar"></div>
<template id="events" data-events="<%= @events.to_json %>"></template>
<hr />
<h2>Selected Courses</h2>
<%= render partial: 'shared/section', collection: @all %>
<!-- Export Modal -->
<div class="modal fade" id="exportModal" tabindex="-1" role="dialog" aria-labelledby="exportModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exportModalLabel">Your calendar has been generated!</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<h5>Apple Calendar</h5>
To add your schedule to Apple Calendar, click the "Add to calendar" button below. If you are on a device
running macOS or iOS, this will open a dialogue which will walk you through adding the calendar.
<hr />
<h5>Google Calendar</h5>
<strong>On desktop:</strong>
<br />
Open your <a href="https://calendar.google.com/">Google Calendar</a>. Click the "Settings" button in the top
right, and then click the Settings tab. In the menu on the left, click "Add calendar" and "From URL". Now,
paste the following link inside the text box: <br />
<code id="calendar-link"></code>
<br />
<strong>On mobile (Android only):</strong>
<br />
Click the "Download calendar file" button. This will download the calendar file which you may then open and
add to your calendar.
<hr />
<h5>.ics file</h5>
To download a .ics file containing your schedule, click the "Download calendar file" button below.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="downloadIcs()">Download calendar file</button>
<button type="button" class="btn btn-primary" onclick="addToSystemCalendar()">Add to system calendar</button>
</div>
</div>
</div>
</div>
</div>
<!-- List of sections in the cart -->
<div class="col order-1 order-lg-1" id="cart">
<div class="card">
<div class="card-body">
<h3 class="card-title"><%= link_to 'Your Cart', schedule_path %></h3>
</div>
<ul class="list-group list-group-flush" id="cart-courses">
<% @cart.each do |cid, sections| %>
<% course = Course.find_by_id(cid) %>
<li id="schedule-<%= cid %>" class="list-group-item" onclick="removeCourse(<%= cid %>)">
<div class="cart-course">
<b class="title"><%= "#{course.subject} #{course.course_number}" %></b>
<!-- <span class="crns">
<%= sections.map { |s| "##{s.crn}" }.join(', ') %>
</span> -->
</div>
</li>
<% end %>
</ul>
</div>
</div>
<template id="cart-data">
<% @cart.each do |cid, sections| %>
<% course = Course.find_by_id cid %>
<div data-id="<%= cid %>" data-title="<%= "#{course.subject} #{course.course_number}" %>">
<% sections.each do |s| %>
<div data-id="<%= s.id %>" data-crn="<%= s.crn %>"></div>
<% end %>
</div>
<% end %>
</template>
</div>
</div>
<!-- Export Modal -->
<div class="modal fade" id="exportModal" tabindex="-1" role="dialog" aria-labelledby="exportModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exportModalLabel">Your calendar has been generated!</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<h5>Apple Calendar</h5>
To add your schedule to Apple Calendar, click the "Add to calendar" button below. If you are on a device
running macOS or iOS, this will open a dialogue which will walk you through adding the calendar.
<hr />
<h5>Google Calendar</h5>
<strong>On desktop:</strong>
<br />
Open your <a href="https://calendar.google.com/">Google Calendar</a>. Click the "Settings" button in the top
right, and then click the Settings tab. In the menu on the left, click "Add calendar" and "From URL". Now,
paste the following link inside the text box: <br />
<code id="calendar-link"></code>
<br />
<strong>On mobile (Android only):</strong>
<br />
Click the "Download calendar file" button. This will download the calendar file which you may then open and
add to your calendar.
<hr />
<h5>.ics file</h5>
To download a .ics file containing your schedule, click the "Download calendar file" button below.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="downloadIcs()">Download calendar file</button>
<button type="button" class="btn btn-primary" onclick="addToSystemCalendar()">Add to system calendar</button>
</div>
</div>
</div>
</div>
<template id="cart-data" data-cart="<%= @cart.to_json %>"></template>
......@@ -4,14 +4,16 @@
<div class="card-header">
<div class="row">
<div class="col">
<h4 class="title"><%= "#{course.subject} #{course.course_number}" %></h4>
<%= link_to course do %>
<h4 class="title"><%= "#{course.subject} #{course.course_number}" %></h4>
<% end %>
</div>
<div class="col">
<h4 id="add-course-btn" class="text-right" onclick="addCourse(event, '<%= course.id %>');">
<i class="fas fa-plus" style="color: green"></i>
</h4>
</div>
<!-- <div class="col">
<h4 id="add-course-btn" class="text-right" onclick="addCourse(event, '<%= course.id %>');">
<i class="fas fa-plus" style="color: green"></i>
</h4>
</div> -->
</div>
<h5><em><%= course.title %></em></h5>
</div>
......@@ -47,19 +49,8 @@
<!-- List of Course Sections -->
<div class="list-group list-group-flush sections" style="display: <%= expanded ? "flex" : "none" %>">
<% if course.has_labs? && !course.lab_course? %>
<% course.course_sections.each do |section| %>
<% lecture_list = section.labs %>
<% unless lecture_list.nil? %>
<% lecture_list.each do |list| %>
<div class="pair">
<%= render partial: 'shared/section', object: list.first, locals: { course: course } %>
<%= render partial: 'shared/section', object: list.last, locals: { course: course } %>
</div>
<hr />
<% end %>
<% end %>
<% end %>
<% if defined?(@instructor) %>
<%= render partial: 'shared/section', collection: course.course_sections.where(instructor: @instructor), locals: { course: course } %>
<% else %>
<%= render partial: 'shared/section', collection: course.course_sections, locals: { course: course } %>
<% end %>
......
......@@ -17,18 +17,20 @@
</div>
</div>
<div class="col-sm align-center order-0 order-sm-1" onclick="window.cart.toggle()">
<h1 style="margin-top:24px">
<span class="fa-layers fa-fw" id="schedule-icon">
<i class="fas fa-shopping-cart"></i>
<span class="fa-layers fa-fw">
<i class="fas fa-circle" data-fa-transform="shrink-3 up-12 right-12" style="color:gray"></i>
<span id="course-counter" class="fa-layers-text fa-inverse" data-fa-transform="shrink-10 up-12 right-12" style="font-weight:600">
<%= @cart.length %>
<div class="col-sm align-center order-0 order-sm-1">
<a id="cart-button" href="<%= schedule_path %>">
<h1 style="margin-top:24px">
<span class="fa-layers fa-fw" id="schedule-icon">
<i class="fas fa-shopping-cart"></i>
<span class="fa-layers fa-fw">
<i class="fas fa-circle" data-fa-transform="shrink-3 up-12 right-12" style="color:gray"></i>
<span id="course-counter" class="fa-layers-text fa-inverse" data-fa-transform="shrink-10 up-12 right-12" style="font-weight:600">
<%= @cart.length %>
</span>
</span>
</span>
</span>
</h1>
</h1>
</a>
</div>
<div class="col order-1 order-sm-0">
......
......@@ -2,7 +2,7 @@
class="list-group-item section-item <%= "selected" if in_cart? section.id %>"
data-crn="<%= section.crn %>"
data-id="<%= section.id %>"
data-cid="<%= course.id %>"
data-cid="<%= course.id if defined?(course) %>"
data-type="<%= section.section_type %>"
onclick="addOrRemoveFromCart(event, this)"
>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment