fix sorting on agenda view

the results of the events and assignments api request were not
being properly merged into a sorted list.

fixes CNVS-9037

test plan:
 - create a bunch of days that contain only events.
 - interleave a bunch of days that contain only assignments.
 - verify that the agenda view is sorted correctly.
 - verify that "load more" works properly.

Change-Id: I7f5c203149bb854496e64af053170b50361b7a96
Reviewed-on: https://gerrit.instructure.com/25641
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Braden Anderson <banderson@instructure.com>
Product-Review: Marc LeGendre <marc@instructure.com>
QA-Review: Marc LeGendre <marc@instructure.com>
This commit is contained in:
Jon Willesen 2013-10-24 18:07:53 -06:00
parent 76a40a005b
commit 22fcb45b3d
3 changed files with 112 additions and 45 deletions

View File

@ -7,14 +7,6 @@ define [
'vendor/jquery.ba-tinypubsub'
], (I18n, _, Backbone, CalendarEventCollection, template) ->
# Public: Helper function translate a model date string into a date object.
#
# m - A model instance.
# prop - The name of the date property (default: 'start_at').
toDate = _.compose(((d) -> new Date(d)),
((m, prop = 'start_at') -> if m.get then m.get(prop) else m[prop]))
class AgendaView extends Backbone.View
PER_PAGE: 50
@ -74,23 +66,31 @@ define [
@eventCollection.canFetch('next') or
@assignmentCollection.canFetch('next')
# Public: Helper function to translate a model date into a timestamp.
# Public: Helper function translate a model date string into a date object.
#
# m - A model instance.
#
# Returns a Date object.
toDate: (m) -> new Date(m.get('start_at'))
# Internal: Helper function to translate a model date into a timestamp.
#
# m - A model instance.
# prop - The name of the date property (default: 'start_at').
#
# Returns a timestamp integer.
toTime: _.compose(((d) -> d.getTime()), toDate)
toTime: (m) => @toDate(m).getTime()
# Public: Helper function to translate a model date into a fudged date.
# Internal: Helper function to translate a model date into a fudged date.
#
# m - A model instance.
# m - A model instance or hash.
# prop - The name of the date property (default: 'start_at').
#
# Returns a fudged date.
toFudgedDate: _.compose(((d) -> $.fudgeDateForProfileTimezone(d)), toDate)
toFudgedDate: (m, prop = 'start_at') =>
d = if m.get then m.get(prop) else m[prop]
$.fudgeDateForProfileTimezone(new Date(d))
# Public: Given two collections, determine the latest shared date.
# Internal: Given two collections, determine the latest shared date.
#
# c1 - Collection object.
# c2 - Collection object.
@ -108,37 +108,87 @@ define [
else
null
# Public: Translate a list of event models to a hash.
# Internal: Change a flat array of objects into a sturctured array of
# objects based on the given iterator function. Similar to _.groupBy,
# except the result is an Array instead of a Hash and this function
# assumes the list is already sorted by the given iterator.
#
# list - An array of model objects.
# limit - A timestamp to stop returning events after (default: null).
# list - The sorted list of values to box.
# iterator - A function that returns the value to box by. The iterator
# is passed the value from the list.
#
# Returns a hash.
eventListToHash: (list, limit = null) =>
_.reduce(list, (result, event) =>
return result if limit and limit < @toTime(event)
# Returns a new boxed array with elemens from the given list.
sortedBoxBy: (list, iterator) ->
_.reduce(list, (result, currentElt) ->
return [[currentElt]] if _.isEmpty(result)
previousBox = _.last(result)
previousElt = _.last(previousBox)
if iterator(currentElt) == iterator(previousElt)
previousBox.push(currentElt)
else
result.push([currentElt])
result
, [])
# Internal: Do necessary changes on each event model in the list
#
# list - the list of the events to prepare
#
# Returns nothing.
prepareEvents: (list) ->
prepare = (event) ->
event.set('start_at', @toFudgedDate(event))
day = I18n.l('#date.formats.short_with_weekday', toDate(event))
if assignment = event.get('assignment')
assignment.due_at = @toFudgedDate(assignment, 'due_at')
result[day] or= []
result[day].push(event.toJSON())
result
, {})
# Public: Format a hash of event data to an object ready to be sent to the template.
#
# result - A hash of event data from AgendaView#toJSON.
#
# Returns an object.
formatResult: (events) =>
result = {days: [], meta: {}}
result.days.push(date: key, events: events[key]) for key of events
result.meta.hasMore = @hasMore()
result
_.each(list, prepare, this)
# Internal: returns the 'start_at' of the event formatted for the template
#
# event - the event to format
#
# Returns the formatted String
formattedDayString: (event) =>
I18n.l('#date.formats.short_with_weekday', @toDate(event))
# Internal: change a box of events into an output hash for toJSON
#
# events - a box of events (all the events occur on the same day)
#
# Returns an Object with 'date' and 'events' keys.
eventBoxToHash: (events) =>
date: @formattedDayString(_.first(events))
events: _.map(events, (e) -> e.toJSON())
# Internal: Format a hash of event data to an object ready to be sent to the template.
#
# boxedEvents - A boxed list of events
#
# Returns an object in the format specified by toJSON.
formatResult: (boxedEvents) ->
days: _.map(boxedEvents, @eventBoxToHash)
meta:
hasMore: @hasMore()
# Public: Creates the json for the template.
#
# Returns an Object:
# {
# days: [
# [date: 'some date', events: [event1.toJSON(), event2.toJSON()],
# [date: ...]
# ],
# meta: {
# hasMore: true/false
# }
# }
toJSON: ->
limit = @limitOf(@eventCollection, @assignmentCollection)
list = _.union(@eventCollection.models, @assignmentCollection.models)
_.compose(@formatResult, @eventListToHash)(list, limit)
limit = @limitOf(@eventCollection, @assignmentCollection)
list = _.union(@eventCollection.models, @assignmentCollection.models)
list = _.filter(list, (e) => @toTime(e) < limit) if limit
list = _.sortBy(list, @toTime)
@prepareEvents(list)
list = @sortedBoxBy(list, @formattedDayString)
@formatResult(list)

View File

@ -34,7 +34,24 @@ require [
ok @container.find('.ig-row').length == 18
# should bin results by day
ok @container.find('.agenda-date').length == 9
dates = @container.find('.agenda-date')
ok dates.length == 10
# the bins should be sorted properly
textDates = _.map(dates, (d) -> d.innerText)
console.log(textDates)
ok _.isEqual(textDates, [
"Mon, Oct 7"
"Tue, Oct 8"
"Wed, Oct 9"
"Thu, Oct 10"
"Fri, Oct 11"
"Sat, Oct 12"
"Mon, Oct 14"
"Wed, Oct 16"
"Fri, Oct 18"
"Fri, Nov 1"
])
# should not show "load more" if there are no more pages
ok !@container.find('.agenda-load-btn').length

View File

@ -3,7 +3,7 @@ define [], () ->
[
{
"all_day": true,
"all_day_date": "2013-10-11",
"all_day_date": "2013-10-12",
"created_at": "2013-10-07T22:46:01Z",
"title": "Book report",
"updated_at": "2013-10-07T22:46:01Z",
@ -14,7 +14,7 @@ define [], () ->
"assignment_group_id": "2",
"automatic_peer_reviews": false,
"description": null,
"due_at": "2013-10-12T05:59:59Z",
"due_at": "2013-10-13T05:59:59Z",
"grade_group_students_individually": null,
"grading_standard_id": null,
"grading_type": "points",
@ -36,8 +36,8 @@ define [], () ->
"locked_for_user": false
},
"context_code": "course_2",
"end_at": "2013-10-12T05:59:59Z",
"start_at": "2013-10-12T05:59:59Z",
"end_at": "2013-10-13T05:59:59Z",
"start_at": "2013-10-13T05:59:59Z",
"url": "http://canvas.dev/api/v1/calendar_events/assignment_6",
"html_url": "http://canvas.dev/courses/2/assignments/6"
}