265 خطوط
18 KiB
PHP
265 خطوط
18 KiB
PHP
<?php
|
|
|
|
namespace Database\Seeders;
|
|
|
|
use App\Models\User;
|
|
use App\Modules\Analytics\Application\AnalyticsProjectionService;
|
|
use App\Modules\Assessments\Domain\Assessment;
|
|
use App\Modules\Assessments\Domain\AssessmentAttempt;
|
|
use App\Modules\Assessments\Domain\Question;
|
|
use App\Modules\Assessments\Domain\QuestionResult;
|
|
use App\Modules\Assignments\Application\AssignmentResolver;
|
|
use App\Modules\Assignments\Domain\Assignment;
|
|
use App\Modules\Courses\Domain\Block;
|
|
use App\Modules\Courses\Domain\Course;
|
|
use App\Modules\Courses\Domain\CourseModule;
|
|
use App\Modules\Courses\Domain\CourseVersion;
|
|
use App\Modules\Courses\Domain\Enums\CourseVersionStatus;
|
|
use App\Modules\Courses\Domain\Lesson;
|
|
use App\Modules\Identity\Domain\Enums\AccountStatus;
|
|
use App\Modules\Identity\Domain\Enums\UserRole;
|
|
use App\Modules\Learner\Domain\LearningEvent;
|
|
use App\Modules\Monitoring\Application\MonitoringEngine;
|
|
use App\Modules\Organizations\Domain\Organization;
|
|
use App\Modules\Subscriptions\Domain\Subscription;
|
|
use App\Modules\Teams\Domain\Team;
|
|
use Illuminate\Database\Seeder;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
class DatabaseSeeder extends Seeder
|
|
{
|
|
/**
|
|
* Seed the application's database.
|
|
*/
|
|
public function run(): void
|
|
{
|
|
$this->user(null, 'مدیر سامانه', 'admin@microlearn.test', UserRole::SuperAdmin);
|
|
|
|
$organization = Organization::query()->updateOrCreate(['slug' => 'safe-academy'], [
|
|
'name' => 'آکادمی ایمن',
|
|
'status' => 'active',
|
|
'default_locale' => 'fa',
|
|
'timezone' => 'Asia/Tehran',
|
|
]);
|
|
|
|
$designer = $this->user($organization->getKey(), 'سارا محمدی', 'designer@microlearn.test', UserRole::CourseDesigner);
|
|
$manager = $this->user($organization->getKey(), 'علی رضایی', 'manager@microlearn.test', UserRole::Manager);
|
|
$learners = collect([
|
|
$this->user($organization->getKey(), 'مریم احمدی', 'maryam@microlearn.test', UserRole::Learner),
|
|
$this->user($organization->getKey(), 'امیر حسینی', 'amir@microlearn.test', UserRole::Learner),
|
|
$this->user($organization->getKey(), 'نازنین کریمی', 'nazanin@microlearn.test', UserRole::Learner),
|
|
]);
|
|
$manager->update(['first_name' => 'علی', 'last_name' => 'رضایی', 'department' => 'عملیات', 'job_level' => 'manager']);
|
|
$profiles = [
|
|
'maryam@microlearn.test' => ['مریم', 'احمدی', 'کارشناس'],
|
|
'amir@microlearn.test' => ['امیر', 'حسینی', 'کارشناس'],
|
|
'nazanin@microlearn.test' => ['نازنین', 'کریمی', 'کارشناس'],
|
|
];
|
|
$learners->each(function (User $learner) use ($manager, $profiles): void {
|
|
[$firstName, $lastName] = $profiles[$learner->email];
|
|
$learner->update([
|
|
'first_name' => $firstName,
|
|
'last_name' => $lastName,
|
|
'department' => 'عملیات',
|
|
'job_level' => 'specialist',
|
|
'direct_manager_id' => $manager->getKey(),
|
|
]);
|
|
});
|
|
|
|
$team = Team::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'name' => 'عملیات'],
|
|
['description' => 'تیم عملیات و ایمنی محیط کار', 'status' => 'active', 'created_by' => $designer->getKey()],
|
|
);
|
|
$team->managers()->syncWithoutDetaching([$manager->getKey()]);
|
|
$team->members()->syncWithoutDetaching($learners->pluck('id')->all());
|
|
|
|
Subscription::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'plan_key' => 'enterprise'],
|
|
[
|
|
'status' => 'active',
|
|
'starts_at' => now()->startOfMonth(),
|
|
'expires_at' => now()->addYear(),
|
|
'seat_limit' => 100,
|
|
'storage_quota_bytes' => 107374182400,
|
|
'storage_used_bytes' => 28991029248,
|
|
'ai_credit_quota' => 10000,
|
|
'ai_credits_used' => 2750,
|
|
'enabled_features' => ['taxonomy', 'advanced_analytics', 'exports', 'certificates'],
|
|
],
|
|
);
|
|
|
|
$course = Course::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'slug' => 'workplace-safety'],
|
|
['title' => 'ایمنی در محیط کار', 'status' => 'draft', 'created_by' => $designer->getKey()],
|
|
);
|
|
$published = CourseVersion::query()->firstOrCreate(
|
|
['course_id' => $course->getKey(), 'version_number' => 1],
|
|
[
|
|
'organization_id' => $organization->getKey(), 'status' => CourseVersionStatus::Published,
|
|
'title' => 'ایمنی در محیط کار', 'description' => 'نسخه پایه منتشرشده برای آموزش ایمنی کارکنان.',
|
|
'settings' => ['language' => 'fa', 'difficulty' => 'beginner'], 'published_at' => now()->subMonth(),
|
|
],
|
|
);
|
|
$publishedModule = CourseModule::query()->updateOrCreate(
|
|
['course_version_id' => $published->getKey(), 'position' => 1],
|
|
['organization_id' => $organization->getKey(), 'title' => 'مبانی ایمنی'],
|
|
);
|
|
$publishedLesson = Lesson::query()->updateOrCreate(
|
|
['course_module_id' => $publishedModule->getKey(), 'position' => 1],
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $published->getKey(), 'title' => 'استفاده صحیح از تجهیزات حفاظتی', 'presentation_mode' => 'flow'],
|
|
);
|
|
Block::query()->updateOrCreate(
|
|
['lesson_id' => $publishedLesson->getKey(), 'position' => 1],
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $published->getKey(), 'type' => 'heading', 'schema_version' => 1, 'data' => ['text' => 'ایمنی مسئولیت همه ماست', 'level' => 2], 'revision' => 1],
|
|
);
|
|
Block::query()->updateOrCreate(
|
|
['lesson_id' => $publishedLesson->getKey(), 'position' => 2],
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $published->getKey(), 'type' => 'key_point', 'schema_version' => 1, 'data' => ['title' => 'نکته کلیدی', 'body' => 'پیش از شروع کار، تجهیزات حفاظت فردی را بررسی و بهدرستی استفاده کنید.'], 'revision' => 1],
|
|
);
|
|
$draft = CourseVersion::query()->updateOrCreate(
|
|
['course_id' => $course->getKey(), 'version_number' => 2],
|
|
[
|
|
'organization_id' => $organization->getKey(), 'source_version_id' => $published->getKey(),
|
|
'status' => CourseVersionStatus::Draft, 'title' => 'ایمنی در محیط کار',
|
|
'description' => 'بازنگری دوره با تمرکز بر تجهیزات حفاظت فردی و گزارش خطر.',
|
|
'settings' => ['language' => 'fa', 'difficulty' => 'beginner'],
|
|
],
|
|
);
|
|
$basics = CourseModule::query()->updateOrCreate(
|
|
['course_version_id' => $draft->getKey(), 'position' => 1],
|
|
['organization_id' => $organization->getKey(), 'title' => 'مبانی ایمنی'],
|
|
);
|
|
$response = CourseModule::query()->updateOrCreate(
|
|
['course_version_id' => $draft->getKey(), 'position' => 2],
|
|
['organization_id' => $organization->getKey(), 'title' => 'واکنش و گزارشدهی'],
|
|
);
|
|
foreach ([[$basics, 1, 'شناخت خطرهای محیط کار'], [$basics, 2, 'تجهیزات حفاظت فردی'], [$response, 1, 'گزارش رویداد و خطر']] as [$module, $position, $title]) {
|
|
Lesson::query()->updateOrCreate(
|
|
['course_module_id' => $module->getKey(), 'position' => $position],
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $draft->getKey(), 'title' => $title, 'presentation_mode' => 'flow'],
|
|
);
|
|
}
|
|
|
|
$bankQuestion = Question::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'is_bank_item' => true, 'prompt' => 'اولین اقدام پس از مشاهده خطر در محیط کار چیست؟'],
|
|
[
|
|
'type' => 'single_choice', 'configuration' => ['options' => [
|
|
['id' => 'report', 'text' => 'ایمنسازی محل و گزارش خطر', 'correct' => true, 'feedback' => 'درست است.'],
|
|
['id' => 'ignore', 'text' => 'نادیده گرفتن تا پایان شیفت', 'correct' => false, 'feedback' => 'خطر باید فوری گزارش شود.'],
|
|
]],
|
|
'difficulty' => 'beginner', 'position' => 0, 'schema_version' => 1,
|
|
'topic' => 'گزارش خطر', 'tags' => ['ایمنی', 'گزارشدهی'], 'explanation' => 'ابتدا محیط را ایمن و سپس خطر را طبق فرایند سازمان گزارش کنید.',
|
|
],
|
|
);
|
|
Question::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'is_bank_item' => true, 'prompt' => 'در زمان نشت ماده ناشناخته چه تصمیمی میگیرید؟'],
|
|
[
|
|
'type' => 'scenario', 'configuration' => ['context' => 'در انبار بوی نامعمول و ظرف آسیبدیده مشاهده میکنید.', 'choices' => [
|
|
['text' => 'فاصله گرفتن، محدودکردن دسترسی و اطلاع به مسئول ایمنی', 'score' => 1, 'feedback' => 'انتخاب ایمن و مطابق رویه است.'],
|
|
['text' => 'نزدیک شدن و بررسی ماده بدون تجهیزات', 'score' => 0, 'feedback' => 'این اقدام مواجهه را افزایش میدهد.'],
|
|
]],
|
|
'difficulty' => 'intermediate', 'position' => 0, 'schema_version' => 1,
|
|
'topic' => 'واکنش اضطراری', 'tags' => ['سناریو', 'تصمیمگیری'], 'explanation' => 'در مواجهه با ماده ناشناخته، کنترل محدوده و گزارش فوری اولویت دارد.',
|
|
],
|
|
);
|
|
|
|
$learnerAssignment = Assignment::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'assignable_type' => 'course', 'assignable_id' => $published->getKey(), 'target_type' => 'team', 'target_id' => $team->getKey()],
|
|
['status' => 'active', 'mandatory' => true, 'due_at' => now()->addDays(14), 'reminder_days' => 3, 'escalation_policy' => ['enabled' => true], 'source' => 'seed', 'assigned_by' => $designer->getKey()],
|
|
);
|
|
app(AssignmentResolver::class)->sync($learnerAssignment);
|
|
|
|
$progressProfiles = [
|
|
'maryam@microlearn.test' => ['progress' => 75, 'status' => 'in_progress', 'completed_at' => null],
|
|
'amir@microlearn.test' => ['progress' => 100, 'status' => 'completed', 'completed_at' => now()->subDays(2)],
|
|
'nazanin@microlearn.test' => ['progress' => 25, 'status' => 'in_progress', 'completed_at' => null],
|
|
];
|
|
foreach ($learners as $learner) {
|
|
DB::table('assignment_users')->where('assignment_id', $learnerAssignment->getKey())->where('user_id', $learner->getKey())->update([...$progressProfiles[$learner->email], 'updated_at' => now()]);
|
|
}
|
|
$publishedBlocks = Block::query()->where('course_version_id', $published->getKey())->orderBy('position')->get();
|
|
$eventRows = [
|
|
[$learners[0], '11111111-1111-4111-8111-111111111101', 'course.opened', null, null, 10, 120],
|
|
[$learners[0], '11111111-1111-4111-8111-111111111102', 'lesson.started', $publishedLesson, null, 9, 60],
|
|
[$learners[0], '11111111-1111-4111-8111-111111111103', 'block.completed', $publishedLesson, $publishedBlocks[0], 8, 240],
|
|
[$learners[1], '22222222-2222-4222-8222-222222222201', 'course.opened', null, null, 7, 90],
|
|
[$learners[1], '22222222-2222-4222-8222-222222222202', 'lesson.started', $publishedLesson, null, 6, 60],
|
|
[$learners[1], '22222222-2222-4222-8222-222222222203', 'lesson.completed', $publishedLesson, null, 2, 420],
|
|
[$learners[1], '22222222-2222-4222-8222-222222222204', 'course.completed', null, null, 2, 30],
|
|
[$learners[2], '33333333-3333-4333-8333-333333333301', 'course.opened', null, null, 14, 45],
|
|
[$learners[2], '33333333-3333-4333-8333-333333333302', 'lesson.started', $publishedLesson, null, 14, 30],
|
|
];
|
|
foreach ($eventRows as [$learner, $clientId, $type, $lesson, $block, $daysAgo, $duration]) {
|
|
LearningEvent::query()->firstOrCreate(['learner_id' => $learner->getKey(), 'client_event_id' => $clientId], [
|
|
'organization_id' => $organization->getKey(), 'event_type' => $type, 'schema_version' => 1,
|
|
'session_id' => 'seed-'.$learner->getKey(), 'correlation_id' => $clientId,
|
|
'device_context' => ['platform' => 'web', 'formFactor' => 'desktop', 'online' => true],
|
|
'assignment_id' => $learnerAssignment->getKey(), 'course_version_id' => $published->getKey(),
|
|
'lesson_id' => $lesson?->getKey(), 'block_id' => $block?->getKey(), 'payload' => ['durationSeconds' => $duration],
|
|
'occurred_at' => now()->subDays($daysAgo), 'received_at' => now()->subDays($daysAgo)->addMinute(),
|
|
]);
|
|
}
|
|
$publishedAssessment = Assessment::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $published->getKey(), 'title' => 'آزمون کوتاه ایمنی'],
|
|
['lesson_id' => $publishedLesson->getKey(), 'settings' => ['passingScore' => 70, 'attemptLimit' => 2]],
|
|
);
|
|
$publishedQuestion = Question::query()->updateOrCreate(
|
|
['assessment_id' => $publishedAssessment->getKey(), 'prompt' => 'پیش از شروع کار چه اقدامی ضروری است؟'],
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $published->getKey(), 'is_bank_item' => false, 'type' => 'single_choice', 'configuration' => ['options' => [['text' => 'بررسی تجهیزات حفاظت فردی', 'correct' => true], ['text' => 'شروع فوری کار', 'correct' => false]]], 'difficulty' => 'beginner', 'position' => 1, 'schema_version' => 1],
|
|
);
|
|
foreach ([[$learners[0], .85, true], [$learners[1], .72, true], [$learners[2], .45, false]] as [$learner, $score, $correct]) {
|
|
$attempt = AssessmentAttempt::query()->updateOrCreate(
|
|
['learner_id' => $learner->getKey(), 'assessment_id' => $publishedAssessment->getKey(), 'attempt_number' => 1],
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $published->getKey(), 'status' => 'completed', 'score' => $score, 'started_at' => now()->subDays(5), 'completed_at' => now()->subDays(5)->addMinutes(5)],
|
|
);
|
|
QuestionResult::query()->updateOrCreate(
|
|
['assessment_attempt_id' => $attempt->getKey(), 'question_id' => $publishedQuestion->getKey()],
|
|
['organization_id' => $organization->getKey(), 'raw_value' => [$correct ? 0 : 1], 'normalized_value' => $score, 'is_correct' => $correct, 'occurred_at' => $attempt->completed_at, 'metadata' => ['source' => 'seed-review']],
|
|
);
|
|
}
|
|
|
|
$assessment = Assessment::query()->updateOrCreate(
|
|
['organization_id' => $organization->getKey(), 'course_version_id' => $draft->getKey(), 'title' => 'ارزیابی مبانی ایمنی'],
|
|
[
|
|
'lesson_id' => Lesson::query()->where('course_version_id', $draft->getKey())->orderBy('position')->value('id'),
|
|
'settings' => ['randomSelection' => false, 'questionPoolSize' => null, 'shuffleQuestions' => true, 'shuffleOptions' => true, 'passingScore' => 70, 'attemptLimit' => 2, 'feedbackMode' => 'after_submission', 'timeLimitSeconds' => 300],
|
|
],
|
|
);
|
|
Question::query()->updateOrCreate(
|
|
['assessment_id' => $assessment->getKey(), 'source_question_id' => $bankQuestion->getKey()],
|
|
[
|
|
'organization_id' => $organization->getKey(), 'course_version_id' => $draft->getKey(), 'is_bank_item' => false,
|
|
'type' => $bankQuestion->type, 'prompt' => $bankQuestion->prompt, 'configuration' => $bankQuestion->configuration,
|
|
'difficulty' => $bankQuestion->difficulty, 'position' => 1, 'schema_version' => 1,
|
|
'topic' => $bankQuestion->topic, 'tags' => $bankQuestion->tags, 'explanation' => $bankQuestion->explanation,
|
|
],
|
|
);
|
|
app(AnalyticsProjectionService::class)->rebuild($organization->getKey());
|
|
app(MonitoringEngine::class)->refresh($organization->getKey());
|
|
}
|
|
|
|
private function user(?string $organizationId, string $name, string $email, UserRole $role): User
|
|
{
|
|
$attributes = [
|
|
'organization_id' => $organizationId,
|
|
'name' => $name,
|
|
'password' => 'password',
|
|
'email_verified_at' => now(),
|
|
'role' => $role,
|
|
'status' => AccountStatus::Active,
|
|
'locale' => 'fa',
|
|
'timezone' => 'Asia/Tehran',
|
|
];
|
|
$user = User::query()->where('email', $email)->first() ?? new User(['email' => $email]);
|
|
|
|
if (! $user->exists && Schema::getColumnType('users', 'id') === 'integer') {
|
|
$user->forceFill(['id' => ((int) User::query()->max('id')) + 1]);
|
|
}
|
|
|
|
$user->fill($attributes)->save();
|
|
|
|
return $user;
|
|
}
|
|
}
|