feat: Initial commit for Saqel Platform architecture, backend, Docker, and documentation
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class AiReport extends Model
|
||||
{
|
||||
protected $fillable = ['user_id', 'type', 'payload', 'summary'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'payload' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Answer extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'quiz_attempt_id',
|
||||
'question_id',
|
||||
'selected_option_id',
|
||||
'is_correct',
|
||||
'time_spent_seconds',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_correct' => 'boolean',
|
||||
];
|
||||
|
||||
public function attempt(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(QuizAttempt::class, 'quiz_attempt_id');
|
||||
}
|
||||
|
||||
public function question(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Question::class);
|
||||
}
|
||||
|
||||
public function selectedOption(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(QuestionOption::class, 'selected_option_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\ConceptFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Concept extends Model
|
||||
{
|
||||
/** @use HasFactory<ConceptFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['subject_id', 'name', 'remedial_lesson_id'];
|
||||
|
||||
public function subject(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Subject::class);
|
||||
}
|
||||
|
||||
public function remedialLesson(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Lesson::class, 'remedial_lesson_id');
|
||||
}
|
||||
|
||||
public function questions(): HasMany
|
||||
{
|
||||
return $this->hasMany(Question::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ConceptMastery extends Model
|
||||
{
|
||||
protected $table = 'concept_mastery';
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'concept_id',
|
||||
'correct_count',
|
||||
'wrong_count',
|
||||
'mastery_score',
|
||||
];
|
||||
|
||||
public function concept(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Concept::class);
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\CourseFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Course extends Model
|
||||
{
|
||||
/** @use HasFactory<CourseFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['subject_id', 'grade', 'track', 'title', 'description'];
|
||||
|
||||
public function subject(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Subject::class);
|
||||
}
|
||||
|
||||
public function lessons(): HasMany
|
||||
{
|
||||
return $this->hasMany(Lesson::class)->orderBy('order_index');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\LessonFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Lesson extends Model
|
||||
{
|
||||
/** @use HasFactory<LessonFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['course_id', 'title', 'order_index', 'video_id', 'duration_seconds', 'transcript'];
|
||||
|
||||
public function course(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Course::class);
|
||||
}
|
||||
|
||||
public function concepts(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Concept::class);
|
||||
}
|
||||
|
||||
public function quizzes(): HasMany
|
||||
{
|
||||
return $this->hasMany(Quiz::class);
|
||||
}
|
||||
|
||||
public function progressFor(int $userId): ?LessonProgress
|
||||
{
|
||||
/** @var LessonProgress|null $progress */
|
||||
$progress = $this->hasOne(LessonProgress::class)
|
||||
->where('user_id', $userId)
|
||||
->first();
|
||||
|
||||
return $progress;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class LessonProgress extends Model
|
||||
{
|
||||
protected $table = 'lesson_progress';
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'lesson_id',
|
||||
'last_position_seconds',
|
||||
'watched_percentage',
|
||||
'completed_at',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'completed_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
|
||||
public function lesson(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Lesson::class);
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\QuestionFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Question extends Model
|
||||
{
|
||||
/** @use HasFactory<QuestionFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['quiz_id', 'concept_id', 'body', 'explanation'];
|
||||
|
||||
public function quiz(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Quiz::class);
|
||||
}
|
||||
|
||||
public function concept(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Concept::class);
|
||||
}
|
||||
|
||||
public function options(): HasMany
|
||||
{
|
||||
return $this->hasMany(QuestionOption::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class QuestionOption extends Model
|
||||
{
|
||||
protected $fillable = ['question_id', 'body', 'is_correct'];
|
||||
|
||||
protected $casts = [
|
||||
'is_correct' => 'boolean',
|
||||
];
|
||||
|
||||
public function question(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Question::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\QuizFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Quiz extends Model
|
||||
{
|
||||
/** @use HasFactory<QuizFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['lesson_id', 'title', 'trigger_seconds'];
|
||||
|
||||
public function lesson(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Lesson::class);
|
||||
}
|
||||
|
||||
public function questions(): HasMany
|
||||
{
|
||||
return $this->hasMany(Question::class);
|
||||
}
|
||||
|
||||
public function attempts(): HasMany
|
||||
{
|
||||
return $this->hasMany(QuizAttempt::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class QuizAttempt extends Model
|
||||
{
|
||||
protected $fillable = ['user_id', 'quiz_id', 'score'];
|
||||
|
||||
public function quiz(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Quiz::class);
|
||||
}
|
||||
|
||||
public function answers(): HasMany
|
||||
{
|
||||
return $this->hasMany(Answer::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Database\Factories\SubjectFactory;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Subject extends Model
|
||||
{
|
||||
/** @use HasFactory<SubjectFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['name', 'slug'];
|
||||
|
||||
public function courses(): HasMany
|
||||
{
|
||||
return $this->hasMany(Course::class);
|
||||
}
|
||||
|
||||
public function concepts(): HasMany
|
||||
{
|
||||
return $this->hasMany(Concept::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
#[Fillable(['name', 'email', 'password', 'role', 'api_token', 'grade', 'track'])]
|
||||
#[Hidden(['password', 'remember_token', 'api_token'])]
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
||||
public function isGuardian(): bool
|
||||
{
|
||||
return $this->role === 'guardian';
|
||||
}
|
||||
|
||||
public function students(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'guardian_student', 'guardian_id', 'student_id');
|
||||
}
|
||||
|
||||
public function guardians(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'guardian_student', 'student_id', 'guardian_id');
|
||||
}
|
||||
|
||||
public function lessonProgress(): HasMany
|
||||
{
|
||||
return $this->hasMany(LessonProgress::class);
|
||||
}
|
||||
|
||||
public function conceptMastery(): HasMany
|
||||
{
|
||||
return $this->hasMany(ConceptMastery::class);
|
||||
}
|
||||
|
||||
public function aiReports(): HasMany
|
||||
{
|
||||
return $this->hasMany(AiReport::class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user