Commit 00d6edf9 authored by Zac Wood's avatar Zac Wood

Added bulk add feature and fixes

parent 32dff2fd
Pipeline #3471 passed with stages
in 12 minutes and 38 seconds
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (5.1.6) actioncable (5.1.6.1)
actionpack (= 5.1.6) actionpack (= 5.1.6.1)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (~> 0.6.1) websocket-driver (~> 0.6.1)
actionmailer (5.1.6) actionmailer (5.1.6.1)
actionpack (= 5.1.6) actionpack (= 5.1.6.1)
actionview (= 5.1.6) actionview (= 5.1.6.1)
activejob (= 5.1.6) activejob (= 5.1.6.1)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (5.1.6) actionpack (5.1.6.1)
actionview (= 5.1.6) actionview (= 5.1.6.1)
activesupport (= 5.1.6) activesupport (= 5.1.6.1)
rack (~> 2.0) rack (~> 2.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2) rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.1.6) actionview (5.1.6.1)
activesupport (= 5.1.6) activesupport (= 5.1.6.1)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3) rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.1.6) activejob (5.1.6.1)
activesupport (= 5.1.6) activesupport (= 5.1.6.1)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (5.1.6) activemodel (5.1.6.1)
activesupport (= 5.1.6) activesupport (= 5.1.6.1)
activerecord (5.1.6) activerecord (5.1.6.1)
activemodel (= 5.1.6) activemodel (= 5.1.6.1)
activesupport (= 5.1.6) activesupport (= 5.1.6.1)
arel (~> 8.0) arel (~> 8.0)
activesupport (5.1.6) activesupport (5.1.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
minitest (~> 5.1) minitest (~> 5.1)
tzinfo (~> 1.1) tzinfo (~> 1.1)
addressable (2.5.2) addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0) public_suffix (>= 2.0.2, < 4.0)
apipie-rails (0.5.10) apipie-rails (0.5.14)
rails (>= 4.1) rails (>= 4.1)
arel (8.0.0) arel (8.0.0)
ast (2.4.0) ast (2.4.0)
...@@ -57,81 +57,87 @@ GEM ...@@ -57,81 +57,87 @@ GEM
childprocess (0.9.0) childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11) ffi (~> 1.0, >= 1.0.11)
coderay (1.1.2) coderay (1.1.2)
concurrent-ruby (1.0.5) concurrent-ruby (1.1.3)
crass (1.0.4) crass (1.0.4)
erubi (1.7.1) erubi (1.7.1)
ffi (1.9.25) ffi (1.9.25)
globalid (0.4.1) globalid (0.4.1)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
httparty (0.16.2) httparty (0.16.3)
mime-types (~> 3.0)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
i18n (1.0.1) i18n (1.2.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
icalendar (2.4.1) icalendar (2.5.2)
ice_cube (~> 0.16)
ice_cube (0.16.3)
jaro_winkler (1.5.1) jaro_winkler (1.5.1)
jbuilder (2.7.0) jbuilder (2.8.0)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
multi_json (>= 1.2) multi_json (>= 1.2)
listen (3.1.5) listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7) rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2) ruby_dep (~> 1.2)
loofah (2.2.2) loofah (2.2.3)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0) mail (2.7.1)
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
maruku (0.7.3) maruku (0.7.3)
method_source (0.9.0) method_source (0.9.2)
mini_mime (1.0.0) mime-types (3.2.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2018.0812)
mini_mime (1.0.1)
mini_portile2 (2.3.0) mini_portile2 (2.3.0)
minitest (5.11.3) minitest (5.11.3)
multi_json (1.13.1) multi_json (1.13.1)
multi_xml (0.6.0) multi_xml (0.6.0)
nio4r (2.3.1) nio4r (2.3.1)
nokogiri (1.8.3) nokogiri (1.8.5)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
parallel (1.12.1) parallel (1.12.1)
parser (2.5.1.2) parser (2.5.3.0)
ast (~> 2.4.0) ast (~> 2.4.0)
powerpack (0.1.2) powerpack (0.1.2)
pry (0.11.3) pry (0.12.2)
coderay (~> 1.1.0) coderay (~> 1.1.0)
method_source (~> 0.9.0) method_source (~> 0.9.0)
pry-doc (0.13.4) pry-doc (0.13.5)
pry (~> 0.11) pry (~> 0.11)
yard (~> 0.9.11) yard (~> 0.9.11)
public_suffix (3.0.2) public_suffix (3.0.3)
puma (3.11.4) puma (3.12.0)
rack (2.0.5) rack (2.0.6)
rack-cors (1.0.2) rack-cors (1.0.2)
rack-test (1.0.0) rack-test (1.1.0)
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rails (5.1.6) rails (5.1.6.1)
actioncable (= 5.1.6) actioncable (= 5.1.6.1)
actionmailer (= 5.1.6) actionmailer (= 5.1.6.1)
actionpack (= 5.1.6) actionpack (= 5.1.6.1)
actionview (= 5.1.6) actionview (= 5.1.6.1)
activejob (= 5.1.6) activejob (= 5.1.6.1)
activemodel (= 5.1.6) activemodel (= 5.1.6.1)
activerecord (= 5.1.6) activerecord (= 5.1.6.1)
activesupport (= 5.1.6) activesupport (= 5.1.6.1)
bundler (>= 1.3.0) bundler (>= 1.3.0)
railties (= 5.1.6) railties (= 5.1.6.1)
sprockets-rails (>= 2.0.0) sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3) rails-dom-testing (2.0.3)
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
nokogiri (>= 1.6) nokogiri (>= 1.6)
rails-html-sanitizer (1.0.4) rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2) loofah (~> 2.2, >= 2.2.2)
railties (5.1.6) railties (5.1.6.1)
actionpack (= 5.1.6) actionpack (= 5.1.6.1)
activesupport (= 5.1.6) activesupport (= 5.1.6.1)
method_source method_source
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
rainbow (3.0.0) rainbow (3.0.0)
rake (12.3.1) rake (12.3.2)
rb-fsevent (0.10.3) rb-fsevent (0.10.3)
rb-inotify (0.9.10) rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2) ffi (>= 0.5.0, < 2)
...@@ -144,12 +150,12 @@ GEM ...@@ -144,12 +150,12 @@ GEM
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1) unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.10.0) ruby-progressbar (1.10.0)
rubyXL (3.3.29) rubyXL (3.3.31)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
rubyzip (>= 1.1.6) rubyzip (>= 1.1.6)
ruby_dep (1.5.0) ruby_dep (1.5.0)
rubyzip (1.2.1) rubyzip (1.2.2)
sass (3.5.7) sass (3.7.2)
sass-listen (~> 4.0.0) sass-listen (~> 4.0.0)
sass-listen (4.0.0) sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4) rb-fsevent (~> 0.9, >= 0.9.4)
...@@ -160,9 +166,9 @@ GEM ...@@ -160,9 +166,9 @@ GEM
sprockets (>= 2.8, < 4.0) sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0) sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3) tilt (>= 1.1, < 3)
selenium-webdriver (3.13.0) selenium-webdriver (3.141.0)
childprocess (~> 0.5) childprocess (~> 0.5)
rubyzip (~> 1.2) rubyzip (~> 1.2, >= 1.2.2)
spring (2.0.2) spring (2.0.2)
activesupport (>= 4.2) activesupport (>= 4.2)
spring-watcher-listen (2.0.1) spring-watcher-listen (2.0.1)
...@@ -176,16 +182,13 @@ GEM ...@@ -176,16 +182,13 @@ GEM
activesupport (>= 4.0) activesupport (>= 4.0)
sprockets (>= 3.0.0) sprockets (>= 3.0.0)
sqlite3 (1.3.13) sqlite3 (1.3.13)
thor (0.20.0) thor (0.20.3)
thread_safe (0.3.6) thread_safe (0.3.6)
tilt (2.0.8) tilt (2.0.9)
turbolinks (5.1.1)
turbolinks-source (~> 5.1)
turbolinks-source (5.1.0)
tzinfo (1.2.5) tzinfo (1.2.5)
thread_safe (~> 0.1) thread_safe (~> 0.1)
unicode-display_width (1.4.0) unicode-display_width (1.4.0)
web-console (3.6.2) web-console (3.7.0)
actionview (>= 5.0) actionview (>= 5.0)
activemodel (>= 5.0) activemodel (>= 5.0)
bindex (>= 0.4.0) bindex (>= 0.4.0)
...@@ -193,9 +196,9 @@ GEM ...@@ -193,9 +196,9 @@ GEM
websocket-driver (0.6.5) websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3) websocket-extensions (0.1.3)
xpath (3.1.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
yard (0.9.14) yard (0.9.16)
PLATFORMS PLATFORMS
ruby ruby
...@@ -214,7 +217,7 @@ DEPENDENCIES ...@@ -214,7 +217,7 @@ DEPENDENCIES
pry-doc pry-doc
puma (~> 3.7) puma (~> 3.7)
rack-cors rack-cors
rails (~> 5.1.6) rails (= 5.1.6.1)
rubocop (~> 0.58.2) rubocop (~> 0.58.2)
rubyXL rubyXL
sass-rails (~> 5.0) sass-rails (~> 5.0)
...@@ -222,7 +225,6 @@ DEPENDENCIES ...@@ -222,7 +225,6 @@ DEPENDENCIES
spring spring
spring-watcher-listen (~> 2.0.0) spring-watcher-listen (~> 2.0.0)
sqlite3 sqlite3
turbolinks (~> 5)
tzinfo-data tzinfo-data
web-console (>= 3.3.0) web-console (>= 3.3.0)
......
...@@ -47,156 +47,3 @@ class Cart { ...@@ -47,156 +47,3 @@ class Cart {
return false; return false;
} }
} }
// class Cart {
// constructor() {
// this.isOpen = false;
// this._courses = {}; {title, id, sections: {id, crn}}
// const cartData = document.getElementById('cart-data');
// const courses = Array.from(cartData.content.children);
// for (const course of courses) {
// const { id, title } = course.dataset;
// const sections = Array.from(course.children).map(node => ({ ...node.dataset }));
// this._courses[id] = { id, title, sections };
// }
// document.getElementById('course-counter').innerText = Object.keys(this._courses).length;
// this._ids = Array.from(document.getElementById('cart-courses').children).map(e => e.dataset.crn);
// }
// get crns() {
// return Object.keys(this._courses)
// .map(cid => this._courses[cid].sections.map(s => s.crn))
// .reduce((prev, curr) => [...prev, ...curr], []);
// }
// get ids() {
// return Object.keys(this._courses)
// .map(cid => this._courses[cid].sections.map(s => s.id))
// .reduce((prev, curr) => [...prev, ...curr], []);
// }
// toggle() {
// const list = document.getElementById('cart');
// const icon = document.getElementById('schedule-icon');
// if (this.isOpen) {
// list.style.display = 'none';
// icon.style.color = 'black';
// } else {
// list.style.display = 'block';
// icon.style.color = 'green';
// }
// this.isOpen = !this.isOpen;
// }
// addCourse(course) {
// this._courses[course.id] = course;
// const courseList = document.getElementById('cart-courses');
// const courseNode = courseList.querySelector(`#schedule-${course.id}`);
// const newNode = this._constructCourseNode(course);
// if (courseNode !== null) courseList.replaceChild(newNode, courseNode);
// else courseList.appendChild(newNode);
// document.getElementById('course-counter').innerText = Object.keys(this._courses).length;
// fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' });
// }
// removeCourse(id) {
// const sectionIds = this._courses[id].sections.map(s => s.id);
// for (const sectionId of sectionIds) {
// const sectionCard = document.querySelector(`#section-${sectionId}`);
// sectionCard && sectionCard.classList.remove('selected');
// }
// delete this._courses[id];
// const courseList = document.getElementById('cart-courses');
// const current = courseList.querySelector(`#schedule-${id}`);
// courseList.removeChild(current);
// document.getElementById('course-counter').innerText = Object.keys(this._courses).length;
// fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' });
// }
// courseContainingSection(id) {
// for (const courseId in this._courses) {
// const course = this._courses[courseId];
// for (const section of course.sections) {
// if (section.id == id) return course;
// }
// }
// return undefined;
// }
// includesSection(id) {
// return !!this.courseContainingSection(id);
// }
// section: { id, crn }
// addSection(section) {
// const course = this._courses[section.cid];
// if (course) {
// course.sections.push(section);
// const courseNode = document.getElementById(`schedule-${course.id}`);
// const crnList = courseNode.querySelector('.crns');
// crnList.innerText = course.sections.map(s => `#${s.crn}`);
// fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' });
// } else {
// const courseCard = document.getElementById(`course-${section.cid}`);
// const title = courseCard.querySelector('.title').innerText;
// this.addCourse({ title, id: section.cid, sections: [section] });
// }
// }
// removeSection(section) {
// const course = this.courseContainingSection(section.id);
// course.sections = course.sections.filter(s => s.id !== section.id);
// const schedule = document.querySelector('#cart-courses');
// const courseNode = schedule.querySelector(`#schedule-${course.id}`);
// const crnList = courseNode.querySelector('.crns');
// if (course.sections.length === 0) {
// this.removeCourse(section.cid);
// } else {
// crnList.innerText = course.sections.map(s => `#${s.crn}`);
// }
// fetch(`/sessions/update?section_ids=${this.ids.join(',')}`, { cache: 'no-store' });
// }
// async downloadIcs() {
// const cal = await fetch(`/api/schedules?crns=${this.crns.join(',')}`);
// const text = await cal.text();
// var blob = new Blob([text], { type: 'text/calendar;charset=utf-8' });
// saveAs(blob, 'test.ics');
// }
// async addToSystemCalendar() {
// const url = `webcal:${window.location.hostname}/api/schedule?crns=${this.crns.join(',')}`;
// window.open(url, '_self');
// }
// _constructCourseNode(course) {
// let html = `<li id="schedule-${course.id}" class="list-group-item schedule-section-card" onclick="removeCourse(${course.id})">`;
// html += `<div style="display: flex; justify-content: space-between;">`;
// html += `<b style="min-width: 15%">${course.title}</b>`;
// html += `<span class="crns" style="color: gray; font-size: 10pt;">`;
// html += course.sections.map(s => `#${s.crn}`).join(', ');
// html += `</span>`;
// html += `</div>`;
// html += `</li>`;
// return elementFromString(html);
// }
// }
// const removeCourse = id => {
// this.cart.removeCourse(id);
// };
#calendar { #calendar {
background-color: white; background-color: white;
padding: 16px; padding: 16px;
margin-bottom: 8px;
} }
.section-item.selected { .section-item.selected {
......
class SearchController < ApplicationController class SearchController < ApplicationController
def index def index
# sorry zach
results = SearchHelper::GenericItem.fetchall(String.new(params[:query]), semester: @semester).group_by(&:type) results = SearchHelper::GenericItem.fetchall(String.new(params[:query]), semester: @semester).group_by(&:type)
@instructors = results[:instructor]
@courses = results[:course] # results = search(params[:query])
@instructors = results[:instructor]&.map(&:data)
@courses = results[:course]&.map(&:data)
end end
# cases
# math 113 - [a-zA-Z]{3,} [1-9]{3}
end end
...@@ -16,11 +16,20 @@ class SessionsController < ApplicationController ...@@ -16,11 +16,20 @@ class SessionsController < ApplicationController
@cart << section_id @cart << section_id
end end
puts @cart.to_json
cookies[:cart] = @cart.to_json cookies[:cart] = @cart.to_json
render json: @cart.to_json render json: @cart.to_json
end end
def add_bulk
crns = params[:crns].split(',')
crns.each { |crn|
section_id = CourseSection.find_by_crn(crn).id.to_s
@cart << section_id unless @cart.include?(section_id)
}
cookies[:cart] = @cart.to_json
redirect_to schedule_path
end
private private
def update_cookie(sym) def update_cookie(sym)
......
<h1>Welcome to Schedules!</h1> <h1>Welcome to Schedules!</h1>
<p>Schedules is a website designed by Mason SRCT designed to help students explore the GMU Catalog.</p> <p>Schedules is a website built by <a href="https://srct.gmu.edu">Mason SRCT</a> designed to help students explore the GMU Catalog.</p>
Search for either professors or courses using the search bar above. Search for either courses or professors using the search bar above.
<br/>
<p></p>
<h3>Quick add</h3>
<p>Want to quickly generate a calendar populated with your semester's classes? Enter the CRNs in a comma separated list below.</p>
<form action="/sessions/add_bulk" class="form">
<div class="input-group">
<input
id="crns"
name="crns"
type="text"
class="form-control"
placeholder="12345,54321,..."
aria-describedby="basic-addon2"
autocomplete="off"
>
<div class="input-group-append">
<button type="submit" class="btn btn-primary" type="button">
Populate Calendar
</button>
</div>
</div>
</form>
...@@ -7,10 +7,32 @@ ...@@ -7,10 +7,32 @@
<%= javascript_include_tag 'fullcalendar.min'%> <%= javascript_include_tag 'fullcalendar.min'%>
<%= stylesheet_link_tag 'fullcalendar.min' %> <%= stylesheet_link_tag 'fullcalendar.min' %>
<button id="open-modal-btn" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exportModal"> <button id="open-modal-btn" type="button" class="btn btn-primary" data-toggle="modal" data-target="#exportModal">
Generate Schedule Generate Schedule
</button> </button>
<div id="calendar"></div> <div id="calendar"></div>
<h3>Quick add</h3>
<p>Populate your calendar quickly by entering a comma separated list of CRNs.</p>
<form action="/sessions/add_bulk" class="form">
<div class="input-group">
<input
id="crns"
name="crns"
type="text"
class="form-control"
placeholder="12345,54321,..."
aria-describedby="basic-addon2"
autocomplete="off"
>
<div class="input-group-append">
<button type="submit" class="btn btn-primary" type="button">
Populate Calendar
</button>
</div>
</div>
</form>
<template id="events" data-events="<%= @events.to_json %>"></template> <template id="events" data-events="<%= @events.to_json %>"></template>
......
<% unless @instructors.nil? %> <% unless @instructors.nil? %>
<h2>Instructors</h2> <h2>Instructors</h2>
<div class="row"> <div class="row">
<% @instructors.map(&:data).each do |instructor| %> <% @instructors.each do |instructor| %>
<div class="col"><%= render partial: 'shared/instructor', object: instructor %></div> <div class="col"><%= render partial: 'shared/instructor', object: instructor %></div>
<% end %> <% end %>
</div> </div>
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<% unless @courses.nil? %> <% unless @courses.nil? %>
<h2>Courses</h2> <h2>Courses</h2>
<% @courses.map(&:data).each do |course| %> <% @courses.each do |course| %>
<div class="col"><%= render partial: 'shared/course', object: course %></div> <div class="col"><%= render partial: 'shared/course', object: course %></div>
<% end %>