Commit 5d22bf87 authored by Zac Wood's avatar Zac Wood
Browse files

Merge branch '19-webcal' into 'master'

Resolve "Add webcal endpoint"

Closes #19

See merge request srct/schedules!13
parents 4aaceed0 b6c67429
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
// Place all the styles related to the CalendarGenerator controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
require 'icalendar'
require 'time'
# Contains functionality for generating schedules.
class SchedulesController < ApplicationController
# Render an iCal file containing the schedules of all the
# course sections with the given CRNs.
def index
crns = params["crns"].split ','
@schedule = Schedule.new crns
render plain: @schedule.to_ical # render a plaintext iCal file
end
end
require 'icalendar' require 'icalendar'
require 'time' require 'time'
# Contains functionality for generating schedules. class Schedule
class CalendarGeneratorController < ApplicationController def initialize(crns)
# Render an iCal file containing the schedules of all the @cal = Icalendar::Calendar.new
# course sections with the given CRNs. @cal.x_wr_calname = 'GMU Fall 2018'
def new
cal = Icalendar::Calendar.new @course_sections = crns.map do |crn|
CourseSection.find_by crn: crn
# the intended format for the json is a list of CRNs end
params[:_json].each do |crn| # for each CRN sent by the post request @course_sections.compact!
section = CourseSection.find_by_crn(crn)
load_events
end
def to_ical
@cal.to_ical
end
private
def load_events
@course_sections.each do |section|
unless section.start_time == "TBA" || section.end_time == "TBA" unless section.start_time == "TBA" || section.end_time == "TBA"
event = generate_event_from_section(section) event = generate_event_from_section(section)
cal.add_event(event) @cal.add_event(event)
end end
if section.days.start_with? "M" if section.days.start_with? "M"
col_day_makeup = generate_event_after_columbus_day(section) col_day_makeup = generate_event_after_columbus_day(section)
cal.add_event(col_day_makeup) @cal.add_event(col_day_makeup)
end end
end end
render plain: cal.to_ical # render a plaintext iCal file
end end
private
# Configures a calendar event from a given section # Configures a calendar event from a given section
# @param section [CourseSection] # @param section [CourseSection]
def generate_event_from_section(section) def generate_event_from_section(section)
......
<h1>CalendarGenerator#generate</h1>
<p>Find me in app/views/calendar_generator/generate.html.erb</p>
...@@ -3,8 +3,7 @@ Rails.application.routes.draw do ...@@ -3,8 +3,7 @@ Rails.application.routes.draw do
scope :api do # Register /api routes scope :api do # Register /api routes
resources :courses, only: [:index, :show] resources :courses, only: [:index, :show]
resources :course_sections, only: [:index] resources :course_sections, only: [:index]
resources :schedules, only: [:index]
post 'generate', controller: 'calendar_generator', action: 'new'
end end
root 'courses#index' # Set the root to be the courses API endpoint root 'courses#index' # Set the root to be the courses API endpoint
......
require 'test_helper' require 'test_helper'
class CalendarGeneratorControllerTest < ActionDispatch::IntegrationTest class SchedulesControllertest < ActionDispatch::IntegrationTest
test "should get generate" do test "should generate schedule" do
crns = [course_sections(:cs112001).crn, course_sections(:cs112002).crn] crns = [course_sections(:cs112001).crn, course_sections(:cs112002).crn]
post "/api/generate", params: crns.to_json, headers: { 'CONTENT_TYPE' => 'application/json' } get "/api/schedules?crns=#{crns.join(',')}"
# DTSTAMP and UID lines uniquely identify events, so we can't test against them. # DTSTAMP and UID lines uniquely identify events, so we can't test against them.
# so remove all the lines starting with them. # so remove all the lines starting with them.
......
...@@ -2,6 +2,7 @@ BEGIN:VCALENDAR ...@@ -2,6 +2,7 @@ BEGIN:VCALENDAR
VERSION:2.0 VERSION:2.0
PRODID:icalendar-ruby PRODID:icalendar-ruby
CALSCALE:GREGORIAN CALSCALE:GREGORIAN
X-WR-CALNAME:GMU Fall 2018
BEGIN:VEVENT BEGIN:VEVENT
DTSTART:20180521T120000 DTSTART:20180521T120000
DTEND:20180521T130000 DTEND:20180521T130000
......
...@@ -9,10 +9,9 @@ interface ScheduleRootProps { ...@@ -9,10 +9,9 @@ interface ScheduleRootProps {
removeCourseSection: (courseSection: CourseSection) => any; removeCourseSection: (courseSection: CourseSection) => any;
} }
const generateSchedule = async (schedule: CourseSection[]): Promise<void> => { const generateSchedule = async (schedule: CourseSection[]) => {
const crns = schedule.map(section => section.crn); const crns = schedule.map(section => section.crn);
const calendar = await ApiService.generateCalendar(crns); ApiService.subscribeToCalendar(crns);
downloadFile(calendar, 'GMU Fall 2018.ics');
}; };
const ScheduleRoot = ({ schedule, removeCourseSection }: ScheduleRootProps) => ( const ScheduleRoot = ({ schedule, removeCourseSection }: ScheduleRootProps) => (
......
...@@ -7,8 +7,8 @@ class ApiService { ...@@ -7,8 +7,8 @@ class ApiService {
searchCourseSections = async (crn: string): Promise<any[]> => searchCourseSections = async (crn: string): Promise<any[]> =>
fetchJson(`${this.apiRoot}/course_sections?crn=${crn}`); fetchJson(`${this.apiRoot}/course_sections?crn=${crn}`);
generateCalendar = async (crns: string[]): Promise<string> => subscribeToCalendar = (crns: string[]) =>
postJson(`${this.apiRoot}/generate`, crns).then(response => response.text()); window.open(`webcal://localhost:3000/api/schedules?crns=${crns.join(',')}`, '_self');
} }
const fetchJson = async (url: string): Promise<any> => fetch(url).then(response => response.json()); const fetchJson = async (url: string): Promise<any> => fetch(url).then(response => response.json());
......
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