1- require ' active_support/time'
1+ require " active_support/time"
22
33# TODO unit tests
44class Api ::ProblemTimelineController < ApplicationController
@@ -26,7 +26,7 @@ def show
2626 return
2727 end
2828
29- # TODO error if student doesn't have any backups for this assignment and course
29+ # TODO error if student doesn't have any backups for this assignment and course
3030
3131 problem_name_to_index = AssignmentProblem . where ( assignment_id : assignment . id )
3232 . pluck ( :display_name , :problem_index )
@@ -40,57 +40,58 @@ def show
4040 )
4141 . order ( :created )
4242
43- problem_to_backups = get_problem_to_backups ( backups , problem_name_to_index . keys )
44- problem_to_sessions = get_problem_to_sessions ( problem_to_backups )
45- timeline_data = get_timeline_data ( problem_to_sessions , problem_name_to_index )
43+ processed_backups = process_backups ( backups , problem_name_to_index )
44+ sessions = get_sessions ( processed_backups )
4645
47- render json : timeline_data , status : :ok
46+ render json : sessions , status : :ok
4847 end
4948
5049 private
5150
5251 SESSION_TIME_GAP_THRESHOLD = 15 . minutes
5352
53+ # TODO move this logic into frontend since it's about styling
5454 # color blind color palette from https://davidmathlogic.com/colorblind/#%23D81B60-%231E88E5-%23FFC107-%23004D40
5555 PINK = "#D81B60"
5656 BLUE = "#1E88E5"
5757 YELLOW = "#FFC107"
5858 DARK_GREEN = "#004D40"
5959
6060 def get_grading_backup_status ( grading_message_question )
61+ # TODO separate label into backup type (unlock vs. correctness) and status (passed boolean), then format label in frontend
6162 if grading_message_question . failed == 0
62- { : label => "Correctness Tests Passed" , : color => DARK_GREEN }
63+ { label : "Correctness Tests Passed" , color : DARK_GREEN }
6364 else
64- { : label => "Correctness Tests Failed" , : color => PINK }
65+ { label : "Correctness Tests Failed" , color : PINK }
6566 end
6667 end
6768
6869 # This works assuming that the unlock_message_cases are all belonging to the same problem
6970 def get_unlocking_backup_status ( unlock_message_cases )
7071 if unlock_message_cases . map { |umc | umc . correct } . all?
71- { : label => "Unlocking Tests Passed" , : color => BLUE }
72+ { label : "Unlocking Tests Passed" , color : BLUE }
7273 else
73- { : label => "Unlocking Tests Failed" , : color => YELLOW }
74+ { label : "Unlocking Tests Failed" , color : YELLOW }
7475 end
7576 end
7677
77- def get_problem_to_backups ( backups , problem_names )
78- # problem name to array of hashes where each hash represents relevant data from a single backup
79- # hash has: timestamp, label, color
80- result = problem_names . map { | name | [ name , [ ] ] } . to_h
78+ def process_backups ( backups , problem_name_to_index )
79+ # returns an array of hashes where each hash represents relevant data from a single backup
80+ # hash has: timestamp, label, color, index, problem_name
81+ result = [ ]
8182
8283 backups . each_with_index do |backup , index |
8384 if backup . grading_location . present?
8485 backup . grading_message_questions . each do |gmq |
8586 status = get_grading_backup_status ( gmq )
86- result [ gmq . question_display_name ] << { : timestamp => Time . iso8601 ( backup . created ) , : index => index } . merge ( status )
87+ result << { timestamp : Time . iso8601 ( backup . created ) , index : index , problem_name : gmq . question_display_name , problem_index : problem_name_to_index [ gmq . question_display_name ] } . merge ( status )
8788 end
8889 end
8990
9091 if backup . unlock_location . present?
9192 # annoyingly, unlock.json doesn't have question display name so we fetch it from analytics.json
9293 backup_problem_names = backup . analytics_message . question_display_names
93- umc_grouped_by_problem_name = backup_problem_names . map { |name | [ name , [ ] ] } . to_h
94+ umc_grouped_by_problem_name = backup_problem_names . map { |name | [ name , [ ] ] } . to_h
9495 backup . unlock_message_cases . each do |umc |
9596 # TODO dangerously (?) assume that unlock message cases
9697 # will always match exactly one of the backup problem names
@@ -102,43 +103,46 @@ def get_problem_to_backups(backups, problem_names)
102103 end
103104 end
104105
105- umc_grouped_by_problem_name . each do |name , unlock_message_cases |
106+ umc_grouped_by_problem_name . each do |problem_name , unlock_message_cases |
106107 status = get_unlocking_backup_status ( unlock_message_cases )
107- result [ name ] << { : timestamp => Time . iso8601 ( backup . created ) , : index => index } . merge ( status )
108+ result << { timestamp : Time . iso8601 ( backup . created ) , index : index , problem_name : problem_name , problem_index : problem_name_to_index [ problem_name ] } . merge ( status )
108109 end
109110 end
110111 end
111112
112113 result
113114 end
114115
115- # This works assuming the array contains backups for the same problem
116- def get_sessions ( backups )
117- if backups . empty?
116+ def get_sessions ( processed_backups )
117+ if processed_backups . empty?
118118 return [ ]
119119 end
120120
121121 # A session is defined as a series of backups where:
122- # 1. Consecutive timestamps are <= SESSION_TIME_GAP_THRESHOLD, and
123- # 2. All labels (and therefore colors) are the same within the session
122+ # 1. All problems worked on are the same within the session, AND
123+ # 2. All labels (and therefore colors) are the same within the session, AND
124+ # 3. Consecutive timestamps are <= SESSION_TIME_GAP_THRESHOLD
124125
125126 result = [ ]
126- # TODO redo sessions computation -- do not group by problem first bc then leads to weird overlap
127+ # TODO only convert to camel case at the end for consistency. generally fix naming conventions in all files...
127128 curr_session =
128129 {
129- startTime : backups [ 0 ] [ :timestamp ] ,
130- endTime : backups [ 0 ] [ :timestamp ] ,
131- label : backups [ 0 ] [ :label ] ,
132- color : backups [ 0 ] [ :color ] ,
133- startIndex : backups [ 0 ] [ :index ] ,
134- endIndex : backups [ 0 ] [ :index ] + 1 ,
130+ startTime : processed_backups [ 0 ] [ :timestamp ] ,
131+ endTime : processed_backups [ 0 ] [ :timestamp ] ,
132+ label : processed_backups [ 0 ] [ :label ] ,
133+ color : processed_backups [ 0 ] [ :color ] ,
134+ startIndex : processed_backups [ 0 ] [ :index ] ,
135+ endIndex : processed_backups [ 0 ] [ :index ] + 1 ,
136+ problemName : processed_backups [ 0 ] [ :problem_name ] ,
137+ problemIndex : processed_backups [ 0 ] [ :problem_index ] ,
135138 }
136139
137- backups . each_cons ( 2 ) do |a , b |
140+ processed_backups . each_cons ( 2 ) do |a , b |
141+ problems_differ = a [ :problem_index ] != b [ :problem_index ]
138142 has_time_gap = b [ :timestamp ] - a [ :timestamp ] > SESSION_TIME_GAP_THRESHOLD
139143 labels_differ = a [ :label ] != b [ :label ]
140144
141- if has_time_gap or labels_differ
145+ if problems_differ or has_time_gap or labels_differ
142146 result << curr_session
143147 curr_session =
144148 {
@@ -147,7 +151,9 @@ def get_sessions(backups)
147151 label : b [ :label ] ,
148152 color : b [ :color ] ,
149153 startIndex : b [ :index ] ,
150- endIndex : b [ :index ] + 1
154+ endIndex : b [ :index ] + 1 ,
155+ problemName : b [ :problem_name ] ,
156+ problemIndex : b [ :problem_index ]
151157 }
152158 else
153159 curr_session [ :endTime ] = b [ :timestamp ]
@@ -158,32 +164,4 @@ def get_sessions(backups)
158164 result << curr_session
159165 result
160166 end
161-
162- def get_problem_to_sessions ( problem_to_backups )
163- # then have helper function to turn each array into sessions (use threshold)
164- # compute startTime and endTime, color
165- problem_to_backups . map { |name , backups | [ name , get_sessions ( backups ) ] } . to_h
166- end
167-
168- def get_timeline_data ( problem_to_sessions , problem_name_to_index )
169- # then flatten all arrays and assign problemIndex using problem_name_to_index hash
170- result = [ ]
171-
172- problem_to_sessions . map do |problem_name , sessions |
173- problem_index = problem_name_to_index [ problem_name ]
174- sessions . each do |session |
175- result << {
176- problemIndex : problem_index ,
177- startTime : session [ :startTime ] ,
178- endTime : session [ :endTime ] ,
179- label : session [ :label ] ,
180- startIndex : session [ :startIndex ] ,
181- endIndex : session [ :endIndex ] ,
182- color : session [ :color ]
183- }
184- end
185- end
186-
187- result
188- end
189167end
0 commit comments