-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WELLMS-423 Import course statistics (#61)
- Loading branch information
Showing
16 changed files
with
329 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
namespace EscolaLms\Reports\Http\Requests\Admin; | ||
|
||
use EscolaLms\Courses\Models\Course; | ||
use EscolaLms\Reports\Models\Report; | ||
use Illuminate\Foundation\Http\FormRequest; | ||
use Illuminate\Validation\Rule; | ||
|
||
class ImportCoursesStatsRequest extends FormRequest | ||
{ | ||
public function authorize() | ||
{ | ||
return $this->user()->can('viewAny', Report::class) || $this->user()->can('update', $this->getCourse()); | ||
} | ||
|
||
protected function prepareForValidation() | ||
{ | ||
$this->merge([ | ||
'course_id' => $this->route('course_id') | ||
]); | ||
} | ||
|
||
public function rules() | ||
{ | ||
return [ | ||
'course_id' => ['required', 'integer', Rule::exists((new Course())->getTable(), 'id')], | ||
'file' => ['required', 'file:xlsx'], | ||
]; | ||
} | ||
|
||
public function getCourseId(): int | ||
{ | ||
return $this->validated()['course_id']; | ||
} | ||
|
||
public function getCourse(): Course | ||
{ | ||
return Course::find($this->getCourseId()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace EscolaLms\Reports\Imports\Stats\Course; | ||
|
||
use EscolaLms\Courses\Models\Course; | ||
use EscolaLms\Reports\Imports\Stats\Course\Sheets\FinishedTopicsAttemptsSheet; | ||
use EscolaLms\Reports\Imports\Stats\Course\Sheets\FinishedTopicsSecondsSheet; | ||
use EscolaLms\Reports\Imports\Stats\Course\Sheets\FinishedTopicsStatusesSheet; | ||
use Maatwebsite\Excel\Concerns\Importable; | ||
use Maatwebsite\Excel\Concerns\WithMultipleSheets; | ||
|
||
class FinishedTopicsImport implements WithMultipleSheets | ||
{ | ||
use Importable; | ||
|
||
protected Course $course; | ||
|
||
public function __construct(Course $course) | ||
{ | ||
$this->course = $course; | ||
} | ||
|
||
public function sheets(): array | ||
{ | ||
return [ | ||
new FinishedTopicsStatusesSheet($this->course), | ||
new FinishedTopicsSecondsSheet($this->course), | ||
new FinishedTopicsAttemptsSheet($this->course), | ||
]; | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/Imports/Stats/Course/Sheets/FinishedTopicsAttemptsSheet.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php | ||
|
||
namespace EscolaLms\Reports\Imports\Stats\Course\Sheets; | ||
|
||
use EscolaLms\Auth\Models\User; | ||
use EscolaLms\Courses\Models\CourseProgress; | ||
use EscolaLms\Courses\Models\CourseUserAttendance; | ||
use EscolaLms\Courses\Models\Topic; | ||
|
||
class FinishedTopicsAttemptsSheet extends FinishedTopicsSheet | ||
{ | ||
protected function processRow(User $user, Topic $topic, $value): CourseProgress | ||
{ | ||
$courseProgress = parent::processRow($user, $topic, $value); | ||
|
||
if ((int) $value === 1) { | ||
CourseUserAttendance::query()->updateOrCreate([ | ||
'course_progress_id' => $courseProgress->getKey(), | ||
'attempt' => 1, | ||
'seconds' => 0, | ||
], [ | ||
'attendance_date' => now(), | ||
]); | ||
} | ||
|
||
CourseUserAttendance::query()->updateOrCreate([ | ||
'course_progress_id' => $courseProgress->getKey(), | ||
'attempt' => $value, | ||
], [ | ||
'seconds' => $courseProgress->seconds, | ||
'attendance_date' => now(), | ||
]); | ||
|
||
return $courseProgress; | ||
} | ||
|
||
protected function prepareUpdateData($value, CourseProgress $courseProgress = null): array | ||
{ | ||
return [ | ||
'attempts' => $value, | ||
]; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/Imports/Stats/Course/Sheets/FinishedTopicsSecondsSheet.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
namespace EscolaLms\Reports\Imports\Stats\Course\Sheets; | ||
|
||
use EscolaLms\Courses\Models\CourseProgress; | ||
|
||
class FinishedTopicsSecondsSheet extends FinishedTopicsSheet | ||
{ | ||
protected function prepareUpdateData($value, CourseProgress $courseProgress = null): array | ||
{ | ||
return [ | ||
'seconds' => $value, | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<?php | ||
|
||
namespace EscolaLms\Reports\Imports\Stats\Course\Sheets; | ||
|
||
use EscolaLms\Auth\Models\User; | ||
use EscolaLms\Courses\Models\Course; | ||
use EscolaLms\Courses\Models\CourseProgress; | ||
use EscolaLms\Courses\Models\Topic; | ||
use EscolaLms\Reports\Stats\Course\Strategies\TopicTitleStrategyContext; | ||
use Maatwebsite\Excel\Concerns\OnEachRow; | ||
use Maatwebsite\Excel\Concerns\WithHeadingRow; | ||
use Maatwebsite\Excel\Row; | ||
|
||
abstract class FinishedTopicsSheet implements OnEachRow, WithHeadingRow | ||
{ | ||
protected Course $course; | ||
protected array $topics = []; | ||
|
||
public function __construct(Course $course) | ||
{ | ||
$this->course = $course; | ||
$this->prepareTopics(); | ||
} | ||
|
||
public function onRow(Row $row) | ||
{ | ||
$user = User::query()->where('email', '=', $row['Email'])->first(); | ||
if ($user) { | ||
foreach ($this->topics as $title => $topic) { | ||
$this->processRow($user, $topic, $row[$title]); | ||
} | ||
} | ||
} | ||
|
||
protected function processRow(User $user, Topic $topic, $value): CourseProgress | ||
{ | ||
$coursesProgress = CourseProgress::query() | ||
->where('user_id', '=', $user->getKey()) | ||
->where('topic_id', '=', $topic->getKey()) | ||
->first(); | ||
if ($coursesProgress) { | ||
$coursesProgress->update($this->prepareUpdateData($value, $coursesProgress)); | ||
} else { | ||
$coursesProgress = CourseProgress::create(array_merge([ | ||
'user_id' => $user->getKey(), | ||
'topic_id' => $topic->getKey(), | ||
], $this->prepareUpdateData($value))); | ||
} | ||
|
||
return $coursesProgress; | ||
} | ||
|
||
protected abstract function prepareUpdateData($value, CourseProgress $courseProgress = null): array; | ||
|
||
protected function prepareTopics() | ||
{ | ||
$this->course->topics->each(function ($topic) { | ||
$this->topics[(new TopicTitleStrategyContext($topic))->getStrategy()->makeTitle()] = $topic; | ||
}); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/Imports/Stats/Course/Sheets/FinishedTopicsStatusesSheet.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
namespace EscolaLms\Reports\Imports\Stats\Course\Sheets; | ||
|
||
use EscolaLms\Courses\Models\CourseProgress; | ||
|
||
class FinishedTopicsStatusesSheet extends FinishedTopicsSheet | ||
{ | ||
protected function prepareUpdateData($value, CourseProgress $courseProgress = null): array | ||
{ | ||
$value = (int) $value; | ||
|
||
return [ | ||
'status' => $value, | ||
'started_at' => $value > 0 ? ($this->course->active_from ?? now()->subDay()) : null, | ||
'finished_at' => $value === 2 ? ($this->course->active_to ?? now()->subMinutes(random_int(1, 120))) : null, | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<?php | ||
|
||
namespace EscolaLms\Reports\Stats\Course; | ||
|
||
use EscolaLms\Auth\Models\GroupUser; | ||
use EscolaLms\Core\Models\User; | ||
use EscolaLms\Courses\Models\Course; | ||
use EscolaLms\Courses\Models\CourseGroupPivot; | ||
use EscolaLms\Courses\Models\CourseProgress; | ||
use EscolaLms\Courses\Models\CourseUserPivot; | ||
use EscolaLms\Courses\Models\Group; | ||
use EscolaLms\Courses\Models\Lesson; | ||
use EscolaLms\Courses\Models\Topic; | ||
use Illuminate\Database\Eloquent\Collection; | ||
use Illuminate\Database\Query\JoinClause; | ||
|
||
abstract class CourseUsersAndGroupsStat extends AbstractCourseStat | ||
{ | ||
protected string $topicTable; | ||
protected string $lessonTable; | ||
protected string $courseTable; | ||
protected string $courseUserTable; | ||
protected string $userTable; | ||
protected string $courseProgressTable; | ||
protected string $groupTable; | ||
protected string $courseGroupTable; | ||
protected string $userGroupTable; | ||
|
||
public function __construct(Course $course) | ||
{ | ||
parent::__construct($course); | ||
|
||
$this->topicTable = (new Topic())->getTable(); | ||
$this->lessonTable = (new Lesson())->getTable(); | ||
$this->courseTable = (new Course())->getTable(); | ||
$this->courseUserTable = (new CourseUserPivot())->getTable(); | ||
$this->userTable = (new User())->getTable(); | ||
$this->courseProgressTable = (new CourseProgress())->getTable(); | ||
$this->groupTable = (new Group())->getTable(); | ||
$this->courseGroupTable = (new CourseGroupPivot())->getTable(); | ||
$this->userGroupTable = (new GroupUser())->getTable(); | ||
} | ||
|
||
protected function getGroupUsers(): \Illuminate\Support\Collection | ||
{ | ||
return $this->formatGroupResult(Topic::query() | ||
->select( | ||
$this->topicTable . '.id as topic_id', | ||
$this->topicTable . '.title as topic_title', | ||
$this->topicTable . '.topicable_id', | ||
$this->topicTable . '.topicable_type', | ||
$this->userTable . '.id as user_id', | ||
$this->userTable . '.email as user_email', | ||
$this->userTable . '.first_name as user_first_name', | ||
$this->userTable . '.last_name as user_last_name', | ||
$this->courseProgressTable . '.finished_at', | ||
$this->courseProgressTable . '.seconds', | ||
$this->courseProgressTable . '.started_at', | ||
$this->courseProgressTable . '.attempt', | ||
) | ||
->with('topicable') | ||
->join($this->lessonTable, $this->topicTable . '.lesson_id', '=', $this->lessonTable . '.id') | ||
->join($this->courseTable, $this->lessonTable . '.course_id', '=', $this->courseTable . '.id') | ||
->where($this->courseTable . '.id', '=', $this->course->getKey()) | ||
->join($this->courseGroupTable, $this->courseTable . '.id', '=', $this->courseGroupTable . '.course_id') | ||
->join($this->groupTable, $this->courseGroupTable . '.group_id', '=', $this->groupTable . '.id') | ||
->join($this->userGroupTable, $this->groupTable . '.id', '=', $this->userGroupTable . '.group_id') | ||
->join($this->userTable, $this->userGroupTable . '.user_id', '=', $this->userTable . '.id') | ||
->leftJoin($this->courseProgressTable, fn(JoinClause $join) => $join | ||
->on($this->courseProgressTable . '.user_id', '=', $this->userTable . '.id') | ||
->on($this->courseProgressTable . '.topic_id', '=', $this->topicTable . '.id') | ||
) | ||
->get()); | ||
} | ||
|
||
private function formatGroupResult(Collection $result): \Illuminate\Support\Collection | ||
{ | ||
return $result | ||
->groupBy('user_email') | ||
->map(function ($topics, $userEmail) { | ||
$finished = collect($topics)->first(fn ($topic) => $topic->finished_at === null) === null; | ||
return [ | ||
'id' => $topics[0]->user_id, | ||
'name' => $topics[0]->user_first_name . ' ' . $topics[0]->user_last_name, | ||
'email' => $userEmail, | ||
'finished_at' => $finished ? collect($topics)->max('finished_at') : null, | ||
'finished' => $finished, | ||
]; | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.