Commit 1a8b57eb authored by Zac Wood's avatar Zac Wood
Browse files

Merge branch '2-pretty-馃拝' into 'master'

Merge for deployment

Closes #2

See merge request srct/schedules!10
parents 0e0da85c 2b631356
.vscode
\ No newline at end of file
.vscode
schedules.code-workspace
stages:
- test
test_api:
image: ruby:2.5
stage: test
script:
- cd schedules_api
- bundle install
- rails db:migrate
- rails test
test_web:
image: node:9
stage: test
script:
- cd schedules_web
- yarn
- yarn build
\ No newline at end of file
version: "3"
services:
api:
image: schedules_api
ports:
- "3000:3000"
command: ./start.sh
volumes:
- ./schedules_api:/api
web:
image: schedules_web
ports:
- "8080:8080"
command: yarn start
volumes:
- ./schedules_web:/web
#! /bin/bash
cd schedules_api/
docker build . -t 'schedules_api'
cd ../schedules_web
docker build . -t 'schedules_web'
cd ..
docker-compose up
FROM ruby:2.5
RUN mkdir /api
WORKDIR /api
ADD . /api
EXPOSE 3000
RUN bundle install
RUN rails db:migrate
RUN rails db:seed
......@@ -12,11 +12,6 @@ gem 'sqlite3'
# Use Puma as the app server
gem 'puma', '~> 3.7'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
......
......@@ -57,7 +57,6 @@ GEM
concurrent-ruby (1.0.5)
crass (1.0.4)
erubi (1.7.1)
execjs (2.7.0)
ffi (1.9.25)
globalid (0.4.1)
activesupport (>= 4.2.0)
......@@ -131,17 +130,6 @@ GEM
rubyzip (>= 1.1.6)
ruby_dep (1.5.0)
rubyzip (1.2.1)
sass (3.5.6)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sass-rails (5.0.7)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
selenium-webdriver (3.13.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
......@@ -160,14 +148,11 @@ GEM
sqlite3 (1.3.13)
thor (0.20.0)
thread_safe (0.3.6)
tilt (2.0.8)
turbolinks (5.1.1)
turbolinks-source (~> 5.1)
turbolinks-source (5.1.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uglifier (4.1.12)
execjs (>= 0.3.0, < 3)
web-console (3.6.2)
actionview (>= 5.0)
activemodel (>= 5.0)
......@@ -197,15 +182,13 @@ DEPENDENCIES
rack-cors
rails (~> 5.1.6)
rubyXL
sass-rails (~> 5.0)
selenium-webdriver
spring
spring-watcher-listen (~> 2.0.0)
sqlite3
turbolinks (~> 5)
tzinfo-data
uglifier (>= 1.3.0)
web-console (>= 3.3.0)
BUNDLED WITH
1.16.1
1.16.2
require 'icalendar'
require 'time'
# Contains functionality for generating schedules.
class CalendarGeneratorController < ApplicationController
def generate
# Render an iCal file containing the schedules of all the
# course sections with the given CRNs.
def new
cal = Icalendar::Calendar.new
params[:_json].each do |crn|
section = Section.find_by_crn(crn)
event = generate_event_from_section(section)
cal.add_event(event)
# the intended format for the json is a list of CRNs
params[:_json].each do |crn| # for each CRN sent by the post request
section = CourseSection.find_by_crn(crn)
unless section.start_time == "TBA" || section.end_time == "TBA"
event = generate_event_from_section(section)
cal.add_event(event)
end
end
render plain: cal.to_ical
render plain: cal.to_ical # render a plaintext iCal file
end
private
# Configures a calendar event from a given section
# @param section [CourseSection]
def generate_event_from_section(section)
event = Icalendar::Event.new
......@@ -30,6 +38,10 @@ class CalendarGeneratorController < ApplicationController
event
end
# Format a DateTime string based on a given date and time
# @param date [String]
# @param time [String]
# @return [String]
def formatted_datetime_str(date, time)
formatted_date = date.to_s.tr('-', '')
formatted_time = Time.parse(time).strftime("%H%M%S")
......@@ -37,6 +49,7 @@ class CalendarGeneratorController < ApplicationController
"#{formatted_date}T#{formatted_time}"
end
# Mapping of days as represented by GMU to the iCal standard
DAYS = {
"M" => "MO",
"T" => "TU",
......@@ -47,6 +60,10 @@ class CalendarGeneratorController < ApplicationController
"U" => "SU"
}.freeze
# Generates a recurrence rule string descripting which day the class event
# should take place on
# @param section [CourseSection]
# @return [String]
def recurrence_rule_str(section)
days = section.days.split("").map do |day|
DAYS[day]
......@@ -55,8 +72,12 @@ class CalendarGeneratorController < ApplicationController
"FREQ=WEEKLY;UNTIL=#{formatted_datetime_str(section.end_date, section.end_time)};BYDAY=#{days.join(',')}"
end
# Get all dates that should excluded from the schedule
# @param section [CourseSection]
# @return [Array]
def exdates_for_section(section)
exdates = Closure.where(semester: section.course.semester).map { |closure|
# Generate exdates for all closures in a semester
exdates = Closure.where(semester: section.course.semester).map { |closure|
generate_exdate(closure.date.to_formatted_s(:number), section.start_time)
}
......@@ -72,7 +93,12 @@ class CalendarGeneratorController < ApplicationController
exdates
end
# Generate a DataTime to use as an exdate
# @param date [String]
# @param time [String]
# @return [Icalendar::Values::DateTime]
def generate_exdate(date, time)
# format the time for use in a DateTime
formatted_time = Time.parse(time).strftime("%H%M%S")
Icalendar::Values::DateTime.new("#{date}T#{formatted_time}")
end
......
# Contains all actions having to do with Sections.
# Contains all actions having to do with CourseSections.
# This is a nested controller -- see +config/routes.rb+ for details
class SectionsController < ApplicationController
class CourseSectionsController < ApplicationController
# Render JSON of all Sections belonging to a given Course.
def index
@course = Course.find(params[:course_id])
@sections = @course.sections
@sections = CourseSection.all
@sections = @sections.where(course_id: params[:course_id]) if params.key?(:course_id)
@sections = @sections.where(crn: params[:crn]) if params.key?(:crn)
render json: @sections
end
end
# Contains all actions having to do with Courses.
class CoursesController < ApplicationController
# Renders JSON of all courses.
# Renders JSON of courses matching params.
def index
@courses = Course.all
# filter by subject + course number if the params are included
@courses = @courses.where(subject: params[:subject].upcase) if params.key?(:subject)
@courses = @courses.where(course_number: params[:course_number]) if params.key?(:course_number)
render json: @courses
end
# Renders JSON of details of a singluar course, such as its sections
def show
@sections = CourseSection.where(course_id: params[:id])
render json: @sections
end
end
# Defines actions for the homepage
class HomeController < ApplicationController
def index; end
end
class SearchController < ApplicationController
def index
if params.key?(:crn)
crn = params[:crn]
@sections = Section.find_by_crn(crn)
render json: @sections
else
render status: 404
end
end
end
......@@ -4,14 +4,16 @@
class Course < ApplicationRecord
# Each course belongs to a +Semester+
belongs_to :semester
has_many :course_sections
# Ensure all necessary fields are present.
# Ensure all necessary are fields present.
validates :course_number, presence: true
validates :subject, presence: true
validates :semester_id, presence: true
# Returns all +Section+ objects that belong to this course.
def sections
Section.where course_id: id
# Returns all +CourseSection+ objects that belong to this course.
# @return [Array]
def course_sections
CourseSection.where course_id: id
end
end
# Contains logic belonging to the +Section+ model.
# Contains logic belonging to the +CourseSection+ model.
#
# TODO: Add more docs
class Section < ApplicationRecord
# Each +Section+ belongs to a +Course+.
class CourseSection < ApplicationRecord
# Each +CourseSection+ belongs to a +Course+.
belongs_to :course
# Ensure all necessary fields are present.
validates :name, presence: true
validates :crn, presence: true
# Unsure if necessary
# validates :section_type, presence: true
validates :title, presence: true
# validates :start_date, presence: true
# validates :end_date, presence: true
# validates :days, presence: true
validates :course_id, presence: true
end
......@@ -2,7 +2,13 @@
#
# A +Semester+ is a simple model that consists of a +year+ and a +season+, e.g. "Fall 2018".
class Semester < ApplicationRecord
has_many :courses
# Ensure necessary fields are present.
validates :year, presence: true
validates :season, presence: true
def courses
Course.where semester_id: id
end
end
......@@ -17,7 +17,7 @@ Rails.application.configure do
# Attempt to read encrypted secrets from `config/secrets.yml.enc`.
# Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or
# `config/secrets.yml.key`.
config.read_encrypted_secrets = true
# config.read_encrypted_secrets = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
......
# Registers all routes for the app.
Rails.application.routes.draw do
scope :api do # Register /api routes
resources :courses, only: [:index] do # GET /api/courses
resources :sections, only: [:index] # GET /api/courses/:course_id/sections
end
resources :courses, only: [:index, :show]
resources :course_sections, only: [:index]
get 'search', controller: 'search', action: 'index'
post 'generate', controller: 'calendar_generator', action: 'generate'
post 'generate', controller: 'calendar_generator', action: 'new'
end
root 'courses#index' # Set the root to be the courses API endpoint
......
# This file is no longer being used.
# Data is now being parsed from Patriot Web.
require 'rubyXL'
# Provides utilities for loading schedules from GMU's excel files.
......@@ -35,7 +38,7 @@ class ExcelLoader
def delete_all_records
Semester.delete_all
Course.delete_all
Section.delete_all
CourseSection.delete_all
end
# Tries to create a course from a given row.
......@@ -67,7 +70,7 @@ class ExcelLoader
# So, split the times string by the - character
times = row.cells[23]&.value
time_strs = times.split('-')
section = Section.create name: section_name,
section = CourseSection.create name: section_name,
course: @current_course,
crn: row.cells[6]&.value,
section_type: row.cells[8]&.value,
......
class ChangeSectionToCourseSection < ActiveRecord::Migration[5.1]
def change
rename_table :sections, :course_sections
end
end
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