Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
schedules
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
11
Issues
11
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
SRCT
schedules
Commits
b32595db
Commit
b32595db
authored
Oct 21, 2018
by
Zac Wood
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use pairs if there are recitations/labs
parent
fa9ffff2
Changes
16
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
429 additions
and
183 deletions
+429
-183
schedules/Gemfile
schedules/Gemfile
+1
-2
schedules/app/assets/javascripts/application.js
schedules/app/assets/javascripts/application.js
+6
-4
schedules/app/assets/javascripts/cart.js
schedules/app/assets/javascripts/cart.js
+181
-115
schedules/app/assets/javascripts/schedules.js
schedules/app/assets/javascripts/schedules.js
+28
-1
schedules/app/assets/javascripts/search.js
schedules/app/assets/javascripts/search.js
+40
-15
schedules/app/controllers/schedules_controller.rb
schedules/app/controllers/schedules_controller.rb
+18
-29
schedules/app/controllers/sessions_controller.rb
schedules/app/controllers/sessions_controller.rb
+44
-0
schedules/app/helpers/schedules_helper.rb
schedules/app/helpers/schedules_helper.rb
+57
-0
schedules/app/models/course.rb
schedules/app/models/course.rb
+8
-0
schedules/app/models/course_section.rb
schedules/app/models/course_section.rb
+21
-2
schedules/app/views/layouts/application.html.erb
schedules/app/views/layouts/application.html.erb
+2
-2
schedules/app/views/schedules/show.html.erb
schedules/app/views/schedules/show.html.erb
+5
-8
schedules/app/views/shared/_cart.html.erb
schedules/app/views/shared/_cart.html.erb
+3
-3
schedules/app/views/shared/_course.html.erb
schedules/app/views/shared/_course.html.erb
+13
-2
schedules/app/views/shared/_section.html.erb
schedules/app/views/shared/_section.html.erb
+1
-0
schedules/config/routes.rb
schedules/config/routes.rb
+1
-0
No files found.
schedules/Gemfile
View file @
b32595db
...
...
@@ -13,10 +13,9 @@ gem 'sqlite3'
gem
'puma'
,
'~> 3.7'
# Use SCSS for stylesheets
gem
'sass-rails'
,
'~> 5.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem
'turbolinks'
,
'~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem
'jbuilder'
,
'~> 2.5'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use ActiveModel has_secure_password
...
...
schedules/app/assets/javascripts/application.js
View file @
b32595db
...
...
@@ -11,7 +11,7 @@
// about supported directives.
//
//= require rails-ujs
//
=
require turbolinks
// require turbolinks
//= require FileSaver
//= require_tree .
// require jquery3
...
...
@@ -25,6 +25,7 @@ const elementFromString = string => {
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
this
.
cart
=
new
Cart
();
FontAwesome
.
dom
.
i2svg
();
});
const
setSemester
=
async
select
=>
{
...
...
@@ -33,6 +34,7 @@ const setSemester = async select => {
};
/** Loads FontAwesome icons on load; fixes weird flickering */
document
.
addEventListener
(
'
turbolinks:load
'
,
()
=>
{
FontAwesome
.
dom
.
i2svg
();
});
FontAwesome
.
dom
.
watch
({
observeMutationsRoot
:
document
});
// document.addEventListener('turbolinks:load', () => {
// FontAwesome.dom.i2svg();
// });
schedules/app/assets/javascripts/cart.js
View file @
b32595db
This diff is collapsed.
Click to expand it.
schedules/app/assets/javascripts/schedules.js
View file @
b32595db
...
...
@@ -3,12 +3,39 @@ document.addEventListener('DOMContentLoaded', () => {
if
(
eventsTemplate
)
{
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
'
,
header
:
false
,
events
:
e
vents
,
events
:
renderE
vents
,
});
document
.
getElementById
(
'
numSchedules
'
).
innerText
=
window
.
events
.
length
;
}
});
let
i
=
0
;
const
renderEvents
=
(
start
,
end
,
timezone
,
callback
)
=>
{
console
.
log
(
window
.
events
[
i
]);
document
.
getElementById
(
'
currentSchedule
'
).
innerText
=
i
+
1
;
callback
(
window
.
events
[
i
]);
};
const
nextSchedule
=
()
=>
{
if
(
i
+
1
<
window
.
events
.
length
)
i
++
;
$
(
'
#calendar
'
).
fullCalendar
(
'
refetchEvents
'
);
console
.
log
(
window
.
events
[
i
]);
};
const
prevSchedule
=
()
=>
{
if
(
i
>
0
)
i
--
;
$
(
'
#calendar
'
).
fullCalendar
(
'
refetchEvents
'
);
console
.
log
(
window
.
events
[
i
]);
};
schedules/app/assets/javascripts/search.js
View file @
b32595db
...
...
@@ -3,33 +3,58 @@
const
sectionWithCrn
=
crn
=>
document
.
getElementById
(
'
search-list
'
).
querySelector
(
`
[data-crn="
${
crn
}
"]
`
);
const
addCourse
=
(
event
,
id
)
=>
{
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
sections
=
sectionsItems
.
map
(
li
=>
({
...
li
.
dataset
}));
this
.
cart
.
addCourse
({
title
,
id
,
sections
});
sectionsItems
.
forEach
(
s
=>
s
.
classList
.
add
(
'
selected
'
));
const
filtered
=
sectionsItems
.
filter
(
li
=>
{
return
!
li
.
parentNode
.
classList
.
contains
(
'
pair
'
)
||
li
.
dataset
.
type
===
'
Lecture
'
;
});
event
.
stopPropagation
();
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.
*/
const
addOrRemoveFromCart
=
(
event
,
sectionNode
)
=>
{
const
addOrRemoveFromCart
=
async
(
event
,
sectionNode
)
=>
{
event
&&
event
.
stopPropagation
();
const
section
=
{
...
sectionNode
.
dataset
};
if
(
this
.
cart
.
includesSection
(
section
.
id
))
{
this
.
cart
.
removeSection
(
section
);
sectionNode
.
classList
.
remove
(
'
selected
'
);
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
'
));
}
}
else
{
this
.
cart
.
addSection
(
section
);
sectionNode
.
classList
.
add
(
'
selected
'
);
await
this
.
cart
.
addSections
([
section
]);
if
(
this
.
cart
.
includesSection
(
section
))
{
sectionNode
.
classList
.
add
(
'
selected
'
);
}
else
{
sectionNode
.
classList
.
remove
(
'
selected
'
);
}
}
event
.
stopPropagation
();
};
/**
...
...
@@ -49,7 +74,7 @@ const removeFromCart = section => {
*/
const
toggleSections
=
course
=>
{
const
sections
=
course
.
querySelector
(
'
.sections
'
);
console
.
log
(
sections
);
if
(
sections
.
style
.
display
===
'
flex
'
)
{
sections
.
style
.
display
=
'
none
'
;
}
else
{
...
...
schedules/app/controllers/schedules_controller.rb
View file @
b32595db
require
'icalendar'
require
'time'
# Contains functionality for generating schedules.
class
SchedulesController
<
ApplicationController
resource_description
do
...
...
@@ -16,35 +13,27 @@ class SchedulesController < ApplicationController
render
plain:
@schedule
.
to_ical
# render a plaintext iCal file
end
DAYS
=
{
"M"
:
Date
.
new
(
2019
,
1
,
14
),
"T"
:
Date
.
new
(
2019
,
1
,
15
),
"W"
:
Date
.
new
(
2019
,
1
,
16
),
"R"
:
Date
.
new
(
2019
,
1
,
17
),
"F"
:
Date
.
new
(
2019
,
1
,
18
),
"S"
:
Date
.
new
(
2019
,
1
,
19
),
"U"
:
Date
.
new
(
2019
,
1
,
20
)
}.
freeze
include
SchedulesHelper
def
show
all_sections
=
@cart
.
values
# schedules = []
all_sections
.
each_with_index
do
|
sections
,
i
|
combined
=
{}
@cart
.
each
do
|
cid
,
sections
|
combined
[
cid
]
=
[]
sections
.
each
do
|
section
|
end
end
@events
=
@cart
.
map
do
|
_cid
,
sections
|
s
=
sections
.
first
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"
)
courses
=
@cart
.
values
.
group_by
do
|
s
|
s
.
course
.
id
end
{
title:
s
.
name
,
start:
"
#{
formatted_date
}
T
#{
time
}
"
,
end:
"
#{
formatted_date
}
T
#{
endtime
}
"
}
end
end
.
flatten
puts
courses
.
keys
id_sets
=
generate_schedules
(
@cart
.
values
)
@events
=
generate_fullcalender_events
(
id_sets
)
end
# this works(?)
# recursively build a list of sets containing 1 section from each course chosen
end
schedules/app/controllers/sessions_controller.rb
View file @
b32595db
...
...
@@ -7,6 +7,50 @@ class SessionsController < ApplicationController
head
:ok
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
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
end
private
def
update_cookie
(
sym
)
...
...
schedules/app/helpers/schedules_helper.rb
0 → 100644
View file @
b32595db
module
SchedulesHelper
DAYS
=
{
"M"
:
Date
.
new
(
2019
,
1
,
14
),
"T"
:
Date
.
new
(
2019
,
1
,
15
),
"W"
:
Date
.
new
(
2019
,
1
,
16
),
"R"
:
Date
.
new
(
2019
,
1
,
17
),
"F"
:
Date
.
new
(
2019
,
1
,
18
),
"S"
:
Date
.
new
(
2019
,
1
,
19
),
"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"
)
{
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
)
end
end
end
end
schedules/app/models/course.rb
View file @
b32595db
...
...
@@ -12,6 +12,14 @@ class Course < ApplicationRecord
validates
:subject
,
presence:
true
validates
:semester_id
,
presence:
true
def
has_labs?
course_sections
.
reject
(
&
:is_lecture?
).
count
.
positive?
end
def
lab_course?
course_sections
.
select
(
&
:is_lecture?
).
count
.
zero?
end
def
self
.
from_subject
(
base_query
,
subject
)
base_query
.
where
(
"courses.subject = ?"
,
subject
.
upcase
)
end
...
...
schedules/app/models/course_section.rb
View file @
b32595db
...
...
@@ -10,13 +10,32 @@ class CourseSection < ApplicationRecord
validates
:title
,
presence:
true
validates
:course_id
,
presence:
true
def
is_lecture?
section_type
==
"Lecture"
end
def
labs
return
nil
unless
section_type
==
"Lecture"
return
nil
unless
is_lecture?
# Lectures have names formatted like "MATH 214 001"
# Labs/recitations have the title format "Recitation for Lecture 001"
# so, match all the sections in the same course which have the same number
# as the last element of their titles
lecture_number
=
name
.
split
[
name
.
split
.
length
-
1
]
course
.
course_sections
.
select
do
|
s
|
labs_for_section
=
course
.
course_sections
.
select
do
|
s
|
s
.
title
.
split
[
s
.
title
.
split
.
length
-
1
]
==
lecture_number
end
labs_for_section
.
map
do
|
lab
|
[
self
,
lab
]
end
end
def
overlaps?
(
other
)
t1_start
,
t1_end
=
Time
.
parse
(
start_time
),
Time
.
parse
(
end_time
)
t2_start
,
t2_end
=
Time
.
parse
(
other
.
start_time
),
Time
.
parse
(
other
.
end_time
)
(
t1_start
<=
t2_end
&&
t2_start
<=
t1_end
)
&&
Set
.
new
(
days
.
split
).
intersect?
(
Set
.
new
(
other
.
days
.
split
))
end
# Select all course sections that have an instructor that matches the given name
...
...
schedules/app/views/layouts/application.html.erb
View file @
b32595db
<!DOCTYPE html>
<html
data-turbolinks=
"false"
>
<html>
<head>
<title>
Schedules
</title>
<%=
csrf_meta_tags
%>
...
...
@@ -15,7 +15,7 @@
<%=
stylesheet_link_tag
'application'
%>
</head>
<body
data-turbolinks=
"false"
>
<body>
<%=
render
partial:
'shared/navbar'
%>
<%=
yield
%>
<%=
render
partial:
'shared/cart'
%>
...
...
schedules/app/views/schedules/show.html.erb
View file @
b32595db
<button
onclick=
"nextSchedule()"
>
Next
</button>
<button
onclick=
"prevSchedule()"
>
Prev
</button>
<span
id=
"currentSchedule"
>
1
</span>
/
<span
id=
"numSchedules"
>
1
</span>
<div
id=
"calendar"
></div>
<template
id=
"events"
data-events=
"
<%=
@events
.
to_json
%>
"
></template>
<script>
/* const cal = document.querySelector('#calendar');
* var calendar = new Calendar(cal, {
* defaultView: 'agendaWeek'
* }); */
</script>
schedules/app/views/shared/_cart.html.erb
View file @
b32595db
...
...
@@ -12,9 +12,9 @@
<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
>
<
!-- <
span class="crns">
<%=
sections
.
map
{
|
s
|
"#
#{
s
.
crn
}
"
}.
join
(
', '
)
%>
</span> --
>
</div>
</li>
<%
end
%>
...
...
schedules/app/views/shared/_course.html.erb
View file @
b32595db
...
...
@@ -47,8 +47,19 @@
<!-- List of Course Sections -->
<div
class=
"list-group list-group-flush sections"
style=
"display:
<%=
expanded
?
"flex"
:
"none"
%>
"
>
<%
if
defined?
(
@instructor
)
%>
<%=
render
partial:
'shared/section'
,
collection:
course
.
course_sections
.
where
(
instructor:
@instructor
),
locals:
{
course:
course
}
%>
<%
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
%>
<%
else
%>
<%=
render
partial:
'shared/section'
,
collection:
course
.
course_sections
,
locals:
{
course:
course
}
%>
<%
end
%>
...
...
schedules/app/views/shared/_section.html.erb
View file @
b32595db
...
...
@@ -3,6 +3,7 @@
data-crn=
"
<%=
section
.
crn
%>
"
data-id=
"
<%=
section
.
id
%>
"
data-cid=
"
<%=
course
.
id
%>
"
data-type=
"
<%=
section
.
section_type
%>
"
onclick=
"addOrRemoveFromCart(event, this)"
>
<span
style=
"float:left"
><b
class=
"subj"
>
<%=
"
#{
section
.
name
}
"
%>
</b>
:
<%=
section
.
title
%>
</span>
...
...
schedules/config/routes.rb
View file @
b32595db
...
...
@@ -2,6 +2,7 @@
Rails
.
application
.
routes
.
draw
do
get
'search'
,
to:
'search#index'
get
'sessions/update'
,
as:
'update_session'
get
'sessions/cart'
resources
:instructors
,
only:
[
:index
,
:show
]
get
'schedule'
,
to:
'schedules#show'
,
as:
'schedule'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment