Skip to content

Commit

Permalink
Non-instructor editors can create courses and assignments
Browse files Browse the repository at this point in the history
  • Loading branch information
kreut committed Jun 29, 2022
1 parent e60f973 commit a2452ef
Show file tree
Hide file tree
Showing 19 changed files with 1,674 additions and 1,575 deletions.
280 changes: 153 additions & 127 deletions app/Http/Controllers/AssignmentController.php

Large diffs are not rendered by default.

54 changes: 35 additions & 19 deletions app/Http/Controllers/CourseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use App\Assignment;
use App\AssignmentSyncQuestion;
use App\AssignToGroup;
use App\AssignToTiming;
use App\AssignToUser;
use App\BetaAssignment;
Expand All @@ -14,7 +13,6 @@
use App\FinalGrade;
use App\Http\Requests\DestroyCourse;
use App\Http\Requests\ResetCourse;
use App\Http\Requests\UpdateCourse;
use App\Jobs\DeleteAssignmentDirectoryFromS3;
use App\School;
use App\Section;
Expand All @@ -24,6 +22,7 @@
use App\Http\Requests\StoreCourse;
use App\User;
use Carbon\Carbon;
use DateTime;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Auth;
Expand Down Expand Up @@ -177,12 +176,12 @@ public function open(Course $course): array
public function canLogIntoCourseAsAnonymousUser(Course $course): array
{

$response['type'] = 'error';
try {

try {
$response['can_log_into_course_as_anonymous_user'] = (boolean)$course->anonymous_users;
$response['type'] = 'success';
} catch (Exception $e) {
$response['type'] = 'error';
$h = new Handler(app());
$h->report($e);
$response['message'] = 'We could not determine whether you can log into the this course as an anonymous user.';
Expand Down Expand Up @@ -795,11 +794,13 @@ public function reOrderAllCourses()
{
$courses = $this->getCourses(Auth::user());
$all_course_ids = [];
foreach ($courses as $value) {
$all_course_ids[] = $value->id;
if ($courses) {
foreach ($courses as $value) {
$all_course_ids[] = $value->id;
}
$course = new Course();
$course->orderCourses($all_course_ids);
}
$course = new Course();
$course->orderCourses($all_course_ids);
}

/**
Expand Down Expand Up @@ -1116,6 +1117,7 @@ function getCourses($user)
{

switch ($user->role) {
case(5):
case(2):
return DB::table('courses')
->select('courses.*', DB::raw("beta_courses.id IS NOT NULL AS is_beta_course"))
Expand Down Expand Up @@ -1180,7 +1182,7 @@ function store(StoreCourse $request,
Enrollment $enrollment,
FinalGrade $finalGrade,
Section $section,
School $school)
School $school): array
{
//todo: check the validation rules
$response['type'] = 'error';
Expand All @@ -1191,14 +1193,24 @@ function store(StoreCourse $request,
$response['message'] = $authorized->message();
return $response;
}


$is_instructor = auth()->user()->role === 2;
try {
$data = $request->validated();
DB::beginTransaction();
if (!$is_instructor) {
$data['start_date'] = date("Y-m-d");
$datetime = new DateTime('+3 months');
$data['end_date'] = $datetime->format("Y-m-d");
$data['crn'] = 'N/A';
$data['section'] = 'N/A';
$data['term'] = 'N/A';
$data['alpha'] = 0;
$data['anonymous_users'] = 0;
}
$data['user_id'] = auth()->user()->id;
$data['school_id'] = $this->getSchoolIdFromRequest($request, $school);
$data['school_id'] = $is_instructor ? $this->getSchoolIdFromRequest($request, $school) : 1;
$data['start_date'] = $this->convertLocalMysqlFormattedDateToUTC($data['start_date'] . '00:00:00', auth()->user()->time_zone);

$data['end_date'] = $this->convertLocalMysqlFormattedDateToUTC($data['end_date'] . '00:00:00', auth()->user()->time_zone);
$data['shown'] = 0;
$data['public_description'] = $request->public_description;
Expand Down Expand Up @@ -1277,16 +1289,20 @@ function update(StoreCourse $request,
try {
$data = $request->validated();
DB::beginTransaction();
$data['school_id'] = $this->getSchoolIdFromRequest($request, $school);
$data['start_date'] = $this->convertLocalMysqlFormattedDateToUTC($data['start_date'], auth()->user()->time_zone);
$data['end_date'] = $this->convertLocalMysqlFormattedDateToUTC($data['end_date'], auth()->user()->time_zone);

$data['public_description'] = $request->public_description;
$data['private_description'] = $request->private_description;
$data['textbook_url'] = $request->textbook_url;
if ($is_beta_course && $request->untether_beta_course) {
$betaCourse->untether($course);
if ($request->user()->role === 2) {
$data['school_id'] = $this->getSchoolIdFromRequest($request, $school);
$data['start_date'] = $this->convertLocalMysqlFormattedDateToUTC($data['start_date'], auth()->user()->time_zone);
$data['end_date'] = $this->convertLocalMysqlFormattedDateToUTC($data['end_date'], auth()->user()->time_zone);

$data['textbook_url'] = $request->textbook_url;
if ($is_beta_course && $request->untether_beta_course) {
$betaCourse->untether($course);
}
unset($data['school']);
}
unset($data['school']);
$course->update($data);
DB::commit();
$response['type'] = 'success';
Expand Down
219 changes: 114 additions & 105 deletions app/Http/Requests/StoreAssignmentProperties.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,142 +44,151 @@ public function authorize()
*/
public function rules(): array
{
$rules = [
'source' => Rule::in(['a', 'x']),
'scoring_type' => Rule::in(['c', 'p']),
'late_policy' => Rule::in(['not accepted', 'marked late', 'deduction']),
'assignment_group_id' => 'required|exists:assignment_groups,id',
'include_in_weighted_average' => Rule::in([0, 1]),
'instructions' => 'max:10000',
'default_open_ended_submission_type' => Rule::in(['file', 'rich text', 'plain text', 'audio', 0]),
'notifications' => Rule::in([0, 1]),
];

if ($this->is_template) {

$unique = Rule::unique('assignment_templates')
->where('user_id', $this->user()->id);
if ($this->route()->getActionMethod() === 'update') {
$unique->ignore($this->route()->parameters()['assignmentTemplate']->id);
}

$rules['template_name'] = ['required', 'max:255', $unique];
$rules['template_description'] = ['required', 'min:5', 'max:255'];
$rules['assign_to_everyone'] = ['required', Rule::in([0, 1])];

} else {
if ($this->user()->role === 5) {
$unique = Rule::unique('assignments')
->where('course_id', $this->course_id);
if ($this->route()->getActionMethod() === 'update') {
$unique->ignore($this->route()->parameters()['assignment']->id);
}
$rules['name'] = ['required', 'max:255', $unique];
} else {
$rules = [
'source' => Rule::in(['a', 'x']),
'scoring_type' => Rule::in(['c', 'p']),
'late_policy' => Rule::in(['not accepted', 'marked late', 'deduction']),
'assignment_group_id' => 'required|exists:assignment_groups,id',
'include_in_weighted_average' => Rule::in([0, 1]),
'instructions' => 'max:10000',
'default_open_ended_submission_type' => Rule::in(['file', 'rich text', 'plain text', 'audio', 0]),
'notifications' => Rule::in([0, 1]),
];

if ($this->is_template) {

$unique = Rule::unique('assignment_templates')
->where('user_id', $this->user()->id);
if ($this->route()->getActionMethod() === 'update') {
$unique->ignore($this->route()->parameters()['assignmentTemplate']->id);
}

}
$rules['template_name'] = ['required', 'max:255', $unique];
$rules['template_description'] = ['required', 'min:5', 'max:255'];
$rules['assign_to_everyone'] = ['required', Rule::in([0, 1])];

} else {
$unique = Rule::unique('assignments')
->where('course_id', $this->course_id);
if ($this->route()->getActionMethod() === 'update') {
$unique->ignore($this->route()->parameters()['assignment']->id);
}
$rules['name'] = ['required', 'max:255', $unique];

if ($this->file_upload_mode === 'compiled_pdf') {
$rules['default_open_ended_submission_type'] = Rule::in(['file', 0]);
}
}

$rules['textbook_url'] = 'nullable|url';

if ($this->file_upload_mode === 'compiled_pdf') {
$rules['default_open_ended_submission_type'] = Rule::in(['file', 0]);
}

if ($this->assessment_type === 'delayed') {
$rules['file_upload_mode'] = Rule::in(['compiled_pdf', 'individual_assessment', 'both']);
}
$rules['textbook_url'] = 'nullable|url';

if ($this->scoring_type === 'p' && $this->assessment_type !== 'delayed') {
$rules['can_view_hint'] = ['required', Rule::in([0, 1])];
if ((int)$this->can_view_hint === 1) {
$rules['hint_penalty'] = [new IsValidHintPenalty()];
}

}
if (in_array($this->assessment_type, ['real time', 'learning tree']) && $this->scoring_type === 'p') {
$rules['number_of_allowed_attempts'] = ['required', Rule::in(['1', '2', '3', '4', 'unlimited'])];
if ($this->number_of_allowed_attempts !== '1') {
$rules['number_of_allowed_attempts_penalty'] = ['required', new IsValidNumberOfAllowedAttemptsPenalty($this->number_of_allowed_attempts)];
if ($this->assessment_type === 'delayed') {
$rules['file_upload_mode'] = Rule::in(['compiled_pdf', 'individual_assessment', 'both']);
}
if ($this->assessment_type === 'real time') {
$rules['solutions_availability'] = ['required', Rule::in(['automatic', 'manual'])];

if ($this->scoring_type === 'p' && $this->assessment_type !== 'delayed') {
$rules['can_view_hint'] = ['required', Rule::in([0, 1])];
if ((int)$this->can_view_hint === 1) {
$rules['hint_penalty'] = [new IsValidHintPenalty()];
}

}
}
$new_assign_tos = [];
if (!$this->is_template) {
foreach ($this->assign_tos as $key => $assign_to) {
$new_assign_tos[$key]['available_from'] = "{$assign_to['available_from_date']} {$assign_to['available_from_time']}";
$new_assign_tos[$key]['due'] = "{$assign_to['due_date']} {$assign_to['due_time']}";
if ($this->late_policy !== 'not accepted') {
$rules['final_submission_deadline_' . $key] = new IsADateLaterThan($this->{'due_' . $key}, 'due', 'late policy deadline');
if (in_array($this->assessment_type, ['real time', 'learning tree']) && $this->scoring_type === 'p') {
$rules['number_of_allowed_attempts'] = ['required', Rule::in(['1', '2', '3', '4', 'unlimited'])];
if ($this->number_of_allowed_attempts !== '1') {
$rules['number_of_allowed_attempts_penalty'] = ['required', new IsValidNumberOfAllowedAttemptsPenalty($this->number_of_allowed_attempts)];
}
if ($this->assessment_type === 'real time') {
$rules['solutions_availability'] = ['required', Rule::in(['automatic', 'manual'])];
}
$rules['due_' . $key] = new IsADateLaterThan($this->{'available_from_' . $key}, 'available on', 'due');
$rules['available_from_date_' . $key] = 'required|date';
$rules['available_from_time_' . $key] = 'required|date_format:H:i:00';
$rules['due_time_' . $key] = 'required|date_format:H:i:00';
$rules['groups_' . $key] = 'required';
}
}
switch ($this->source) {
case('a'):
$rules['algorithmic'] = ['required', Rule::in(0,1)];
$rules['points_per_question'] = ['required', Rule::in('number of points', 'question weight')];
if ($this->points_per_question === 'number of points') {
$rules['default_points_per_question'] = 'numeric|min:0|max:1000';
$new_assign_tos = [];
if (!$this->is_template) {
foreach ($this->assign_tos as $key => $assign_to) {
$new_assign_tos[$key]['available_from'] = "{$assign_to['available_from_date']} {$assign_to['available_from_time']}";
$new_assign_tos[$key]['due'] = "{$assign_to['due_date']} {$assign_to['due_time']}";
if ($this->late_policy !== 'not accepted') {
$rules['final_submission_deadline_' . $key] = new IsADateLaterThan($this->{'due_' . $key}, 'due', 'late policy deadline');
}
$rules['due_' . $key] = new IsADateLaterThan($this->{'available_from_' . $key}, 'available on', 'due');
$rules['available_from_date_' . $key] = 'required|date';
$rules['available_from_time_' . $key] = 'required|date_format:H:i:00';
$rules['due_time_' . $key] = 'required|date_format:H:i:00';
$rules['groups_' . $key] = 'required';
}
if ($this->points_per_question === 'question weight') {
$rules['total_points'] = ['numeric', 'min:0', 'not_in:0', 'max:1000'];
if (!$this->is_template && $this->route()->getActionMethod() === 'update') {
$assignment_id = $this->route()->parameters()['assignment']->id;
if (abs(Assignment::find($assignment_id)->total_points - $this->total_points) >= PHP_FLOAT_EPSILON) {
$rules['total_points'][] = new IsNotOpenOrNoSubmissions($new_assign_tos);
}
switch ($this->source) {
case('a'):
$rules['algorithmic'] = ['required', Rule::in(0, 1)];
$rules['points_per_question'] = ['required', Rule::in('number of points', 'question weight')];
if ($this->points_per_question === 'number of points') {
$rules['default_points_per_question'] = 'numeric|min:0|max:1000';
}
if ($this->points_per_question === 'question weight') {
$rules['total_points'] = ['numeric', 'min:0', 'not_in:0', 'max:1000'];
if (!$this->is_template && $this->route()->getActionMethod() === 'update') {
$assignment_id = $this->route()->parameters()['assignment']->id;
if (abs(Assignment::find($assignment_id)->total_points - $this->total_points) >= PHP_FLOAT_EPSILON) {
$rules['total_points'][] = new IsNotOpenOrNoSubmissions($new_assign_tos);
}
}
}
}
if ((int)($this->randomizations) === 1) {
$rules['number_of_randomized_assessments'] = ['required',
'integer',
'gt:0',
new IsNotClickerAssessment($this->assessment_type),
];
if (!$this->is_template && $this->route()->getActionMethod() === 'update') {
$assignment_id = $this->route()->parameters()['assignment']->id;
if (Assignment::find($assignment_id)->number_of_randomized_assessments !== $this->number_of_randomized_assessments) {
$rules['number_of_randomized_assessments'][] = new HasNoRandomizedAssignmentQuestions($assignment_id);
if ((int)($this->randomizations) === 1) {
$rules['number_of_randomized_assessments'] = ['required',
'integer',
'gt:0',
new IsNotClickerAssessment($this->assessment_type),
];
if (!$this->is_template && $this->route()->getActionMethod() === 'update') {
$assignment_id = $this->route()->parameters()['assignment']->id;
if (Assignment::find($assignment_id)->number_of_randomized_assessments !== $this->number_of_randomized_assessments) {
$rules['number_of_randomized_assessments'][] = new HasNoRandomizedAssignmentQuestions($assignment_id);
}
}

}
break;
case('x'):
$rules['external_source_points'] = 'required|integer|min:0|max:200';
break;

}
if ($this->assessment_type === 'learning tree') {
$learning_tree_rules = $this->learningTreeSuccessRubricRules($this);
foreach ($learning_tree_rules as $key => $value) {
$rules[$key] = $value;
}
break;
case('x'):
$rules['external_source_points'] = 'required|integer|min:0|max:200';
break;

}
if ($this->assessment_type === 'learning tree') {
$learning_tree_rules = $this->learningTreeSuccessRubricRules($this);
foreach ($learning_tree_rules as $key => $value) {
$rules[$key] = $value;
}
}
if ($this->assessment_type === 'clicker') {
$rules['default_clicker_time_to_submit'] = new IsValidPeriodOfTime();
}
if ($this->late_policy === 'deduction') {
//has to be at least one or division by 0 issue in setScoreBasedOnLatePolicy
//deducting 100% makes no sense!
$rules['late_deduction_percent'] = 'required|integer|min:1|max:99';
if (!$this->late_deduction_applied_once) {
$rules['late_deduction_application_period'] = new IsValidPeriodOfTime();
if ($this->assessment_type === 'clicker') {
$rules['default_clicker_time_to_submit'] = new IsValidPeriodOfTime();
}
if ($this->late_policy === 'deduction') {
//has to be at least one or division by 0 issue in setScoreBasedOnLatePolicy
//deducting 100% makes no sense!
$rules['late_deduction_percent'] = 'required|integer|min:1|max:99';
if (!$this->late_deduction_applied_once) {
$rules['late_deduction_application_period'] = new IsValidPeriodOfTime();
}
}
}

if ($this->scoring_type === 'c') {
$rules['scoring_type'] = ['required', new isValidAssesmentTypeForScoringType($this->assessment_type)];
$rules['default_completion_scoring_mode'] = ['required', new isValidDefaultCompletionScoringType($this->completion_split_auto_graded_percentage)];
if ($this->scoring_type === 'c') {
$rules['scoring_type'] = ['required', new isValidAssesmentTypeForScoringType($this->assessment_type)];
$rules['default_completion_scoring_mode'] = ['required', new isValidDefaultCompletionScoringType($this->completion_split_auto_graded_percentage)];

}
}

return $rules;
}

Expand Down
Loading

0 comments on commit a2452ef

Please sign in to comment.