Skip to content

Commit ef1edba

Browse files
committed
Better time management for assignments
1 parent f9a8307 commit ef1edba

8 files changed

Lines changed: 135 additions & 58 deletions

File tree

app/Helpers/Helper.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ public static function getDefaultAssignTos(int $course_id): array
3737
return [['groups' => [['value' => ['course_id' => $course_id], 'text' => 'Everybody']],
3838
'selectedGroup' => '',
3939
'available_from_date' => Carbon::now()->format('Y-m-d'),
40-
'available_from_time' => '09:00:00',
40+
'available_from_time' => '9:00 AM',
4141
'due_date' => Carbon::now()->addDay()->format('Y-m-d'),
42-
'due_time' => '09:00:00']];
42+
'due_time' => '9:00 AM']];
4343
}
4444

4545
public static function getQtiQuestionType(string $qti_json)

app/Http/Controllers/AssignmentController.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ function store(StoreAssignmentProperties $request,
755755

756756
try {
757757
$data = $request->validated();
758+
$assign_tos = $this->reformatAssignToTimes($request->assign_tos);
758759
if ($request->user()->role === 5) {
759760

760761
$assignment_json = '{"public_description":null,"private_description":null,"assessment_type":"real time","number_of_allowed_attempts":"1","number_of_allowed_attempts_penalty":null,"can_view_hint":0,"hint_penalty":null,"algorithmic":0,"learning_tree_success_level":null,"learning_tree_success_criteria":null,"number_of_successful_branches_for_a_reset":null,"number_of_resets":null,"min_time":null,"min_number_of_successful_assessments":null,"free_pass_for_satisfying_learning_tree_criteria":null,"min_time_needed_in_learning_tree":null,"percent_earned_for_exploring_learning_tree":null,"submission_count_percent_decrease":null,"assignment_group_id":1,"source":"a","instructions":"","number_of_randomized_assessments":null,"external_source_points":null,"scoring_type":"p","points_per_question":"number of points","default_completion_scoring_mode":null,"default_points_per_question":"10.00","total_points":null,"default_clicker_time_to_submit":null,"show_points_per_question":1,"file_upload_mode":null,"default_open_ended_submission_type":"0","default_open_ended_text_editor":null,"late_policy":"not accepted","late_deduction_percent":null,"late_deduction_application_period":"once","shown":1,"show_scores":1,"solutions_released":0,"solutions_availability":"automatic","graders_can_see_student_names":1,"students_can_view_assignment_statistics":0,"include_in_weighted_average":1,"notifications":1,"course_id":512,"lms_resource_link_id":null,"textbook_url":null}';
@@ -768,12 +769,11 @@ function store(StoreAssignmentProperties $request,
768769
$date = date("Y-m-d");
769770
$datetime = new DateTime('tomorrow');
770771
$tomorrow = $datetime->format('Y-m-d');
771-
$assign_tos = '[{"groups":[{"value":{"course_id":' . $course->id . '},"text":"Everybody"}],"selectedGroup":null,"available_from_date":"' . $date . '","available_from_time":"09:00:00","due_date":"' . $tomorrow . '","due_time":"09:00:00"}]';
772+
$assign_tos = '[{"groups":[{"value":{"course_id":' . $course->id . '},"text":"Everybody"}],"selectedGroup":null,"available_from_date":"' . $date . '","available_from_time":"9:00 AM","due_date":"' . $tomorrow . '","due_time":"9:00 AM"}]';
772773
$assign_tos = json_decode($assign_tos, true);
773774
$this->addAssignTos($assignment, $assign_tos, $section, $request->user());
774775
} else {
775776

776-
$assign_tos = $request->assign_tos;
777777
$repeated_groups = $this->groupsMustNotRepeat($assign_tos);
778778
if ($repeated_groups) {
779779
$response['message'] = $repeated_groups;
@@ -1321,7 +1321,7 @@ function update(StoreAssignmentProperties $request,
13211321
}
13221322

13231323

1324-
$assign_tos = $request->assign_tos;
1324+
$assign_tos = $this->reformatAssignToTimes($request->assign_tos);
13251325
$repeated_groups = $this->groupsMustNotRepeat($assign_tos);
13261326
if ($repeated_groups) {
13271327
$response['message'] = $repeated_groups;
@@ -1589,5 +1589,22 @@ public function validAssessmentTypeSwitch(Assignment $assignment, $new_assessmen
15891589

15901590
}
15911591

1592+
/**
1593+
* @param $assign_tos
1594+
* @return array
1595+
*/
1596+
function reformatAssignToTimes($assign_tos): array
1597+
{
1598+
foreach ($assign_tos as $key => $assign_to) {
1599+
foreach (['available_from_time', 'due_time', 'final_submission_deadline_time'] as $time_key => $value) {
1600+
if (isset($assign_tos[$key][$value])) {
1601+
$assign_tos[$key][$value] = DateTime::createFromFormat('g:i A', $assign_to[$value])->format('H:i:00');
1602+
}
1603+
}
1604+
}
1605+
return $assign_tos;
1606+
}
1607+
1608+
15921609

15931610
}

app/Http/Requests/StoreAssignmentProperties.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,12 @@ public function rules(): array
121121
$new_assign_tos[$key]['due'] = "{$assign_to['due_date']} {$assign_to['due_time']}";
122122
if ($this->late_policy !== 'not accepted') {
123123
$rules['final_submission_deadline_' . $key] = new IsADateLaterThan($this->{'due_' . $key}, 'due', 'late policy deadline');
124+
$rules['final_submission_deadline_time_' . $key] = 'date_format:g:i A';
124125
}
125126
$rules['due_' . $key] = new IsADateLaterThan($this->{'available_from_' . $key}, 'available on', 'due');
126127
$rules['available_from_date_' . $key] = 'required|date';
127-
$rules['available_from_time_' . $key] = 'required|date_format:H:i:00';
128-
$rules['due_time_' . $key] = 'required|date_format:H:i:00';
128+
$rules['available_from_time_' . $key] = 'required|date_format:g:i A';
129+
$rules['due_time_' . $key] = 'required|date_format:g:i A';
129130
$rules['groups_' . $key] = 'required';
130131
}
131132
}
@@ -197,10 +198,14 @@ public function messages()
197198
$messages = [];
198199
if (!$this->is_template) {
199200
foreach ($this->assign_tos as $key => $assign_to) {
201+
$index = $key + 1;
200202
$messages["groups_{$key}.required"] = 'The assign to field is required.';
201203
$messages["available_from_date_{$key}.required"] = 'This date is required.';
202-
$messages["available_from_time_{$key}.required"] = 'This time is required: H:i:00';
203-
$messages["due_time_{$key}.required"] = 'This time is required: H:i:00';
204+
$messages["available_from_time_{$key}.required"] = $this->getTimeFormatErrorMessage('available on', $index);
205+
$messages["available_from_time_{$key}.date_format"] = $this->getTimeFormatErrorMessage('available on', $index);
206+
$messages["due_time_{$key}.required"] = $this->getTimeFormatErrorMessage('due time', $index);
207+
$messages["due_time_{$key}.date_format"] = $this->getTimeFormatErrorMessage('due time', $index);
208+
$messages["final_submission_deadline_time_{$key}.date_format"] = $this->getTimeFormatErrorMessage('final submission deadline time', $index);
204209
}
205210
$messages['name.unique'] = "Assignment names must be unique with a course.";
206211
} else {
@@ -209,4 +214,14 @@ public function messages()
209214
$messages['textbook_url.url'] = "The URL should be of the form https://my-textbook-url.com/some-page.";
210215
return $messages;
211216
}
217+
218+
/**
219+
* @param string $field
220+
* @param int $index
221+
* @return string
222+
*/
223+
public function getTimeFormatErrorMessage(string $field, int $index): string
224+
{
225+
return "Time for $field $index needs a valid time such as 9:00 AM.";
226+
}
212227
}

app/Traits/Test.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,26 @@ public function createAssignTosFromGroups($assignment_info, $groups){
3333
'groups' => $groups,
3434
'available_from' => '2020-06-10 09:00:00',
3535
'available_from_date' => '2020-06-10',
36-
'available_from_time' => '09:00:00',
36+
'available_from_time' => '9:00 AM',
3737
'due' => '2020-06-12 09:00:00',
3838
'due_date' => '2020-06-12',
39-
'due_time' => '09:00:00',
39+
'due_time' => '9:00 AM',
4040
'final_submission_deadline' => '2021-06-12 09:00:00',
4141
'final_submission_deadline_date' => '2021-06-12',
42-
'final_submission_deadline_time' => '09:00:00'
42+
'final_submission_deadline_time' => '9:00 AM',
4343
]
4444
];
4545
$assignment_info['assign_tos'] = $assign_tos;
4646
foreach ( $assignment_info['assign_tos'][0]['groups'] as $key => $group) {
4747
$group_info = ["groups_$key" => [$groups],
4848
"due_$key" => '2020-06-12 09:00:00',
4949
"due_date_$key" => '2020-06-12',
50-
"due_time_$key" => '09:00:00',
50+
"due_time_$key" => '9:00 AM',
5151
"available_from_$key" => '2020-06-10',
5252
"available_from_date_$key" => '2020-06-12',
53-
"available_from_time_$key" => '09:00:00',
53+
"available_from_time_$key" => '9:00 AM',
5454
"final_submission_deadline_date_$key" => '2021-06-12',
55-
"final_submission_deadline_time_$key" => '09:00:00'];
55+
"final_submission_deadline_time_$key" => '9:00 AM'];
5656
foreach ($group_info as $info_key => $info_value) {
5757
$assignment_info[$info_key] = $info_value;
5858
}

resources/js/components/AssignmentProperties.vue

Lines changed: 58 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,7 @@
12551255
required
12561256
:options="assignToGroups"
12571257
:class="{ 'is-invalid': form.errors.has(`groups_${index}`) }"
1258-
@change="updateAssignTos(assignTo)"
1258+
@change="form.errors.clear(`groups_${index}`);updateAssignTos(assignTo)"
12591259
/>
12601260
<has-error :form="form" :field="`groups_${index}`"/>
12611261
</b-col>
@@ -1294,18 +1294,25 @@
12941294
required
12951295
tabindex="0"
12961296
:min="min"
1297+
class="datepicker"
12971298
:class="{ 'is-invalid': form.errors.has(`available_from_date_${index}`) }"
12981299
/>
12991300
<has-error :form="form" :field="`available_from_date_${index}`"/>
13001301
</b-col>
13011302
<b-col>
1302-
<b-form-timepicker :id="`available_from_time_${index}`"
1303-
v-model="assignTo.available_from_time"
1304-
tabindex="0"
1305-
locale="en"
1306-
:class="{ 'is-invalid': form.errors.has(`available_from_time_${index}`) }"
1303+
<b-input-group class="time-input-group">
1304+
<b-form-input :id="`available_from_time_${index}`"
1305+
v-model="assignTo.available_from_time"
1306+
:class="{ 'is-invalid': form.errors.has(`available_from_time_${index}`) }"
1307+
class="time-input"
1308+
@input="form.errors.clear(`available_from_time_${index}`)"
1309+
@shown="form.errors.clear(`available_from_time_${index}`)"
13071310
/>
1311+
<b-input-group-append>
1312+
<span class="input-group-text"><b-icon-clock/></span>
1313+
</b-input-group-append>
13081314
<has-error :form="form" :field="`available_from_time_${index}`"/>
1315+
</b-input-group>
13091316
</b-col>
13101317
</b-form-row>
13111318
</b-form-group>
@@ -1327,20 +1334,26 @@
13271334
tabindex="0"
13281335
:min="min"
13291336
:class="{ 'is-invalid': form.errors.has(`due_${index}`) }"
1337+
class="datepicker"
13301338
@shown="form.errors.clear(`due_${index}`)"
13311339
/>
13321340
<has-error :form="form" :field="`due_${index}`"/>
13331341
</b-col>
13341342
<b-col>
1335-
<b-form-timepicker :id="`due_time_${index}`"
1336-
v-model="assignTo.due_time"
1337-
tabindex="0"
1338-
locale="en"
1339-
required
1340-
:class="{ 'is-invalid': form.errors.has(`due_time_${index}`) }"
1341-
@shown="form.errors.clear(`due_time_${index}`)"
1343+
<b-input-group class="time-input-group">
1344+
<b-form-input :id="`due_time_${index}`"
1345+
v-model="assignTo.due_time"
1346+
required
1347+
:class="{ 'is-invalid': form.errors.has(`due_time_${index}`) }"
1348+
class="time-input"
1349+
@input="form.errors.clear(`due_time_${index}`)"
1350+
@shown="form.errors.clear(`due_time_${index}`)"
13421351
/>
1343-
<has-error :form="form" :field="`due_time_${index}`"/>
1352+
<b-input-group-append>
1353+
<span class="input-group-text"><b-icon-clock/></span>
1354+
</b-input-group-append>
1355+
<has-error :form="form" :field="`due_time_${index}`"/>
1356+
</b-input-group>
13441357
</b-col>
13451358
</b-form-row>
13461359
</b-form-group>
@@ -1373,22 +1386,28 @@
13731386
tabindex="0"
13741387
:min="min"
13751388
:class="{ 'is-invalid': form.errors.has(`final_submission_deadline_${index}`) }"
1389+
class="datepicker"
13761390
:disabled="Boolean(solutionsReleased) && assessmentType !== 'real time'"
13771391
@shown="form.errors.clear(`final_submission_deadline_${index}`)"
13781392
/>
13791393
<has-error :form="form" :field="`final_submission_deadline_${index}`"/>
13801394
</b-col>
13811395
<b-col>
1382-
<b-form-timepicker :id="`final_submission_deadline_time_${index}`"
1383-
v-model="assignTo.final_submission_deadline_time"
1384-
tabindex="0"
1385-
locale="en"
1386-
required
1387-
:class="{ 'is-invalid': form.errors.has(`final_submission_deadline_time_${index}`) }"
1388-
:disabled="Boolean(solutionsReleased) && assessmentType !== 'real time'"
1389-
@shown="form.errors.clear(`final_submission_deadline_time_${index}`)"
1396+
<b-input-group class="time-input-group">
1397+
<b-form-input :id="`final_submission_deadline_time_${index}`"
1398+
v-model="assignTo.final_submission_deadline_time"
1399+
required
1400+
:class="{ 'is-invalid': form.errors.has(`final_submission_deadline_time_${index}`) }"
1401+
class="time-input"
1402+
:disabled="Boolean(solutionsReleased) && assessmentType !== 'real time'"
1403+
@input="form.errors.clear(`final_submission_deadline_time_${index}`)"
1404+
@shown="form.errors.clear(`final_submission_deadline_time_${index}`)"
13901405
/>
1391-
<has-error :form="form" :field="`final_submission_deadline_time_${index}`"/>
1406+
<b-input-group-append>
1407+
<span class="input-group-text"><b-icon-clock/></span>
1408+
</b-input-group-append>
1409+
<has-error :form="form" :field="`final_submission_deadline_time_${index}`"/>
1410+
</b-input-group>
13921411
</b-col>
13931412
</b-form-row>
13941413
</b-form-group>
@@ -1595,11 +1614,8 @@ export default {
15951614
fixDatePickerAccessibilitysForAssignTos () {
15961615
for (let i = 0; i < this.form.assign_tos.length; i++) {
15971616
fixDatePicker(`available_from_${i}`, `selected_available_from_${i}`)
1598-
fixDatePicker(`available_from_time_${i}`, `selected_available_from_time_${i}`)
15991617
fixDatePicker(`due_date_${i}`, `selected_due_date_${i}`)
1600-
fixDatePicker(`due_time_${i}`, `selected_due_time_${i}`)
16011618
fixDatePicker(`final_submission_deadline_${i}`, `selected_final_submission_deadline_${i}`)
1602-
fixDatePicker(`final_submission_deadline_time_${i}`, `selected_final_submission_deadline_time_${i}`)
16031619
}
16041620
},
16051621
checkDefaultOpenEndedSubmissionType () {
@@ -1910,3 +1926,19 @@ export default {
19101926
}
19111927
}
19121928
</script>
1929+
<style scoped>
1930+
.datepicker {
1931+
border-color: #8a8f90;
1932+
}
1933+
1934+
.time-input-group .input-group-text {
1935+
width: 40px;
1936+
border-left: none;
1937+
background-color: #ffffff;
1938+
border-color: #8a8f90;
1939+
}
1940+
1941+
.time-input-group .time-input {
1942+
border-right: none;
1943+
}
1944+
</style>

resources/js/helpers/AssignmentProperties.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import axios from 'axios'
22
import Form from 'vform'
33

4+
function reformatTime (vm, time) {
5+
return vm.$moment(time, 'HH:mm:ss').format('h:mm A')
6+
}
7+
48
export const assignmentForm = new Form({
59
name: '',
610
assign_tos: [],
@@ -90,11 +94,11 @@ export function defaultAssignTos (moment, courseStartDate, courseEndDate) {
9094
groups: [],
9195
selectedGroup: null,
9296
available_from_date: moment(courseStartDate).format('YYYY-MM-DD'),
93-
available_from_time: '09:00:00',
97+
available_from_time: '9:00 AM',
9498
due_date: moment(moment(), 'YYYY-MM-DD').format('YYYY-MM-DD'),
95-
due_time: '09:00:00',
99+
due_time: '9:00 AM',
96100
final_submission_deadline_date: moment(courseEndDate).format('YYYY-MM-DD'),
97-
final_submission_deadline_time: '09:00:00'
101+
final_submission_deadline_time: '9:00 AM'
98102
}
99103
}
100104

@@ -120,9 +124,9 @@ export function resetAssignmentForm (form, assignmentId) {
120124
form.public_description = ''
121125
form.private_description = ''
122126
form.available_from_date = ''
123-
form.available_from_time = '09:00:00'
127+
form.available_from_time = '9:00 AM'
124128
form.due_date = ''
125-
form.due_time = '09:00:00'
129+
form.due_time = '9:00 AM'
126130
form.type_of_submission = 'correct'
127131
form.num_submissions_needed = '2'
128132
form.default_open_ended_submission_type = 'file'
@@ -213,6 +217,15 @@ export async function editAssignmentProperties (assignmentProperties, vm) {
213217
for (let i = 0; i < assignmentProperties.assign_tos.length; i++) {
214218
vm.form.assign_tos[i].groups = vm.form.assign_tos[i].formatted_groups
215219
vm.form.assign_tos[i].selectedGroup = null
220+
if (vm.form.assign_tos[i].available_from_time) {
221+
vm.form.assign_tos[i].available_from_time = reformatTime(vm, vm.form.assign_tos[i].available_from_time)
222+
}
223+
if (vm.form.assign_tos[i].due_time) {
224+
vm.form.assign_tos[i].due_time = reformatTime(vm, vm.form.assign_tos[i].due_time)
225+
}
226+
if (vm.form.assign_tos[i].final_submission_deadline_time) {
227+
vm.form.assign_tos[i].final_submission_deadline_time = reformatTime(vm, vm.form.assign_tos[i].final_submission_deadline_time)
228+
}
216229
}
217230
}
218231
vm.form.algorithmic = assignmentProperties.algorithmic

0 commit comments

Comments
 (0)